/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * mozilla.org. * Portions created by the Initial Developer are Copyright (C) 2004 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Stuart Parmenter * Vladimir Vukicevic * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsThebesRenderingContext.h" #include "nsThebesDeviceContext.h" #include "nsString.h" #include "nsTransform2D.h" #include "nsIServiceManager.h" #include "nsIInterfaceRequestorUtils.h" #include "nsGfxCIID.h" #include "nsThebesRegion.h" #include #include #include #include #include "gfxPlatform.h" #ifdef XP_WIN #include "gfxWindowsSurface.h" #include "cairo-win32.h" #endif static NS_DEFINE_CID(kRegionCID, NS_REGION_CID); ////////////////////////////////////////////////////////////////////// // XXXTodo: rename FORM_TWIPS to FROM_APPUNITS #define FROM_TWIPS(_x) ((gfxFloat)((_x)/(mP2A))) #define FROM_TWIPS_INT(_x) (NSToIntRound((gfxFloat)((_x)/(mP2A)))) #define TO_TWIPS(_x) ((nscoord)((_x)*(mP2A))) #define GFX_RECT_FROM_TWIPS_RECT(_r) (gfxRect(FROM_TWIPS((_r).x), FROM_TWIPS((_r).y), FROM_TWIPS((_r).width), FROM_TWIPS((_r).height))) ////////////////////////////////////////////////////////////////////// NS_IMPL_ISUPPORTS1(nsThebesRenderingContext, nsIRenderingContext) // Hard limit substring lengths to 8000 characters ... this lets us statically // size the cluster buffer array in FindSafeLength #define MAX_GFX_TEXT_BUF_SIZE 8000 static PRInt32 GetMaxChunkLength(nsThebesRenderingContext* aContext) { PRInt32 len = aContext->GetMaxStringLength(); return PR_MIN(len, MAX_GFX_TEXT_BUF_SIZE); } static PRInt32 FindSafeLength(nsThebesRenderingContext* aContext, const PRUnichar *aString, PRUint32 aLength, PRUint32 aMaxChunkLength) { if (aLength <= aMaxChunkLength) return aLength; PRInt32 len = aMaxChunkLength; // Ensure that we don't break inside a surrogate pair while (len > 0 && NS_IS_LOW_SURROGATE(aString[len])) { len--; } if (len == 0) { // We don't want our caller to go into an infinite loop, so don't // return zero. It's hard to imagine how we could actually get here // unless there are languages that allow clusters of arbitrary size. // If there are and someone feeds us a 500+ character cluster, too // bad. return aMaxChunkLength; } return len; } static PRInt32 FindSafeLength(nsThebesRenderingContext* aContext, const char *aString, PRUint32 aLength, PRUint32 aMaxChunkLength) { // Since it's ASCII, we don't need to worry about clusters or RTL return PR_MIN(aLength, aMaxChunkLength); } nsThebesRenderingContext::nsThebesRenderingContext() : mLineStyle(nsLineStyle_kNone) , mColor(NS_RGB(0,0,0)) { } nsThebesRenderingContext::~nsThebesRenderingContext() { } ////////////////////////////////////////////////////////////////////// //// nsIRenderingContext NS_IMETHODIMP nsThebesRenderingContext::Init(nsIDeviceContext* aContext, gfxASurface *aThebesSurface) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Init ctx %p thebesSurface %p\n", this, aContext, aThebesSurface)); mDeviceContext = aContext; mWidget = nsnull; mThebes = new gfxContext(aThebesSurface); return (CommonInit()); } NS_IMETHODIMP nsThebesRenderingContext::Init(nsIDeviceContext* aContext, gfxContext *aThebesContext) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Init ctx %p thebesContext %p\n", this, aContext, aThebesContext)); mDeviceContext = aContext; mWidget = nsnull; mThebes = aThebesContext; return (CommonInit()); } NS_IMETHODIMP nsThebesRenderingContext::Init(nsIDeviceContext* aContext, nsIWidget *aWidget) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Init ctx %p widget %p\n", this, aContext, aWidget)); mDeviceContext = aContext; mWidget = aWidget; mThebes = new gfxContext(aWidget->GetThebesSurface()); //mThebes->SetColor(gfxRGBA(0.9, 0.0, 0.0, 0.3)); //mThebes->Paint(); //mThebes->Translate(gfxPoint(300,0)); //mThebes->Rotate(M_PI/4); return (CommonInit()); } NS_IMETHODIMP nsThebesRenderingContext::CommonInit(void) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::CommonInit\n", this)); mThebes->SetLineWidth(1.0); mP2A = mDeviceContext->AppUnitsPerDevPixel(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetDeviceContext(nsIDeviceContext *& aDeviceContext) { aDeviceContext = mDeviceContext; NS_IF_ADDREF(aDeviceContext); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::PushTranslation(PushedTranslation* aState) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PushTranslation\n", this)); // XXX this is slow! PushState(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::PopTranslation(PushedTranslation* aState) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PopTranslation\n", this)); // XXX this is slow! PopState(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::SetTranslation(nscoord aX, nscoord aY) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetTranslation %d %d\n", this, aX, aY)); gfxMatrix newMat(mThebes->CurrentMatrix()); newMat.x0 = aX; newMat.y0 = aY; mThebes->SetMatrix(newMat); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::PushState() { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PushState\n", this)); mThebes->Save(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::PopState() { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PopState\n", this)); mThebes->Restore(); return NS_OK; } // // clipping // NS_IMETHODIMP nsThebesRenderingContext::SetClipRect(const nsRect& aRect, nsClipCombine aCombine) { //return NS_OK; PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetClipRect [%d,%d,%d,%d] %d\n", this, aRect.x, aRect.y, aRect.width, aRect.height, aCombine)); if (aCombine == nsClipCombine_kReplace) { mThebes->ResetClip(); } else if (aCombine != nsClipCombine_kIntersect) { NS_WARNING("Unexpected usage of SetClipRect"); } mThebes->NewPath(); gfxRect clipRect(GFX_RECT_FROM_TWIPS_RECT(aRect)); if (mThebes->UserToDevicePixelSnapped(clipRect, PR_TRUE)) { gfxMatrix mat(mThebes->CurrentMatrix()); mThebes->IdentityMatrix(); mThebes->Rectangle(clipRect); mThebes->SetMatrix(mat); } else { mThebes->Rectangle(clipRect); } mThebes->Clip(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::SetClipRegion(const nsIRegion& pxRegion, nsClipCombine aCombine) { //return NS_OK; // Region is in device coords, no transformation. // This should only be called when there is no transform in place, when we // we just start painting a widget. The region is set by the platform paint // routine. NS_ASSERTION(aCombine == nsClipCombine_kReplace, "Unexpected usage of SetClipRegion"); nsRegionComplexity cplx; pxRegion.GetRegionComplexity(cplx); gfxMatrix mat = mThebes->CurrentMatrix(); mThebes->IdentityMatrix(); mThebes->ResetClip(); // GetBoundingBox, GetRects, FreeRects are non-const nsIRegion *evilPxRegion = const_cast(&pxRegion); if (cplx == eRegionComplexity_rect) { PRInt32 x, y, w, h; evilPxRegion->GetBoundingBox(&x, &y, &w, &h); PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetClipRegion %d [%d,%d,%d,%d]", this, cplx, x, y, w, h)); mThebes->NewPath(); mThebes->Rectangle(gfxRect(x, y, w, h), PR_TRUE); mThebes->Clip(); } else if (cplx == eRegionComplexity_complex) { nsRegionRectSet *rects = nsnull; nsresult rv = evilPxRegion->GetRects (&rects); PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetClipRegion %d %d rects", this, cplx, rects->mNumRects)); if (NS_FAILED(rv) || !rects) { mThebes->SetMatrix(mat); return rv; } mThebes->NewPath(); for (PRUint32 i = 0; i < rects->mNumRects; i++) { mThebes->Rectangle(gfxRect(rects->mRects[i].x, rects->mRects[i].y, rects->mRects[i].width, rects->mRects[i].height), PR_TRUE); } mThebes->Clip(); evilPxRegion->FreeRects (rects); } mThebes->SetMatrix(mat); return NS_OK; } // // other junk // NS_IMETHODIMP nsThebesRenderingContext::SetLineStyle(nsLineStyle aLineStyle) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetLineStyle %d\n", this, aLineStyle)); switch (aLineStyle) { case nsLineStyle_kSolid: mThebes->SetDash(gfxContext::gfxLineSolid); break; case nsLineStyle_kDashed: mThebes->SetDash(gfxContext::gfxLineDashed); break; case nsLineStyle_kDotted: mThebes->SetDash(gfxContext::gfxLineDotted); break; case nsLineStyle_kNone: default: // nothing uses kNone NS_ERROR("SetLineStyle: Invalid line style"); break; } mLineStyle = aLineStyle; return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::SetColor(nscolor aColor) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetColor 0x%08x\n", this, aColor)); /* This sets the color assuming the sRGB color space, since that's what all * CSS colors are defined to be in by the spec. */ mThebes->SetColor(gfxRGBA(aColor)); mColor = aColor; return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetColor(nscolor &aColor) const { aColor = mColor; return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::Translate(nscoord aX, nscoord aY) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Translate %d %d\n", this, aX, aY)); mThebes->Translate (gfxPoint(FROM_TWIPS(aX), FROM_TWIPS(aY))); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::Scale(float aSx, float aSy) { // as far as I can tell, noone actually calls this PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Scale %f %f\n", this, aSx, aSy)); mThebes->Scale (aSx, aSy); return NS_OK; } void nsThebesRenderingContext::UpdateTempTransformMatrix() { //PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::UpdateTempTransformMatrix\n", this)); /***** * Thebes matrix layout: gfx matrix layout: * | xx yx 0 | | m00 m01 0 | * | xy yy 0 | | m10 m11 0 | * | x0 y0 1 | | m20 m21 1 | *****/ const gfxMatrix& ctm = mThebes->CurrentMatrix(); NS_ASSERTION(ctm.yx == 0 && ctm.xy == 0, "Can't represent Thebes matrix to Gfx"); mTempTransform.SetToTranslate(TO_TWIPS(ctm.x0), TO_TWIPS(ctm.y0)); mTempTransform.AddScale(ctm.xx, ctm.yy); } nsTransform2D& nsThebesRenderingContext::CurrentTransform() { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::CurrentTransform\n", this)); UpdateTempTransformMatrix(); return mTempTransform; } /**** **** XXXXXX **** **** On other gfx implementations, the transform returned by this **** has a built in twips to pixels ratio. That is, you pass in **** twips to any nsTransform2D TransformCoord method, and you **** get back pixels. This makes no sense. We don't do this. **** This in turn breaks SVG and ; those should just be **** fixed to not use this! ****/ NS_IMETHODIMP nsThebesRenderingContext::GetCurrentTransform(nsTransform2D *&aTransform) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::GetCurrentTransform\n", this)); UpdateTempTransformMatrix(); aTransform = &mTempTransform; return NS_OK; } void nsThebesRenderingContext::TransformCoord (nscoord *aX, nscoord *aY) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::TransformCoord\n", this)); gfxPoint pt(FROM_TWIPS(*aX), FROM_TWIPS(*aY)); pt = mThebes->UserToDevice (pt); *aX = TO_TWIPS(pt.x); *aY = TO_TWIPS(pt.y); } NS_IMETHODIMP nsThebesRenderingContext::DrawLine(nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawLine %d %d %d %d\n", this, aX0, aY0, aX1, aY1)); gfxPoint p0 = gfxPoint(FROM_TWIPS(aX0), FROM_TWIPS(aY0)); gfxPoint p1 = gfxPoint(FROM_TWIPS(aX1), FROM_TWIPS(aY1)); // we can't draw thick lines with gfx, so we always assume we want pixel-aligned // lines if the rendering context is at 1.0 scale gfxMatrix savedMatrix = mThebes->CurrentMatrix(); if (!savedMatrix.HasNonTranslation()) { p0 = mThebes->UserToDevice(p0); p1 = mThebes->UserToDevice(p1); p0.Round(); p1.Round(); mThebes->IdentityMatrix(); mThebes->NewPath(); // snap straight lines if (p0.x == p1.x) { mThebes->Line(p0 + gfxPoint(0.5, 0), p1 + gfxPoint(0.5, 0)); } else if (p0.y == p1.y) { mThebes->Line(p0 + gfxPoint(0, 0.5), p1 + gfxPoint(0, 0.5)); } else { mThebes->Line(p0, p1); } mThebes->Stroke(); mThebes->SetMatrix(savedMatrix); } else { mThebes->NewPath(); mThebes->Line(p0, p1); mThebes->Stroke(); } return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::DrawRect(const nsRect& aRect) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawRect [%d,%d,%d,%d]\n", this, aRect.x, aRect.y, aRect.width, aRect.height)); mThebes->NewPath(); mThebes->Rectangle(GFX_RECT_FROM_TWIPS_RECT(aRect), PR_TRUE); mThebes->Stroke(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::DrawRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) { DrawRect(nsRect(aX, aY, aWidth, aHeight)); return NS_OK; } /* Clamp r to (0,0) (2^23,2^23) * these are to be device coordinates. * * Returns PR_FALSE if the rectangle is completely out of bounds, * PR_TRUE otherwise. * * This function assumes that it will be called with a rectangle being * drawn into a surface with an identity transformation matrix; that * is, anything above or to the left of (0,0) will be offscreen. * * First it checks if the rectangle is entirely beyond * CAIRO_COORD_MAX; if so, it can't ever appear on the screen -- * PR_FALSE is returned. * * Then it shifts any rectangles with x/y < 0 so that x and y are = 0, * and adjusts the width and height appropriately. For example, a * rectangle from (0,-5) with dimensions (5,10) will become a * rectangle from (0,0) with dimensions (5,5). * * If after negative x/y adjustment to 0, either the width or height * is negative, then the rectangle is completely offscreen, and * nothing is drawn -- PR_FALSE is returned. * * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX, * the width and height are clamped such x+width or y+height are equal * to CAIRO_COORD_MAX, and PR_TRUE is returned. */ #define CAIRO_COORD_MAX (8388608.0) static PRBool ConditionRect(gfxRect& r) { // if either x or y is way out of bounds; // note that we don't handle negative w/h here if (r.pos.x > CAIRO_COORD_MAX || r.pos.y > CAIRO_COORD_MAX) return PR_FALSE; if (r.pos.x < 0.0) { r.size.width += r.pos.x; if (r.size.width < 0.0) return PR_FALSE; r.pos.x = 0.0; } if (r.pos.x + r.size.width > CAIRO_COORD_MAX) { r.size.width = CAIRO_COORD_MAX - r.pos.x; } if (r.pos.y < 0.0) { r.size.height += r.pos.y; if (r.size.height < 0.0) return PR_FALSE; r.pos.y = 0.0; } if (r.pos.y + r.size.height > CAIRO_COORD_MAX) { r.size.height = CAIRO_COORD_MAX - r.pos.y; } return PR_TRUE; } NS_IMETHODIMP nsThebesRenderingContext::FillRect(const nsRect& aRect) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillRect [%d,%d,%d,%d]\n", this, aRect.x, aRect.y, aRect.width, aRect.height)); gfxRect r(GFX_RECT_FROM_TWIPS_RECT(aRect)); /* Clamp coordinates to work around a design bug in cairo */ nscoord bigval = (nscoord)(CAIRO_COORD_MAX*mP2A); if (aRect.width > bigval || aRect.height > bigval || aRect.x < -bigval || aRect.x > bigval || aRect.y < -bigval || aRect.y > bigval) { gfxMatrix mat = mThebes->CurrentMatrix(); r = mat.Transform(r); if (!ConditionRect(r)) return NS_OK; mThebes->IdentityMatrix(); mThebes->NewPath(); PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillRect conditioned to [%f,%f,%f,%f]\n", this, r.pos.x, r.pos.y, r.size.width, r.size.height)); mThebes->Rectangle(r, PR_TRUE); mThebes->Fill(); mThebes->SetMatrix(mat); return NS_OK; } PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillRect raw [%f,%f,%f,%f]\n", this, r.pos.x, r.pos.y, r.size.width, r.size.height)); mThebes->NewPath(); mThebes->Rectangle(r, PR_TRUE); mThebes->Fill(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::FillRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) { FillRect(nsRect(aX, aY, aWidth, aHeight)); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::InvertRect(const nsRect& aRect) { gfxContext::GraphicsOperator lastOp = mThebes->CurrentOperator(); mThebes->SetOperator(gfxContext::OPERATOR_XOR); nsresult rv = FillRect(aRect); mThebes->SetOperator(lastOp); return rv; } NS_IMETHODIMP nsThebesRenderingContext::InvertRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) { return InvertRect(nsRect(aX, aY, aWidth, aHeight)); } NS_IMETHODIMP nsThebesRenderingContext::DrawEllipse(const nsRect& aRect) { return DrawEllipse(aRect.x, aRect.y, aRect.width, aRect.height); } NS_IMETHODIMP nsThebesRenderingContext::DrawEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawEllipse [%d,%d,%d,%d]\n", this, aX, aY, aWidth, aHeight)); mThebes->NewPath(); mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0, FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0), gfxSize(FROM_TWIPS(aWidth), FROM_TWIPS(aHeight))); mThebes->Stroke(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::FillEllipse(const nsRect& aRect) { return FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height); } NS_IMETHODIMP nsThebesRenderingContext::FillEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillEllipse [%d,%d,%d,%d]\n", this, aX, aY, aWidth, aHeight)); mThebes->NewPath(); mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0, FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0), gfxSize(FROM_TWIPS(aWidth), FROM_TWIPS(aHeight))); mThebes->Fill(); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::FillPolygon(const nsPoint twPoints[], PRInt32 aNumPoints) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillPolygon %d\n", this, aNumPoints)); if (aNumPoints == 0) return NS_OK; if (aNumPoints == 4) { } nsAutoArrayPtr pxPoints(new gfxPoint[aNumPoints]); for (int i = 0; i < aNumPoints; i++) { pxPoints[i].x = FROM_TWIPS(twPoints[i].x); pxPoints[i].y = FROM_TWIPS(twPoints[i].y); } mThebes->NewPath(); mThebes->Polygon(pxPoints, aNumPoints); mThebes->Fill(); return NS_OK; } void* nsThebesRenderingContext::GetNativeGraphicData(GraphicDataType aType) { if (aType == NATIVE_GDK_DRAWABLE) { if (mWidget) return mWidget->GetNativeData(NS_NATIVE_WIDGET); } if (aType == NATIVE_THEBES_CONTEXT) return mThebes; if (aType == NATIVE_CAIRO_CONTEXT) return mThebes->GetCairo(); #ifdef XP_WIN if (aType == NATIVE_WINDOWS_DC) { nsRefPtr surf(mThebes->CurrentSurface()); if (!surf || surf->CairoStatus()) return nsnull; return static_cast(static_cast(surf.get()))->GetDC(); } #endif #ifdef XP_OS2 if (aType == NATIVE_OS2_PS) { nsRefPtr surf(mThebes->CurrentSurface()); if (!surf || surf->CairoStatus()) return nsnull; return (void*)(static_cast(static_cast(surf.get()))->GetPS()); } #endif return nsnull; } NS_IMETHODIMP nsThebesRenderingContext::PushFilter(const nsRect& twRect, PRBool aAreaIsOpaque, float aOpacity) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PushFilter [%d,%d,%d,%d] isOpaque: %d opacity: %f\n", this, twRect.x, twRect.y, twRect.width, twRect.height, aAreaIsOpaque, aOpacity)); mOpacityArray.AppendElement(aOpacity); mThebes->Save(); mThebes->Clip(GFX_RECT_FROM_TWIPS_RECT(twRect)); mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::PopFilter() { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PopFilter\n")); if (mOpacityArray.Length() > 0) { float f = mOpacityArray[mOpacityArray.Length()-1]; mOpacityArray.RemoveElementAt(mOpacityArray.Length()-1); mThebes->PopGroupToSource(); if (f < 0.0) { mThebes->SetOperator(gfxContext::OPERATOR_SOURCE); mThebes->Paint(); } else { mThebes->SetOperator(gfxContext::OPERATOR_OVER); mThebes->Paint(f); } mThebes->Restore(); } return NS_OK; } // // text junk // NS_IMETHODIMP nsThebesRenderingContext::SetRightToLeftText(PRBool aIsRTL) { return mFontMetrics->SetRightToLeftText(aIsRTL); } NS_IMETHODIMP nsThebesRenderingContext::GetRightToLeftText(PRBool* aIsRTL) { *aIsRTL = mFontMetrics->GetRightToLeftText(); return NS_OK; } void nsThebesRenderingContext::SetTextRunRTL(PRBool aIsRTL) { mFontMetrics->SetTextRunRTL(aIsRTL); } NS_IMETHODIMP nsThebesRenderingContext::SetFont(const nsFont& aFont, nsIAtom* aLangGroup, gfxUserFontSet *aUserFontSet) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetFont %p\n", this, &aFont)); nsCOMPtr newMetrics; mDeviceContext->GetMetricsFor(aFont, aLangGroup, aUserFontSet, *getter_AddRefs(newMetrics)); mFontMetrics = reinterpret_cast(newMetrics.get()); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::SetFont(nsIFontMetrics *aFontMetrics) { PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetFont[Metrics] %p\n", this, aFontMetrics)); mFontMetrics = static_cast(aFontMetrics); return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetFontMetrics(nsIFontMetrics *&aFontMetrics) { aFontMetrics = mFontMetrics; NS_IF_ADDREF(aFontMetrics); return NS_OK; } PRInt32 nsThebesRenderingContext::GetMaxStringLength() { if (!mFontMetrics) return 1; return mFontMetrics->GetMaxStringLength(); } NS_IMETHODIMP nsThebesRenderingContext::GetWidth(char aC, nscoord &aWidth) { if (aC == ' ' && mFontMetrics) return mFontMetrics->GetSpaceWidth(aWidth); return GetWidth(&aC, 1, aWidth); } NS_IMETHODIMP nsThebesRenderingContext::GetWidth(PRUnichar aC, nscoord &aWidth, PRInt32 *aFontID) { return GetWidth(&aC, 1, aWidth, aFontID); } NS_IMETHODIMP nsThebesRenderingContext::GetWidth(const nsString& aString, nscoord &aWidth, PRInt32 *aFontID) { return GetWidth(aString.get(), aString.Length(), aWidth, aFontID); } NS_IMETHODIMP nsThebesRenderingContext::GetWidth(const char* aString, nscoord& aWidth) { return GetWidth(aString, strlen(aString), aWidth); } NS_IMETHODIMP nsThebesRenderingContext::DrawString(const nsString& aString, nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord* aSpacing) { return DrawString(aString.get(), aString.Length(), aX, aY, aFontID, aSpacing); } NS_IMETHODIMP nsThebesRenderingContext::GetWidth(const char* aString, PRUint32 aLength, nscoord& aWidth) { PRUint32 maxChunkLength = GetMaxChunkLength(this); aWidth = 0; while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nscoord width; nsresult rv = GetWidthInternal(aString, len, width); if (NS_FAILED(rv)) return rv; aWidth += width; aLength -= len; aString += len; } return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetWidth(const PRUnichar *aString, PRUint32 aLength, nscoord &aWidth, PRInt32 *aFontID) { PRUint32 maxChunkLength = GetMaxChunkLength(this); aWidth = 0; if (aFontID) { *aFontID = 0; } while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nscoord width; nsresult rv = GetWidthInternal(aString, len, width); if (NS_FAILED(rv)) return rv; aWidth += width; aLength -= len; aString += len; } return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetTextDimensions(const char* aString, PRUint32 aLength, nsTextDimensions& aDimensions) { PRUint32 maxChunkLength = GetMaxChunkLength(this); if (aLength <= maxChunkLength) return GetTextDimensionsInternal(aString, aLength, aDimensions); PRBool firstIteration = PR_TRUE; while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nsTextDimensions dimensions; nsresult rv = GetTextDimensionsInternal(aString, len, dimensions); if (NS_FAILED(rv)) return rv; if (firstIteration) { // Instead of combining with a Clear()ed nsTextDimensions, we // assign directly in the first iteration. This ensures that // negative ascent/ descent can be returned. aDimensions = dimensions; } else { aDimensions.Combine(dimensions); } aLength -= len; aString += len; firstIteration = PR_FALSE; } return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetTextDimensions(const PRUnichar* aString, PRUint32 aLength, nsTextDimensions& aDimensions, PRInt32* aFontID) { PRUint32 maxChunkLength = GetMaxChunkLength(this); if (aLength <= maxChunkLength) return GetTextDimensionsInternal(aString, aLength, aDimensions); if (aFontID) { *aFontID = nsnull; } PRBool firstIteration = PR_TRUE; while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nsTextDimensions dimensions; nsresult rv = GetTextDimensionsInternal(aString, len, dimensions); if (NS_FAILED(rv)) return rv; if (firstIteration) { // Instead of combining with a Clear()ed nsTextDimensions, we // assign directly in the first iteration. This ensures that // negative ascent/ descent can be returned. aDimensions = dimensions; } else { aDimensions.Combine(dimensions); } aLength -= len; aString += len; firstIteration = PR_FALSE; } return NS_OK; } #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) NS_IMETHODIMP nsThebesRenderingContext::GetTextDimensions(const char* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID) { PRUint32 maxChunkLength = GetMaxChunkLength(this); if (aLength <= PRInt32(maxChunkLength)) return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks, aNumBreaks, aDimensions, aNumCharsFit, aLastWordDimensions, aFontID); if (aFontID) { *aFontID = 0; } // Do a naive implementation based on 3-arg GetTextDimensions PRInt32 x = 0; PRInt32 wordCount; for (wordCount = 0; wordCount < aNumBreaks; ++wordCount) { PRInt32 lastBreak = wordCount > 0 ? aBreaks[wordCount - 1] : 0; nsTextDimensions dimensions; NS_ASSERTION(aBreaks[wordCount] > lastBreak, "Breaks must be monotonically increasing"); NS_ASSERTION(aBreaks[wordCount] <= aLength, "Breaks can't exceed string length"); // Call safe method nsresult rv = GetTextDimensions(aString + lastBreak, aBreaks[wordCount] - lastBreak, dimensions); if (NS_FAILED(rv)) return rv; x += dimensions.width; // The first word always "fits" if (x > aAvailWidth && wordCount > 0) break; // aDimensions ascent/descent should exclude the last word (unless there // is only one word) so we let it run one word behind if (wordCount == 0) { aDimensions = dimensions; } else { aDimensions.Combine(aLastWordDimensions); } aNumCharsFit = aBreaks[wordCount]; aLastWordDimensions = dimensions; } // aDimensions width should include all the text aDimensions.width = x; return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetTextDimensions(const PRUnichar* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID) { PRUint32 maxChunkLength = GetMaxChunkLength(this); if (aLength <= PRInt32(maxChunkLength)) return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks, aNumBreaks, aDimensions, aNumCharsFit, aLastWordDimensions, aFontID); if (aFontID) { *aFontID = 0; } // Do a naive implementation based on 3-arg GetTextDimensions PRInt32 x = 0; PRInt32 wordCount; for (wordCount = 0; wordCount < aNumBreaks; ++wordCount) { PRInt32 lastBreak = wordCount > 0 ? aBreaks[wordCount - 1] : 0; NS_ASSERTION(aBreaks[wordCount] > lastBreak, "Breaks must be monotonically increasing"); NS_ASSERTION(aBreaks[wordCount] <= aLength, "Breaks can't exceed string length"); nsTextDimensions dimensions; // Call safe method nsresult rv = GetTextDimensions(aString + lastBreak, aBreaks[wordCount] - lastBreak, dimensions); if (NS_FAILED(rv)) return rv; x += dimensions.width; // The first word always "fits" if (x > aAvailWidth && wordCount > 0) break; // aDimensions ascent/descent should exclude the last word (unless there // is only one word) so we let it run one word behind if (wordCount == 0) { aDimensions = dimensions; } else { aDimensions.Combine(aLastWordDimensions); } aNumCharsFit = aBreaks[wordCount]; aLastWordDimensions = dimensions; } // aDimensions width should include all the text aDimensions.width = x; return NS_OK; } #endif #ifdef MOZ_MATHML NS_IMETHODIMP nsThebesRenderingContext::GetBoundingMetrics(const char* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics) { PRUint32 maxChunkLength = GetMaxChunkLength(this); if (aLength <= maxChunkLength) return GetBoundingMetricsInternal(aString, aLength, aBoundingMetrics); PRBool firstIteration = PR_TRUE; while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nsBoundingMetrics metrics; nsresult rv = GetBoundingMetricsInternal(aString, len, metrics); if (NS_FAILED(rv)) return rv; if (firstIteration) { // Instead of combining with a Clear()ed nsBoundingMetrics, we // assign directly in the first iteration. This ensures that // negative ascent/ descent can be returned and the left bearing // is properly initialized. aBoundingMetrics = metrics; } else { aBoundingMetrics += metrics; } aLength -= len; aString += len; firstIteration = PR_FALSE; } return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics, PRInt32* aFontID) { PRUint32 maxChunkLength = GetMaxChunkLength(this); if (aLength <= maxChunkLength) return GetBoundingMetricsInternal(aString, aLength, aBoundingMetrics, aFontID); if (aFontID) { *aFontID = 0; } PRBool firstIteration = PR_TRUE; while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nsBoundingMetrics metrics; nsresult rv = GetBoundingMetricsInternal(aString, len, metrics); if (NS_FAILED(rv)) return rv; if (firstIteration) { // Instead of combining with a Clear()ed nsBoundingMetrics, we // assign directly in the first iteration. This ensures that // negative ascent/ descent can be returned and the left bearing // is properly initialized. aBoundingMetrics = metrics; } else { aBoundingMetrics += metrics; } aLength -= len; aString += len; firstIteration = PR_FALSE; } return NS_OK; } #endif NS_IMETHODIMP nsThebesRenderingContext::DrawString(const char *aString, PRUint32 aLength, nscoord aX, nscoord aY, const nscoord* aSpacing) { PRUint32 maxChunkLength = GetMaxChunkLength(this); while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nsresult rv = DrawStringInternal(aString, len, aX, aY); if (NS_FAILED(rv)) return rv; aLength -= len; if (aLength > 0) { nscoord width; rv = GetWidthInternal(aString, len, width); if (NS_FAILED(rv)) return rv; aX += width; aString += len; } } return NS_OK; } NS_IMETHODIMP nsThebesRenderingContext::DrawString(const PRUnichar *aString, PRUint32 aLength, nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord* aSpacing) { PRUint32 maxChunkLength = GetMaxChunkLength(this); if (aLength <= maxChunkLength) { return DrawStringInternal(aString, aLength, aX, aY, aFontID, aSpacing); } PRBool isRTL = PR_FALSE; GetRightToLeftText(&isRTL); if (isRTL) { nscoord totalWidth = 0; if (aSpacing) { for (PRUint32 i = 0; i < aLength; ++i) { totalWidth += aSpacing[i]; } } else { nsresult rv = GetWidth(aString, aLength, totalWidth); if (NS_FAILED(rv)) return rv; } aX += totalWidth; } while (aLength > 0) { PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength); nscoord width = 0; if (aSpacing) { for (PRInt32 i = 0; i < len; ++i) { width += aSpacing[i]; } } else { nsresult rv = GetWidthInternal(aString, len, width); if (NS_FAILED(rv)) return rv; } if (isRTL) { aX -= width; } nsresult rv = DrawStringInternal(aString, len, aX, aY, aFontID, aSpacing); if (NS_FAILED(rv)) return rv; aLength -= len; if (!isRTL) { aX += width; } aString += len; if (aSpacing) { aSpacing += len; } } return NS_OK; } nsresult nsThebesRenderingContext::GetWidthInternal(const char* aString, PRUint32 aLength, nscoord& aWidth) { #ifdef DISABLE_TEXT aWidth = (8 * aLength); return NS_OK; #endif if (aLength == 0) { aWidth = 0; return NS_OK; } return mFontMetrics->GetWidth(aString, aLength, aWidth, this); } nsresult nsThebesRenderingContext::GetWidthInternal(const PRUnichar *aString, PRUint32 aLength, nscoord &aWidth, PRInt32 *aFontID) { #ifdef DISABLE_TEXT aWidth = (8 * aLength); return NS_OK; #endif if (aLength == 0) { aWidth = 0; return NS_OK; } return mFontMetrics->GetWidth(aString, aLength, aWidth, aFontID, this); } nsresult nsThebesRenderingContext::GetTextDimensionsInternal(const char* aString, PRUint32 aLength, nsTextDimensions& aDimensions) { mFontMetrics->GetMaxAscent(aDimensions.ascent); mFontMetrics->GetMaxDescent(aDimensions.descent); return GetWidth(aString, aLength, aDimensions.width); } nsresult nsThebesRenderingContext::GetTextDimensionsInternal(const PRUnichar* aString, PRUint32 aLength, nsTextDimensions& aDimensions, PRInt32* aFontID) { mFontMetrics->GetMaxAscent(aDimensions.ascent); mFontMetrics->GetMaxDescent(aDimensions.descent); return GetWidth(aString, aLength, aDimensions.width, aFontID); } #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) || defined(XP_MACOSX) || defined (MOZ_DFB) nsresult nsThebesRenderingContext::GetTextDimensionsInternal(const char* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID) { return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsThebesRenderingContext::GetTextDimensionsInternal(const PRUnichar* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID) { return NS_ERROR_NOT_IMPLEMENTED; } #endif #ifdef MOZ_MATHML nsresult nsThebesRenderingContext::GetBoundingMetricsInternal(const char* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics) { return mFontMetrics->GetBoundingMetrics(aString, aLength, this, aBoundingMetrics); } nsresult nsThebesRenderingContext::GetBoundingMetricsInternal(const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics, PRInt32* aFontID) { return mFontMetrics->GetBoundingMetrics(aString, aLength, this, aBoundingMetrics); } #endif // MOZ_MATHML nsresult nsThebesRenderingContext::DrawStringInternal(const char *aString, PRUint32 aLength, nscoord aX, nscoord aY, const nscoord* aSpacing) { #ifdef DISABLE_TEXT return NS_OK; #endif return mFontMetrics->DrawString(aString, aLength, aX, aY, aSpacing, this); } nsresult nsThebesRenderingContext::DrawStringInternal(const PRUnichar *aString, PRUint32 aLength, nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord* aSpacing) { #ifdef DISABLE_TEXT return NS_OK; #endif return mFontMetrics->DrawString(aString, aLength, aX, aY, aFontID, aSpacing, this); } PRInt32 nsThebesRenderingContext::GetPosition(const PRUnichar *aText, PRUint32 aLength, nsPoint aPt) { return -1; } NS_IMETHODIMP nsThebesRenderingContext::GetRangeWidth(const PRUnichar *aText, PRUint32 aLength, PRUint32 aStart, PRUint32 aEnd, PRUint32 &aWidth) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsThebesRenderingContext::GetRangeWidth(const char *aText, PRUint32 aLength, PRUint32 aStart, PRUint32 aEnd, PRUint32 &aWidth) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsThebesRenderingContext::RenderEPS(const nsRect& aRect, FILE *aDataFile) { return NS_ERROR_NOT_IMPLEMENTED; }