2 // REFACTOR: Move into kpPainter
5 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
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>
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 ()
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 ()
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
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
);
117 kpPixmapFX::draw_ToQColor (pack
->textStyle
.foregroundColor (),
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 ();
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 ());
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
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
));
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";
180 // Set the RGB of transparent pixels to the foreground colour to avoid
181 // anti-aliasing the foreground colored text with undefined RGBs.
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.
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 ()));
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 (),
219 ::DebugAlpha (*destPixmap
);
221 #if DEBUG_KP_SELECTION
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
;
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 ())
248 // Is the text box completely invisible?
249 if (textStyle ().foregroundColor ().isTransparent () &&
250 textStyle ().effectiveBackgroundColor ().isTransparent ())
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
267 kpPixmapFX::paintPixmapAt (destPixmap
,
268 modifyingRect
.topLeft () - docRect
.topLeft (),
271 // Opaque text on a transparent or opaque background?
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 ()
286 << (int *) d
->textStyle
.effectiveBackgroundColor ().toQRgb ()
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
);
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 ());