there is no moc file generated for this class
[kdegraphics.git] / kolourpaint / layers / selections / text / kpTextSelection_Paint.cpp
blobbfbcb8253f2b2b746079bd4831d6f32b181dbdc8
2 // REFACTOR: Move into kpPainter
4 /*
5 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
6 All rights reserved.
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
12 1. Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright
15 notice, this list of conditions and the following disclaimer in the
16 documentation and/or other materials provided with the distribution.
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #define DEBUG_KP_SELECTION 0
34 #include <kpTextSelection.h>
35 #include <kpTextSelectionPrivate.h>
37 #include <QBitmap>
38 #include <QFont>
39 #include <QList>
40 #include <QPainter>
42 #include <KDebug>
44 #include <kpPixmapFX.h>
45 #include <kpTextStyle.h>
48 static void DebugAlpha (const QPixmap &pm)
50 #if DEBUG_KP_SELECTION
51 kDebug () << "\tpixmap: hasMask=" << !pm.mask ().isNull ()
52 << " hasAlpha=" << pm.hasAlpha ()
53 << " hasAlphaChannel=" << pm.hasAlphaChannel ()
54 << endl;
55 #endif
56 KP_PFX_CHECK_NO_ALPHA_CHANNEL (pm);
60 struct DrawTextLinesPackage
62 QRect wholeAreaRect, textAreaRect;
64 QList <QString> textLines;
65 kpTextStyle textStyle;
68 // TODO: QPainter::drawText() draws the same text with the same font differently
69 // on the RGB and mask layers. The effect of this can be best seen by
70 // contrasting the rendering of a text box with opaque text but a
71 // see-through background, on top of transparent document areas compared
72 // to opaque document areas.
73 static void DrawTextHelper (QPainter *p, bool drawingOnRGBLayer, void *data)
75 DrawTextLinesPackage *pack = static_cast <DrawTextLinesPackage *> (data);
77 const QFontMetrics fontMetrics (pack->textStyle.font ());
79 #if DEBUG_KP_SELECTION
80 kDebug () << "kpTextSelection_Paint.cpp:DrawTextHelper(drawingOnRGBLayer="
81 << drawingOnRGBLayer << ")" << endl;
83 kDebug () << "\theight=" << fontMetrics.height ()
84 << " leading=" << fontMetrics.leading ()
85 << " ascent=" << fontMetrics.ascent ()
86 << " descent=" << fontMetrics.descent ()
87 << " lineSpacing=" << fontMetrics.lineSpacing ()
88 << endl;
89 #endif
92 // Has an opaque background?
93 const kpColor backColor = pack->textStyle.effectiveBackgroundColor ();
94 if (backColor.isOpaque ())
96 // Fill in the background.
97 p->fillRect (pack->wholeAreaRect,
98 kpPixmapFX::draw_ToQColor (backColor, drawingOnRGBLayer));
102 // Don't draw on the RGB layer if the text is transparent for 2 reasons:
104 // 1. It saves CPU - transparency is determined by the mask, not the RGB
105 // layer anyway.
106 // 2. More importantly, since QPainter::drawText() draws the same text
107 // with the same font differently on the RGB and mask layers, we don't
108 // draw on the RGB layer to avoid artifacts.
109 // TODO: Don't the shape drawing methods have to worry about this too?
110 // Later: Yes they do, see kpPixmapFX::draw_ToQColor().
111 // Why not centralize this in draw()?
112 if (!(drawingOnRGBLayer && pack->textStyle.foregroundColor ().isTransparent ()))
114 p->setClipRect (pack->wholeAreaRect);
116 p->setPen (
117 kpPixmapFX::draw_ToQColor (pack->textStyle.foregroundColor (),
118 drawingOnRGBLayer));
119 p->setFont (pack->textStyle.font ());
122 // Draw a line at a time instead of using QPainter::drawText(QRect,...).
123 // Else, the line heights become >QFontMetrics::height() if you type Chinese
124 // characters (!) and then the cursor gets out of sync.
125 int baseLine = pack->textAreaRect.y () + fontMetrics.ascent ();
126 foreach (const QString &str, pack->textLines)
128 // Note: It seems text does not antialias without XRENDER.
129 p->drawText (pack->textAreaRect.x (), baseLine, str);
131 baseLine += fontMetrics.lineSpacing ();
136 // private
138 // This method uses the text style colors as follows:
140 // A transparent foreground means that text is drawn in the transparent color
141 // ("setting" document pixels) as opposed to being see-through and not being
142 // drawn, which is why we need to wrap this with drawText().
144 // However, a transparent background is see-through and permits the text to
145 // antialias with the document pixels below.
146 void kpTextSelection::drawTextInternal (QPixmap *destPixmap, const QRect &docRect) const
148 // Pixels are set to transparent only if the foreground is transparent.
149 // In contrast, a transparent background does not set any pixels so we
150 // don't include it in here.
151 const bool anyColorTransparent =
152 (d->textStyle.foregroundColor ().isTransparent ());
154 // [Check 1]
155 if (anyColorTransparent)
157 // draw() normally does this for us but we have "Check 2" just below
158 // (but before draw()) that needs to know whether we will have a mask
159 // in draw().
161 // Currently, this makes no functional difference since "Subcheck 2.1"
162 // only passes if "Check 1" fails (us). But this is good futureproofing
163 // in case another subcheck is placed under "Check 2".
164 destPixmap->setMask (kpPixmapFX::getNonNullMask (*destPixmap));
168 // [Check 2]
169 if (kpPixmapFX::hasMask (*destPixmap))
171 // [Subcheck 2.1] Are we drawing opaque text over potentially
172 // transparent document pixels?
173 if (d->textStyle.foregroundColor ().isOpaque () &&
174 d->textStyle.effectiveBackgroundColor ().isTransparent ())
176 #if DEBUG_KP_SELECTION
177 kDebug () << "\tensuring image's transparent pixels are defined";
178 #endif
180 // Set the RGB of transparent pixels to the foreground colour to avoid
181 // anti-aliasing the foreground colored text with undefined RGBs.
182 #if 1
183 // TODO: This might not work under Qt4.
184 // See kpPixmapFX::pixmapWithDefinedTransparentPixels() API Doc.
185 *destPixmap = kpPixmapFX::pixmapWithDefinedTransparentPixels (*destPixmap,
186 d->textStyle.foregroundColor ().toQColor ());
187 // This touches fewer pixels and could be more efficient than the
188 // above but does not work since setPixmapAt() only copies the RGB
189 // data of non-transparent pixels.
190 #else
191 QRect modifyingRectRelPixmap = modifyingRect;
192 modifyingRectRelPixmap.translate (-docRect.x (), -docRect.y ());
194 // This does not work since setPixmapAt() only copies the RGB data
195 // of non-transparent pixels.
196 kpPixmapFX::setPixmapAt (destPixmap,
197 modifyingRectRelPixmap,
198 kpPixmapFX::pixmapWithDefinedTransparentPixels (
199 kpPixmapFX::getPixmapAt (*destPixmap, modifyingRectRelPixmap),
200 d->textStyle.foregroundColor ().toQColor ()));
201 #endif
206 DrawTextLinesPackage pack;
207 pack.wholeAreaRect = boundingRect ().translated (-docRect.topLeft ());
208 pack.textAreaRect = textAreaRect ().translated (-docRect.topLeft ());
209 pack.textLines = textLines ();
210 pack.textStyle = textStyle ();
212 kpPixmapFX::draw (destPixmap, &::DrawTextHelper,
213 textStyle ().foregroundColor ().isOpaque () ||
214 textStyle ().effectiveBackgroundColor ().isOpaque (),
215 anyColorTransparent,
216 &pack);
219 ::DebugAlpha (*destPixmap);
221 #if DEBUG_KP_SELECTION
222 kDebug ();
223 #endif
226 // private
228 // This wraps drawTextInternal(), ironing out NOP cases and also ensuring
229 // that transparent pixels are "painted" on top of the document, not "set".
230 void kpTextSelection::drawText (QPixmap *destPixmap, const QRect &docRect) const
232 #if DEBUG_KP_SELECTION
233 kDebug () << "kpTextSelection::drawText("
234 << "docRect=" << docRect << ")"
235 << " boundingRect=" << boundingRect () << endl;
236 #endif
238 ::DebugAlpha (*destPixmap);
241 // Drawing text is slow so if the text box will be rendered completely
242 // outside of <destRect>, don't bother rendering it at all.
243 const QRect modifyingRect = docRect.intersect (boundingRect ());
244 if (modifyingRect.isEmpty ())
245 return;
248 // Is the text box completely invisible?
249 if (textStyle ().foregroundColor ().isTransparent () &&
250 textStyle ().effectiveBackgroundColor ().isTransparent ())
252 return;
256 // Transparent text on an opaque background?
257 if (textStyle ().foregroundColor ().isTransparent () &&
258 textStyle ().effectiveBackgroundColor ().isOpaque ())
260 kpImage floatImage (modifyingRect.width (), modifyingRect.height ());
262 // This draws transparent text by "setting" transparent pixels...
263 drawTextInternal (&floatImage, modifyingRect);
265 // ... convert that into "painting" transparent pixels on top of
266 // the document.
267 kpPixmapFX::paintPixmapAt (destPixmap,
268 modifyingRect.topLeft () - docRect.topLeft (),
269 floatImage);
271 // Opaque text on a transparent or opaque background?
272 else
274 drawTextInternal (destPixmap, docRect);
279 // public virtual [kpAbstractSelection]
280 void kpTextSelection::paint (QPixmap *destPixmap, const QRect &docRect) const
282 #if DEBUG_KP_SELECTION
283 kDebug () << "kpTextSelection::paint() textStyle: fcol="
284 << (int *) d->textStyle.foregroundColor ().toQRgb ()
285 << " bcol="
286 << (int *) d->textStyle.effectiveBackgroundColor ().toQRgb ()
287 << endl;
288 #endif
290 // (may have to antialias with background)
291 drawText (destPixmap, docRect);
295 // public virtual [kpAbstractSelection]
296 void kpTextSelection::paintBorder (QPixmap *destPixmap, const QRect &docRect,
297 bool selectionFinished) const
299 paintRectangularBorder (destPixmap, docRect, selectionFinished);
303 // public
304 kpImage kpTextSelection::approximateImage () const
306 kpImage retImage (width (), height ());
308 // Are we a text box with a see-through background?
309 if (d->textStyle.effectiveBackgroundColor ().isTransparent ())
311 // Give it a defined background of an arbitrarily neutral color.
312 kpPixmapFX::fill (&retImage, kpColor::Transparent);
315 paint (&retImage, boundingRect ());
317 return retImage;