From 4ed5e327b96c754b513d4fe84dfe73a363034e39 Mon Sep 17 00:00:00 2001 From: Rob Arnold Date: Tue, 30 Sep 2008 20:57:51 -0400 Subject: [PATCH] Bug 438517 - Add text-shadow support to nsTextBoxFrame r+sr=roc --- layout/xul/base/src/nsTextBoxFrame.cpp | 154 +++++++++++++++++++------ layout/xul/base/src/nsTextBoxFrame.h | 12 ++ 2 files changed, 129 insertions(+), 37 deletions(-) diff --git a/layout/xul/base/src/nsTextBoxFrame.cpp b/layout/xul/base/src/nsTextBoxFrame.cpp index a694f37cfcff..a1e920b78bd6 100644 --- a/layout/xul/base/src/nsTextBoxFrame.cpp +++ b/layout/xul/base/src/nsTextBoxFrame.cpp @@ -369,35 +369,30 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, if (mTitle.IsEmpty()) return; - nsRect textRect(aPt, GetSize()); - textRect.Deflate(GetUsedBorderAndPadding()); + nsRect textRect(CalcTextRect(aRenderingContext, aPt)); - // determine (cropped) title and underline position - nsPresContext* presContext = PresContext(); - LayoutTitle(presContext, aRenderingContext, textRect); - - // make the rect as small as our (cropped) text. - nscoord outerWidth = textRect.width; - textRect.width = mTitleWidth; - - // Align our text within the overall rect by checking our text-align property. - const nsStyleVisibility* vis = GetStyleVisibility(); + // Paint the text shadow before doing any foreground stuff const nsStyleText* textStyle = GetStyleText(); - - if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER) - textRect.x += (outerWidth - textRect.width)/2; - else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT) { - if (vis->mDirection == NS_STYLE_DIRECTION_LTR) - textRect.x += (outerWidth - textRect.width); - } - else { - if (vis->mDirection == NS_STYLE_DIRECTION_RTL) - textRect.x += (outerWidth - textRect.width); + if (textStyle->mTextShadow) { + // Text shadow happens with the last value being painted at the back, + // ie. it is painted first. + for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) { + PaintOneShadow(aRenderingContext.ThebesContext(), + textRect, + textStyle->mTextShadow->ShadowAt(i - 1), + GetStyleColor()->mColor); + } } - // don't draw if the title is not dirty - if (PR_FALSE == aDirtyRect.Intersects(textRect)) - return; + DrawText(aRenderingContext, textRect, 0); +} + +void +nsTextBoxFrame::DrawText(nsIRenderingContext& aRenderingContext, + const nsRect& aTextRect, + const nscolor& aOverrideColor) +{ + nsPresContext* presContext = PresContext(); // paint the title nscolor overColor; @@ -414,7 +409,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, const nsStyleTextReset* styleText = context->GetStyleTextReset(); if (decorMask & styleText->mTextDecoration) { // a decoration defined here - nscolor color = context->GetStyleColor()->mColor; + nscolor color = aOverrideColor ? aOverrideColor : context->GetStyleColor()->mColor; if (NS_STYLE_TEXT_DECORATION_UNDERLINE & decorMask & styleText->mTextDecoration) { underColor = color; @@ -449,11 +444,11 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, fontMet->GetMaxAscent(ascent); nscoord baseline = - presContext->RoundAppUnitsToNearestDevPixels(textRect.y + ascent); + presContext->RoundAppUnitsToNearestDevPixels(aTextRect.y + ascent); nsRefPtr ctx = aRenderingContext.ThebesContext(); - gfxPoint pt(presContext->AppUnitsToGfxUnits(textRect.x), - presContext->AppUnitsToGfxUnits(textRect.y)); - gfxFloat width = presContext->AppUnitsToGfxUnits(textRect.width); + gfxPoint pt(presContext->AppUnitsToGfxUnits(aTextRect.x), + presContext->AppUnitsToGfxUnits(aTextRect.y)); + gfxFloat width = presContext->AppUnitsToGfxUnits(aTextRect.width); gfxFloat ascentPixel = presContext->AppUnitsToGfxUnits(ascent); if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) { fontMet->GetUnderline(offset, size); @@ -489,7 +484,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, CalculateUnderline(aRenderingContext); - aRenderingContext.SetColor(GetStyleColor()->mColor); + aRenderingContext.SetColor(aOverrideColor ? aOverrideColor : GetStyleColor()->mColor); #ifdef IBMBIDI nsresult rv = NS_ERROR_FAILURE; @@ -508,7 +503,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, posResolve.logicalIndex = mAccessKeyInfo->mAccesskeyIndex; rv = bidiUtils->RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), direction, presContext, aRenderingContext, - textRect.x, baseline, + aTextRect.x, baseline, &posResolve, 1); mAccessKeyInfo->mBeforeWidth = posResolve.visualLeftTwips; @@ -517,7 +512,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, { rv = bidiUtils->RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), direction, presContext, aRenderingContext, - textRect.x, baseline); + aTextRect.x, baseline); } } } @@ -537,17 +532,63 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, mAccessKeyInfo->mBeforeWidth = 0; } - aRenderingContext.DrawString(mCroppedTitle, textRect.x, baseline); + aRenderingContext.DrawString(mCroppedTitle, aTextRect.x, baseline); } if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) { - aRenderingContext.FillRect(textRect.x + mAccessKeyInfo->mBeforeWidth, - textRect.y + mAccessKeyInfo->mAccessOffset, + aRenderingContext.FillRect(aTextRect.x + mAccessKeyInfo->mBeforeWidth, + aTextRect.y + mAccessKeyInfo->mAccessOffset, mAccessKeyInfo->mAccessWidth, mAccessKeyInfo->mAccessUnderlineSize); } } +void nsTextBoxFrame::PaintOneShadow(gfxContext* aCtx, + const nsRect& aTextRect, + nsCSSShadowItem* aShadowDetails, + const nscolor& aForegroundColor) { + nsPoint shadowOffset(aShadowDetails->mXOffset, + aShadowDetails->mYOffset); + nscoord blurRadius = PR_MAX(aShadowDetails->mRadius, 0); + + nsRect shadowRect(aTextRect); + shadowRect.MoveBy(shadowOffset); + + gfxRect shadowRectGFX(shadowRect.x, shadowRect.y, shadowRect.width, shadowRect.height); + + nsContextBoxBlur contextBoxBlur; + gfxContext* shadowContext = contextBoxBlur.Init(shadowRectGFX, blurRadius, + PresContext()->AppUnitsPerDevPixel(), + aCtx); + + if (!shadowContext) + return; + + nscolor shadowColor; + if (aShadowDetails->mHasColor) + shadowColor = aShadowDetails->mColor; + else + shadowColor = aForegroundColor; + + // Conjure an nsIRenderingContext from a gfxContext for DrawText + nsCOMPtr renderingContext = nsnull; + nsIDeviceContext* devCtx = PresContext()->DeviceContext(); + devCtx->CreateRenderingContextInstance(*getter_AddRefs(renderingContext)); + if (!renderingContext) return; + renderingContext->Init(devCtx, shadowContext); + + aCtx->Save(); + aCtx->NewPath(); + aCtx->SetColor(gfxRGBA(shadowColor)); + + // Draw the text onto our alpha-only surface to capture the alpha values. + // Remember that the box blur context has a device offset on it, so we don't need to + // translate any coordinates to fit on the surface. + DrawText(*renderingContext, shadowRect, shadowColor); + contextBoxBlur.DoPaint(); + aCtx->Restore(); +} + void nsTextBoxFrame::LayoutTitle(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -888,7 +929,16 @@ nsTextBoxFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState) mState |= NS_STATE_NEED_LAYOUT; - return nsLeafBoxFrame::DoLayout(aBoxLayoutState); + nsresult rv = nsLeafBoxFrame::DoLayout(aBoxLayoutState); + + const nsStyleText* textStyle = GetStyleText(); + if (textStyle->mTextShadow) { + nsPoint origin(0,0); + nsRect textRect = CalcTextRect(*aBoxLayoutState.GetRenderingContext(), origin); + nsRect overflowRect(nsLayoutUtils::GetTextShadowRectsUnion(textRect, this)); + FinishAndStoreOverflow(&overflowRect, GetSize()); + } + return rv; } /* virtual */ void @@ -928,6 +978,36 @@ nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState) } } +nsRect +nsTextBoxFrame::CalcTextRect(nsIRenderingContext &aRenderingContext, const nsPoint &aTextOrigin) +{ + nsRect textRect(aTextOrigin, GetSize()); + textRect.Deflate(GetUsedBorderAndPadding()); + // determine (cropped) title and underline position + nsPresContext* presContext = PresContext(); + LayoutTitle(presContext, aRenderingContext, textRect); + + // make the rect as small as our (cropped) text. + nscoord outerWidth = textRect.width; + textRect.width = mTitleWidth; + + // Align our text within the overall rect by checking our text-align property. + const nsStyleVisibility* vis = GetStyleVisibility(); + const nsStyleText* textStyle = GetStyleText(); + + if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER) + textRect.x += (outerWidth - textRect.width)/2; + else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT) { + if (vis->mDirection == NS_STYLE_DIRECTION_LTR) + textRect.x += (outerWidth - textRect.width); + } + else { + if (vis->mDirection == NS_STYLE_DIRECTION_RTL) + textRect.x += (outerWidth - textRect.width); + } + return textRect; +} + /** * Ok return our dimensions */ diff --git a/layout/xul/base/src/nsTextBoxFrame.h b/layout/xul/base/src/nsTextBoxFrame.h index 1a704166b740..22eecb171913 100644 --- a/layout/xul/base/src/nsTextBoxFrame.h +++ b/layout/xul/base/src/nsTextBoxFrame.h @@ -104,6 +104,8 @@ protected: void CalcTextSize(nsBoxLayoutState& aBoxLayoutState); + nsRect CalcTextRect(nsIRenderingContext &aRenderingContext, const nsPoint &aTextOrigin); + nsTextBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext); void CalculateTitleForWidth(nsPresContext* aPresContext, @@ -123,6 +125,16 @@ private: PRBool AlwaysAppendAccessKey(); PRBool InsertSeparatorBeforeAccessKey(); + void DrawText(nsIRenderingContext& aRenderingContext, + const nsRect& aTextRect, + const nscolor& aOverrideColor); + + void PaintOneShadow(gfxContext * aCtx, + const nsRect& aTextRect, + nsCSSShadowItem* aShadowDetails, + const nscolor& aForegroundColor); + + CroppingStyle mCropType; nsString mTitle; nsString mCroppedTitle;