LCOV - code coverage report
Current view: directory - gfx/thebes - gfxContext.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 988 47 4.8 %
Date: 2012-06-02 Functions: 103 10 9.7 %

       1                 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Oracle Corporation code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Oracle Corporation.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2005
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Stuart Parmenter <pavlov@pavlov.net>
      23                 :  *   Vladimir Vukicevic <vladimir@pobox.com>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #ifdef _MSC_VER
      40                 : #define _USE_MATH_DEFINES
      41                 : #endif
      42                 : #include <math.h>
      43                 : 
      44                 : #ifndef M_PI
      45                 : #define M_PI 3.14159265358979323846
      46                 : #endif
      47                 : 
      48                 : #include "cairo.h"
      49                 : 
      50                 : #include "gfxContext.h"
      51                 : 
      52                 : #include "gfxColor.h"
      53                 : #include "gfxMatrix.h"
      54                 : #include "gfxASurface.h"
      55                 : #include "gfxPattern.h"
      56                 : #include "gfxPlatform.h"
      57                 : #include "gfxTeeSurface.h"
      58                 : 
      59                 : using namespace mozilla;
      60                 : using namespace mozilla::gfx;
      61                 : 
      62                 : /* This class lives on the stack and allows gfxContext users to easily, and
      63                 :  * performantly get a gfx::Pattern to use for drawing in their current context.
      64                 :  */
      65                 : class GeneralPattern
      66                 : {
      67                 : public:    
      68               0 :   GeneralPattern(gfxContext *aContext) : mContext(aContext), mPattern(NULL) {}
      69               0 :   ~GeneralPattern() { if (mPattern) { mPattern->~Pattern(); } }
      70                 : 
      71               0 :   operator mozilla::gfx::Pattern&()
      72                 :   {
      73               0 :     gfxContext::AzureState &state = mContext->CurrentState();
      74                 : 
      75               0 :     if (state.pattern) {
      76               0 :       return *state.pattern->GetPattern(mContext->mDT);
      77               0 :     } else if (state.sourceSurface) {
      78               0 :       mPattern = new (mSurfacePattern.addr())
      79               0 :         SurfacePattern(state.sourceSurface, EXTEND_CLAMP, state.surfTransform);
      80               0 :       return *mPattern;
      81                 :     } else {
      82               0 :       mPattern = new (mColorPattern.addr())
      83               0 :         ColorPattern(state.color);
      84               0 :       return *mPattern;
      85                 :     }
      86                 :   }
      87                 : 
      88                 : private:
      89                 :   union {
      90                 :     mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
      91                 :     mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
      92                 :   };
      93                 : 
      94                 :   gfxContext *mContext;
      95                 :   Pattern *mPattern;
      96                 : };
      97                 : 
      98              64 : gfxContext::gfxContext(gfxASurface *surface)
      99                 :   : mRefCairo(NULL)
     100              64 :   , mSurface(surface)
     101                 : {
     102              64 :   MOZ_COUNT_CTOR(gfxContext);
     103                 : 
     104              64 :   mCairo = cairo_create(surface->CairoSurface());
     105              64 :   mFlags = surface->GetDefaultContextFlags();
     106              64 :   if (mSurface->GetRotateForLandscape()) {
     107                 :     // Rotate page 90 degrees to draw landscape page on portrait paper
     108               0 :     gfxIntSize size = mSurface->GetSize();
     109               0 :     Translate(gfxPoint(0, size.width));
     110                 :     gfxMatrix matrix(0, -1,
     111                 :                       1,  0,
     112               0 :                       0,  0);
     113               0 :     Multiply(matrix);
     114                 :   }
     115              64 : }
     116                 : 
     117               0 : gfxContext::gfxContext(DrawTarget *aTarget)
     118                 :   : mPathIsRect(false)
     119                 :   , mTransformChanged(false)
     120                 :   , mCairo(NULL)
     121                 :   , mRefCairo(NULL)
     122                 :   , mSurface(NULL)
     123                 :   , mFlags(0)
     124                 :   , mDT(aTarget)
     125               0 :   , mOriginalDT(aTarget)
     126                 : {
     127               0 :   MOZ_COUNT_CTOR(gfxContext);
     128                 : 
     129               0 :   mStateStack.SetLength(1);
     130               0 :   CurrentState().drawTarget = mDT;
     131               0 :   mDT->SetTransform(Matrix());
     132               0 : }
     133                 : 
     134             128 : gfxContext::~gfxContext()
     135                 : {
     136              64 :   if (mCairo) {
     137              64 :     cairo_destroy(mCairo);
     138                 :   }
     139              64 :   if (mRefCairo) {
     140               0 :     cairo_destroy(mRefCairo);
     141                 :   }
     142              64 :   if (mDT) {
     143               0 :     for (int i = mStateStack.Length() - 1; i >= 0; i--) {
     144               0 :       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
     145               0 :         mDT->PopClip();
     146                 :       }
     147                 : 
     148               0 :       if (mStateStack[i].clipWasReset) {
     149               0 :         break;
     150                 :       }
     151                 :     }
     152               0 :     mDT->Flush();
     153                 :   }
     154              64 :   MOZ_COUNT_DTOR(gfxContext);
     155              64 : }
     156                 : 
     157                 : gfxASurface *
     158               0 : gfxContext::OriginalSurface()
     159                 : {
     160               0 :     return mSurface;
     161                 : }
     162                 : 
     163                 : already_AddRefed<gfxASurface>
     164               0 : gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
     165                 : {
     166               0 :   if (mCairo) {
     167               0 :     cairo_surface_t *s = cairo_get_group_target(mCairo);
     168               0 :     if (s == mSurface->CairoSurface()) {
     169               0 :         if (dx && dy)
     170               0 :             cairo_surface_get_device_offset(s, dx, dy);
     171               0 :         gfxASurface *ret = mSurface;
     172               0 :         NS_ADDREF(ret);
     173               0 :         return ret;
     174                 :     }
     175                 : 
     176               0 :     if (dx && dy)
     177               0 :         cairo_surface_get_device_offset(s, dx, dy);
     178               0 :     return gfxASurface::Wrap(s);
     179                 :   } else {
     180               0 :     if (dx && dy) {
     181               0 :       *dx = *dy = 0;
     182                 :     }
     183                 :     // An Azure context doesn't have a surface backing it.
     184               0 :     return NULL;
     185                 :   }
     186                 : }
     187                 : 
     188                 : cairo_t *
     189               0 : gfxContext::GetCairo()
     190                 : {
     191               0 :   if (mCairo) {
     192               0 :     return mCairo;
     193                 :   }
     194                 : 
     195               0 :   if (mRefCairo) {
     196                 :     // Set transform!
     197               0 :     return mRefCairo;
     198                 :   }
     199                 : 
     200               0 :   mRefCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface()); 
     201                 : 
     202               0 :   return mRefCairo;
     203                 : }
     204                 : 
     205                 : void
     206               0 : gfxContext::Save()
     207                 : {
     208               0 :   if (mCairo) {
     209               0 :     cairo_save(mCairo);
     210                 :   } else {
     211               0 :     CurrentState().transform = mDT->GetTransform();
     212               0 :     mStateStack.AppendElement(AzureState(CurrentState()));
     213               0 :     CurrentState().clipWasReset = false;
     214               0 :     CurrentState().pushedClips.Clear();
     215                 :   }
     216               0 : }
     217                 : 
     218                 : void
     219               0 : gfxContext::Restore()
     220                 : {
     221               0 :   if (mCairo) {
     222               0 :     cairo_restore(mCairo);
     223                 :   } else {
     224               0 :     for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
     225               0 :       mDT->PopClip();
     226                 :     }
     227                 : 
     228               0 :     if (CurrentState().clipWasReset &&
     229               0 :         CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
     230               0 :       PushClipsToDT(mDT);
     231                 :     }
     232                 : 
     233               0 :     mStateStack.RemoveElementAt(mStateStack.Length() - 1);
     234                 : 
     235               0 :     if (mPathBuilder || mPath || mPathIsRect) {
     236               0 :       mTransformChanged = true;
     237               0 :       mPathTransform = mDT->GetTransform();
     238                 :     }
     239                 : 
     240               0 :     mDT = CurrentState().drawTarget;
     241               0 :     mDT->SetTransform(CurrentState().transform);
     242                 :   }
     243               0 : }
     244                 : 
     245                 : // drawing
     246                 : void
     247               0 : gfxContext::NewPath()
     248                 : {
     249               0 :   if (mCairo) {
     250               0 :     cairo_new_path(mCairo);
     251                 :   } else {
     252               0 :     mPath = NULL;
     253               0 :     mPathBuilder = NULL;
     254               0 :     mPathIsRect = false;
     255               0 :     mTransformChanged = false;
     256                 :   }
     257               0 : }
     258                 : 
     259                 : void
     260               0 : gfxContext::ClosePath()
     261                 : {
     262               0 :   if (mCairo) {
     263               0 :     cairo_close_path(mCairo);
     264                 :   } else {
     265               0 :     EnsurePathBuilder();
     266               0 :     mPathBuilder->Close();
     267                 :   }
     268               0 : }
     269                 : 
     270               0 : already_AddRefed<gfxPath> gfxContext::CopyPath() const
     271                 : {
     272               0 :   if (mCairo) {
     273               0 :     nsRefPtr<gfxPath> path = new gfxPath(cairo_copy_path(mCairo));
     274               0 :     return path.forget();
     275                 :   } else {
     276                 :     // XXX - This is not yet supported for Azure.
     277               0 :     return NULL;
     278                 :   }
     279                 : }
     280                 : 
     281               0 : void gfxContext::AppendPath(gfxPath* path)
     282                 : {
     283               0 :   if (mCairo) {
     284               0 :     if (path->mPath->status == CAIRO_STATUS_SUCCESS && path->mPath->num_data != 0)
     285               0 :         cairo_append_path(mCairo, path->mPath);
     286                 :   } else {
     287                 :     // XXX - This is not yet supported for Azure.
     288               0 :     return;
     289                 :   }
     290                 : }
     291                 : 
     292                 : gfxPoint
     293               0 : gfxContext::CurrentPoint()
     294                 : {
     295               0 :   if (mCairo) {
     296                 :     double x, y;
     297               0 :     cairo_get_current_point(mCairo, &x, &y);
     298               0 :     return gfxPoint(x, y);
     299                 :   } else {
     300               0 :     EnsurePathBuilder();
     301               0 :     return ThebesPoint(mPathBuilder->CurrentPoint());
     302                 :   }
     303                 : }
     304                 : 
     305                 : void
     306               0 : gfxContext::Stroke()
     307                 : {
     308               0 :   if (mCairo) {
     309               0 :     cairo_stroke_preserve(mCairo);
     310                 :   } else {
     311               0 :     AzureState &state = CurrentState();
     312               0 :     if (mPathIsRect) {
     313               0 :       mDT->StrokeRect(mRect, GeneralPattern(this),
     314                 :                       state.strokeOptions,
     315               0 :                       DrawOptions(1.0f, GetOp(), state.aaMode));
     316                 :     } else {
     317               0 :       EnsurePath();
     318                 : 
     319               0 :       mDT->Stroke(mPath, GeneralPattern(this), state.strokeOptions,
     320               0 :                   DrawOptions(1.0f, GetOp(), state.aaMode));
     321                 :     }
     322                 :   }
     323               0 : }
     324                 : 
     325                 : void
     326              21 : gfxContext::Fill()
     327                 : {
     328              21 :   if (mCairo) {
     329              21 :     cairo_fill_preserve(mCairo);
     330                 :   } else {
     331               0 :     FillAzure(1.0f);
     332                 :   }
     333              21 : }
     334                 : 
     335                 : void
     336               0 : gfxContext::FillWithOpacity(gfxFloat aOpacity)
     337                 : {
     338               0 :   if (mCairo) {
     339                 :     // This method exists in the hope that one day cairo gets a direct
     340                 :     // API for this, and then we would change this method to use that
     341                 :     // API instead.
     342               0 :     if (aOpacity != 1.0) {
     343               0 :       gfxContextAutoSaveRestore saveRestore(this);
     344               0 :       Clip();
     345               0 :       Paint(aOpacity);
     346                 :     } else {
     347               0 :       Fill();
     348                 :     }
     349                 :   } else {
     350               0 :     FillAzure(Float(aOpacity));
     351                 :   }
     352               0 : }
     353                 : 
     354                 : void
     355               0 : gfxContext::MoveTo(const gfxPoint& pt)
     356                 : {
     357               0 :   if (mCairo) {
     358               0 :     cairo_move_to(mCairo, pt.x, pt.y);
     359                 :   } else {
     360               0 :     EnsurePathBuilder();
     361               0 :     mPathBuilder->MoveTo(ToPoint(pt));
     362                 :   }
     363               0 : }
     364                 : 
     365                 : void
     366               0 : gfxContext::NewSubPath()
     367                 : {
     368               0 :   if (mCairo) {
     369               0 :     cairo_new_sub_path(mCairo);
     370                 :   } else {
     371                 :     // XXX - This has no users, we should kill it, it should be equivelant to a
     372                 :     // MoveTo to the path's current point.
     373                 :   }
     374               0 : }
     375                 : 
     376                 : void
     377               0 : gfxContext::LineTo(const gfxPoint& pt)
     378                 : {
     379               0 :   if (mCairo) {
     380               0 :     cairo_line_to(mCairo, pt.x, pt.y);
     381                 :   } else {
     382               0 :     EnsurePathBuilder();
     383               0 :     mPathBuilder->LineTo(ToPoint(pt));
     384                 :   }
     385               0 : }
     386                 : 
     387                 : void
     388               0 : gfxContext::CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3)
     389                 : {
     390               0 :   if (mCairo) {
     391               0 :     cairo_curve_to(mCairo, pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
     392                 :   } else {
     393               0 :     EnsurePathBuilder();
     394               0 :     mPathBuilder->BezierTo(ToPoint(pt1), ToPoint(pt2), ToPoint(pt3));
     395                 :   }
     396               0 : }
     397                 : 
     398                 : void
     399               0 : gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
     400                 : {
     401               0 :   if (mCairo) {
     402                 :     double cx, cy;
     403               0 :     cairo_get_current_point(mCairo, &cx, &cy);
     404                 :     cairo_curve_to(mCairo,
     405                 :                    (cx + pt1.x * 2.0) / 3.0,
     406                 :                    (cy + pt1.y * 2.0) / 3.0,
     407                 :                    (pt1.x * 2.0 + pt2.x) / 3.0,
     408                 :                    (pt1.y * 2.0 + pt2.y) / 3.0,
     409                 :                    pt2.x,
     410               0 :                    pt2.y);
     411                 :   } else {
     412               0 :     EnsurePathBuilder();
     413               0 :     mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2));
     414                 :   }
     415               0 : }
     416                 : 
     417                 : void
     418               0 : gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
     419                 :                 gfxFloat angle1, gfxFloat angle2)
     420                 : {
     421               0 :   if (mCairo) {
     422               0 :     cairo_arc(mCairo, center.x, center.y, radius, angle1, angle2);
     423                 :   } else {
     424               0 :     EnsurePathBuilder();
     425               0 :     mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2));
     426                 :   }
     427               0 : }
     428                 : 
     429                 : void
     430               0 : gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius,
     431                 :                         gfxFloat angle1, gfxFloat angle2)
     432                 : {
     433               0 :   if (mCairo) {
     434               0 :     cairo_arc_negative(mCairo, center.x, center.y, radius, angle1, angle2);
     435                 :   } else {
     436               0 :     EnsurePathBuilder();
     437               0 :     mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1));
     438                 :   }
     439               0 : }
     440                 : 
     441                 : void
     442               0 : gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
     443                 : {
     444               0 :   if (mCairo) {
     445               0 :     MoveTo(start);
     446               0 :     LineTo(end);
     447                 :   } else {
     448               0 :     EnsurePathBuilder();
     449               0 :     mPathBuilder->MoveTo(ToPoint(start));
     450               0 :     mPathBuilder->LineTo(ToPoint(end));
     451                 :   }
     452               0 : }
     453                 : 
     454                 : // XXX snapToPixels is only valid when snapping for filled
     455                 : // rectangles and for even-width stroked rectangles.
     456                 : // For odd-width stroked rectangles, we need to offset x/y by
     457                 : // 0.5...
     458                 : void
     459              21 : gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
     460                 : {
     461              21 :   if (mCairo) {
     462              21 :     if (snapToPixels) {
     463               0 :         gfxRect snappedRect(rect);
     464                 : 
     465               0 :         if (UserToDevicePixelSnapped(snappedRect, true))
     466                 :         {
     467                 :             cairo_matrix_t mat;
     468               0 :             cairo_get_matrix(mCairo, &mat);
     469               0 :             cairo_identity_matrix(mCairo);
     470               0 :             Rectangle(snappedRect);
     471               0 :             cairo_set_matrix(mCairo, &mat);
     472                 : 
     473               0 :             return;
     474                 :         }
     475                 :     }
     476                 : 
     477              21 :     cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
     478                 :   } else {
     479               0 :     Rect rec = ToRect(rect);
     480                 : 
     481               0 :     if (snapToPixels) {
     482               0 :       gfxRect newRect(rect);
     483               0 :       if (UserToDevicePixelSnapped(newRect, true)) {
     484               0 :         gfxMatrix mat = ThebesMatrix(mDT->GetTransform());
     485               0 :         mat.Invert();
     486                 : 
     487                 :         // We need the user space rect.
     488               0 :         rec = ToRect(mat.TransformBounds(newRect));
     489                 :       }
     490                 :     }
     491                 : 
     492               0 :     if (!mPathBuilder && !mPathIsRect) {
     493               0 :       mPathIsRect = true;
     494               0 :       mRect = rec;
     495               0 :       return;
     496               0 :     } else if (!mPathBuilder) {
     497               0 :       EnsurePathBuilder();
     498                 :     }
     499                 :     
     500               0 :     mPathBuilder->MoveTo(rec.TopLeft());
     501               0 :     mPathBuilder->LineTo(rec.TopRight());
     502               0 :     mPathBuilder->LineTo(rec.BottomRight());
     503               0 :     mPathBuilder->LineTo(rec.BottomLeft());
     504               0 :     mPathBuilder->Close();
     505                 :   }
     506                 : }
     507                 : 
     508                 : void
     509               0 : gfxContext::Ellipse(const gfxPoint& center, const gfxSize& dimensions)
     510                 : {
     511               0 :   gfxSize halfDim = dimensions / 2.0;
     512               0 :   gfxRect r(center - gfxPoint(halfDim.width, halfDim.height), dimensions);
     513               0 :   gfxCornerSizes c(halfDim, halfDim, halfDim, halfDim);
     514                 : 
     515               0 :   RoundedRectangle (r, c);
     516               0 : }
     517                 : 
     518                 : void
     519               0 : gfxContext::Polygon(const gfxPoint *points, PRUint32 numPoints)
     520                 : {
     521               0 :   if (mCairo) {
     522               0 :     if (numPoints == 0)
     523               0 :         return;
     524                 : 
     525               0 :     cairo_move_to(mCairo, points[0].x, points[0].y);
     526               0 :     for (PRUint32 i = 1; i < numPoints; ++i) {
     527               0 :         cairo_line_to(mCairo, points[i].x, points[i].y);
     528                 :     }
     529                 :   } else {
     530               0 :     if (numPoints == 0) {
     531               0 :       return;
     532                 :     }
     533                 : 
     534               0 :     EnsurePathBuilder();
     535                 : 
     536               0 :     mPathBuilder->MoveTo(ToPoint(points[0]));
     537               0 :     for (PRUint32 i = 1; i < numPoints; i++) {
     538               0 :       mPathBuilder->LineTo(ToPoint(points[i]));
     539                 :     }
     540                 :   }
     541                 : }
     542                 : 
     543                 : void
     544               0 : gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size)
     545                 : {
     546               0 :   if (mCairo) {
     547               0 :     cairo_save(mCairo);
     548               0 :     cairo_set_source_surface(mCairo, surface->CairoSurface(), 0, 0);
     549               0 :     cairo_new_path(mCairo);
     550                 : 
     551                 :     // pixel-snap this
     552               0 :     Rectangle(gfxRect(gfxPoint(0.0, 0.0), size), true);
     553                 : 
     554               0 :     cairo_fill(mCairo);
     555               0 :     cairo_restore(mCairo);
     556                 :   } else {
     557                 :     RefPtr<SourceSurface> surf =
     558               0 :       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
     559                 : 
     560               0 :     Rect rect(0, 0, Float(size.width), Float(size.height));
     561               0 :     rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
     562                 : 
     563                 :     // XXX - Should fix pixel snapping.
     564               0 :     mDT->DrawSurface(surf, rect, rect);
     565                 :   }
     566               0 : }
     567                 : 
     568                 : // transform stuff
     569                 : void
     570              19 : gfxContext::Translate(const gfxPoint& pt)
     571                 : {
     572              19 :   if (mCairo) {
     573              19 :     cairo_translate(mCairo, pt.x, pt.y);
     574                 :   } else {
     575               0 :     MOZ_ASSERT(!mPathBuilder);
     576                 : 
     577               0 :     Matrix newMatrix = mDT->GetTransform();
     578               0 :     mDT->SetTransform(newMatrix.Translate(Float(pt.x), Float(pt.y)));
     579                 :   }
     580              19 : }
     581                 : 
     582                 : void
     583              12 : gfxContext::Scale(gfxFloat x, gfxFloat y)
     584                 : {
     585              12 :   if (mCairo) {
     586              12 :     cairo_scale(mCairo, x, y);
     587                 :   } else {
     588               0 :     MOZ_ASSERT(!mPathBuilder);
     589                 : 
     590               0 :     Matrix newMatrix = mDT->GetTransform();
     591               0 :     mDT->SetTransform(newMatrix.Scale(Float(x), Float(y)));
     592                 :   }
     593              12 : }
     594                 : 
     595                 : void
     596               0 : gfxContext::Rotate(gfxFloat angle)
     597                 : {
     598               0 :   if (mCairo) {
     599               0 :     cairo_rotate(mCairo, angle);
     600                 :   } else {
     601               0 :     MOZ_ASSERT(!mPathBuilder);
     602                 : 
     603               0 :     Matrix rotation = Matrix::Rotation(Float(angle));
     604               0 :     mDT->SetTransform(rotation * mDT->GetTransform());
     605                 :   }
     606               0 : }
     607                 : 
     608                 : void
     609               0 : gfxContext::Multiply(const gfxMatrix& matrix)
     610                 : {
     611               0 :   if (mCairo) {
     612               0 :     const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
     613               0 :     cairo_transform(mCairo, &mat);
     614                 :   } else {
     615               0 :     MOZ_ASSERT(!mPathBuilder);
     616                 : 
     617               0 :     mDT->SetTransform(ToMatrix(matrix) * mDT->GetTransform());
     618                 :   }
     619               0 : }
     620                 : 
     621                 : void
     622               0 : gfxContext::SetMatrix(const gfxMatrix& matrix)
     623                 : {
     624               0 :   if (mCairo) {
     625               0 :     const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
     626               0 :     cairo_set_matrix(mCairo, &mat);
     627                 :   } else {
     628               0 :     MOZ_ASSERT(!mPathBuilder);
     629                 : 
     630               0 :     mDT->SetTransform(ToMatrix(matrix));
     631                 :   }
     632               0 : }
     633                 : 
     634                 : void
     635               0 : gfxContext::IdentityMatrix()
     636                 : {
     637               0 :   if (mCairo) {
     638               0 :     cairo_identity_matrix(mCairo);
     639                 :   } else {
     640               0 :     MOZ_ASSERT(!mPathBuilder);
     641                 : 
     642               0 :     mDT->SetTransform(Matrix());
     643                 :   }
     644               0 : }
     645                 : 
     646                 : gfxMatrix
     647               0 : gfxContext::CurrentMatrix() const
     648                 : {
     649               0 :   if (mCairo) {
     650                 :     cairo_matrix_t mat;
     651               0 :     cairo_get_matrix(mCairo, &mat);
     652               0 :     return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat));
     653                 :   } else {
     654               0 :     return ThebesMatrix(mDT->GetTransform());
     655                 :   }
     656                 : }
     657                 : 
     658                 : void
     659               0 : gfxContext::NudgeCurrentMatrixToIntegers()
     660                 : {
     661               0 :   if (mCairo) {
     662                 :     cairo_matrix_t mat;
     663               0 :     cairo_get_matrix(mCairo, &mat);
     664               0 :     gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)).NudgeToIntegers();
     665               0 :     cairo_set_matrix(mCairo, &mat);
     666                 :   } else {
     667               0 :     gfxMatrix matrix = ThebesMatrix(mTransform);
     668               0 :     matrix.NudgeToIntegers();
     669               0 :     mTransform = ToMatrix(matrix);
     670                 :   }
     671               0 : }
     672                 : 
     673                 : gfxPoint
     674               0 : gfxContext::DeviceToUser(const gfxPoint& point) const
     675                 : {
     676               0 :   if (mCairo) {
     677               0 :     gfxPoint ret = point;
     678               0 :     cairo_device_to_user(mCairo, &ret.x, &ret.y);
     679               0 :     return ret;
     680                 :   } else {
     681               0 :     Matrix matrix = mDT->GetTransform();
     682                 : 
     683               0 :     matrix.Invert();
     684                 : 
     685               0 :     return ThebesPoint(matrix * ToPoint(point));
     686                 :   }
     687                 : }
     688                 : 
     689                 : gfxSize
     690               0 : gfxContext::DeviceToUser(const gfxSize& size) const
     691                 : {
     692               0 :   if (mCairo) {
     693               0 :     gfxSize ret = size;
     694               0 :     cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
     695               0 :     return ret;
     696                 :   } else {
     697               0 :     Matrix matrix = mDT->GetTransform();
     698                 : 
     699               0 :     matrix.Invert();
     700                 : 
     701               0 :     return ThebesSize(matrix * ToSize(size));
     702                 :   }
     703                 : }
     704                 : 
     705                 : gfxRect
     706               0 : gfxContext::DeviceToUser(const gfxRect& rect) const
     707                 : {
     708               0 :   if (mCairo) {
     709               0 :     gfxRect ret = rect;
     710               0 :     cairo_device_to_user(mCairo, &ret.x, &ret.y);
     711               0 :     cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
     712               0 :     return ret;
     713                 :   } else {
     714               0 :     Matrix matrix = mDT->GetTransform();
     715                 : 
     716               0 :     matrix.Invert();
     717                 : 
     718               0 :     return ThebesRect(matrix.TransformBounds(ToRect(rect)));
     719                 :   }
     720                 : }
     721                 : 
     722                 : gfxPoint
     723               0 : gfxContext::UserToDevice(const gfxPoint& point) const
     724                 : {
     725               0 :   if (mCairo) {
     726               0 :     gfxPoint ret = point;
     727               0 :     cairo_user_to_device(mCairo, &ret.x, &ret.y);
     728               0 :     return ret;
     729                 :   } else {
     730               0 :     return ThebesPoint(mDT->GetTransform() * ToPoint(point));
     731                 :   }
     732                 : }
     733                 : 
     734                 : gfxSize
     735               0 : gfxContext::UserToDevice(const gfxSize& size) const
     736                 : {
     737               0 :   if (mCairo) {
     738               0 :     gfxSize ret = size;
     739               0 :     cairo_user_to_device_distance(mCairo, &ret.width, &ret.height);
     740               0 :     return ret;
     741                 :   } else {
     742               0 :     const Matrix &matrix = mDT->GetTransform();
     743                 : 
     744               0 :     gfxSize newSize = size;
     745               0 :     newSize.width = newSize.width * matrix._11 + newSize.height * matrix._12;
     746               0 :     newSize.height = newSize.width * matrix._21 + newSize.height * matrix._22;
     747               0 :     return newSize;
     748                 :   }
     749                 : }
     750                 : 
     751                 : gfxRect
     752               0 : gfxContext::UserToDevice(const gfxRect& rect) const
     753                 : {
     754               0 :   if (mCairo) {
     755               0 :     double xmin = rect.X(), ymin = rect.Y(), xmax = rect.XMost(), ymax = rect.YMost();
     756                 : 
     757                 :     double x[3], y[3];
     758               0 :     x[0] = xmin;  y[0] = ymax;
     759               0 :     x[1] = xmax;  y[1] = ymax;
     760               0 :     x[2] = xmax;  y[2] = ymin;
     761                 : 
     762               0 :     cairo_user_to_device(mCairo, &xmin, &ymin);
     763               0 :     xmax = xmin;
     764               0 :     ymax = ymin;
     765               0 :     for (int i = 0; i < 3; i++) {
     766               0 :         cairo_user_to_device(mCairo, &x[i], &y[i]);
     767               0 :         xmin = NS_MIN(xmin, x[i]);
     768               0 :         xmax = NS_MAX(xmax, x[i]);
     769               0 :         ymin = NS_MIN(ymin, y[i]);
     770               0 :         ymax = NS_MAX(ymax, y[i]);
     771                 :     }
     772                 : 
     773               0 :     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
     774                 :   } else {
     775               0 :     const Matrix &matrix = mDT->GetTransform();
     776               0 :     return ThebesRect(matrix.TransformBounds(ToRect(rect)));
     777                 :   }
     778                 : }
     779                 : 
     780                 : bool
     781               0 : gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
     782                 : {
     783               0 :   if (GetFlags() & FLAG_DISABLE_SNAPPING)
     784               0 :       return false;
     785                 : 
     786                 :   // if we're not at 1.0 scale, don't snap, unless we're
     787                 :   // ignoring the scale.  If we're not -just- a scale,
     788                 :   // never snap.
     789               0 :   const gfxFloat epsilon = 0.0000001;
     790                 : #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
     791               0 :   if (mCairo) {
     792                 :     cairo_matrix_t mat;
     793               0 :     cairo_get_matrix(mCairo, &mat);
     794               0 :     if (!ignoreScale &&
     795               0 :         (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
     796               0 :           !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
     797               0 :         return false;
     798                 :   } else {
     799               0 :     Matrix mat = mDT->GetTransform();
     800               0 :     if (!ignoreScale &&
     801               0 :         (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
     802               0 :           !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
     803               0 :         return false;
     804                 :   }
     805                 : #undef WITHIN_E
     806                 : 
     807               0 :   gfxPoint p1 = UserToDevice(rect.TopLeft());
     808               0 :   gfxPoint p2 = UserToDevice(rect.TopRight());
     809               0 :   gfxPoint p3 = UserToDevice(rect.BottomRight());
     810                 : 
     811                 :   // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
     812                 :   // two opposite corners define the entire rectangle. So check if
     813                 :   // the axis-aligned rectangle with opposite corners p1 and p3
     814                 :   // define an axis-aligned rectangle whose other corners are p2 and p4.
     815                 :   // We actually only need to check one of p2 and p4, since an affine
     816                 :   // transform maps parallelograms to parallelograms.
     817               0 :   if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
     818               0 :       p1.Round();
     819               0 :       p3.Round();
     820                 : 
     821               0 :       rect.MoveTo(gfxPoint(NS_MIN(p1.x, p3.x), NS_MIN(p1.y, p3.y)));
     822               0 :       rect.SizeTo(gfxSize(NS_MAX(p1.x, p3.x) - rect.X(),
     823               0 :                           NS_MAX(p1.y, p3.y) - rect.Y()));
     824               0 :       return true;
     825                 :   }
     826                 : 
     827               0 :   return false;
     828                 : }
     829                 : 
     830                 : bool
     831               0 : gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
     832                 : {
     833               0 :   if (GetFlags() & FLAG_DISABLE_SNAPPING)
     834               0 :       return false;
     835                 : 
     836                 :   // if we're not at 1.0 scale, don't snap, unless we're
     837                 :   // ignoring the scale.  If we're not -just- a scale,
     838                 :   // never snap.
     839               0 :   const gfxFloat epsilon = 0.0000001;
     840                 : #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
     841               0 :   if (mCairo) {
     842                 :     cairo_matrix_t mat;
     843               0 :     cairo_get_matrix(mCairo, &mat);
     844               0 :     if (!ignoreScale &&
     845               0 :         (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
     846               0 :           !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
     847               0 :         return false;
     848                 :   } else {
     849               0 :     Matrix mat = mDT->GetTransform();
     850               0 :     if (!ignoreScale &&
     851               0 :         (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
     852               0 :           !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
     853               0 :         return false;
     854                 :   }
     855                 : #undef WITHIN_E
     856                 : 
     857               0 :   pt = UserToDevice(pt);
     858               0 :   pt.Round();
     859               0 :   return true;
     860                 : }
     861                 : 
     862                 : void
     863               0 : gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect,
     864                 :                                                gfxPattern *pattern)
     865                 : {
     866               0 :   gfxRect r(rect);
     867                 : 
     868                 :   // Bob attempts to pixel-snap the rectangle, and returns true if
     869                 :   // the snapping succeeds.  If it does, we need to set up an
     870                 :   // identity matrix, because the rectangle given back is in device
     871                 :   // coordinates.
     872                 :   //
     873                 :   // We then have to call a translate to dr.pos afterwards, to make
     874                 :   // sure the image lines up in the right place with our pixel
     875                 :   // snapped rectangle.
     876                 :   //
     877                 :   // If snapping wasn't successful, we just translate to where the
     878                 :   // pattern would normally start (in app coordinates) and do the
     879                 :   // same thing.
     880               0 :   Rectangle(r, true);
     881               0 :   SetPattern(pattern);
     882               0 : }
     883                 : 
     884                 : void
     885               0 : gfxContext::SetAntialiasMode(AntialiasMode mode)
     886                 : {
     887               0 :   if (mCairo) {
     888               0 :     if (mode == MODE_ALIASED) {
     889               0 :         cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_NONE);
     890               0 :     } else if (mode == MODE_COVERAGE) {
     891               0 :         cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_DEFAULT);
     892                 :     }
     893                 :   } else {
     894               0 :     if (mode == MODE_ALIASED) {
     895               0 :       CurrentState().aaMode = AA_NONE;
     896               0 :     } else if (mode == MODE_COVERAGE) {
     897               0 :       CurrentState().aaMode = AA_SUBPIXEL;
     898                 :     }
     899                 :   }
     900               0 : }
     901                 : 
     902                 : gfxContext::AntialiasMode
     903               0 : gfxContext::CurrentAntialiasMode() const
     904                 : {
     905               0 :   if (mCairo) {
     906               0 :     cairo_antialias_t aa = cairo_get_antialias(mCairo);
     907               0 :     if (aa == CAIRO_ANTIALIAS_NONE)
     908               0 :         return MODE_ALIASED;
     909               0 :     return MODE_COVERAGE;
     910                 :   } else {
     911               0 :     if (CurrentState().aaMode == AA_NONE) {
     912               0 :       return MODE_ALIASED;
     913                 :     }
     914               0 :     return MODE_COVERAGE;
     915                 :   }
     916                 : }
     917                 : 
     918                 : void
     919               0 : gfxContext::SetDash(gfxLineType ltype)
     920                 : {
     921                 :   static double dash[] = {5.0, 5.0};
     922                 :   static double dot[] = {1.0, 1.0};
     923                 : 
     924               0 :   switch (ltype) {
     925                 :       case gfxLineDashed:
     926               0 :           SetDash(dash, 2, 0.0);
     927               0 :           break;
     928                 :       case gfxLineDotted:
     929               0 :           SetDash(dot, 2, 0.0);
     930               0 :           break;
     931                 :       case gfxLineSolid:
     932                 :       default:
     933               0 :           SetDash(nsnull, 0, 0.0);
     934               0 :           break;
     935                 :   }
     936               0 : }
     937                 : 
     938                 : void
     939               0 : gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
     940                 : {
     941               0 :   if (mCairo) {
     942               0 :     cairo_set_dash(mCairo, dashes, ndash, offset);
     943                 :   } else {
     944               0 :     AzureState &state = CurrentState();
     945                 : 
     946               0 :     state.dashPattern.SetLength(ndash);
     947               0 :     for (int i = 0; i < ndash; i++) {
     948               0 :       state.dashPattern[i] = Float(dashes[i]);
     949                 :     }
     950               0 :     state.strokeOptions.mDashLength = ndash;
     951               0 :     state.strokeOptions.mDashOffset = Float(offset);
     952               0 :     state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements() : NULL;
     953                 :   }
     954               0 : }
     955                 : 
     956                 : bool
     957               0 : gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
     958                 : {
     959               0 :   if (mCairo) {
     960               0 :     int count = cairo_get_dash_count(mCairo);
     961               0 :     if (count <= 0 || !dashes.SetLength(count)) {
     962               0 :         return false;
     963                 :     }
     964               0 :     cairo_get_dash(mCairo, dashes.Elements(), offset);
     965               0 :     return true;
     966                 :   } else {
     967               0 :     const AzureState &state = CurrentState();
     968               0 :     int count = state.strokeOptions.mDashLength;
     969                 : 
     970               0 :     if (count <= 0 || !dashes.SetLength(count)) {
     971               0 :       return false;
     972                 :     }
     973                 : 
     974               0 :     for (int i = 0; i < count; i++) {
     975               0 :       dashes[i] = state.dashPattern[i];
     976                 :     }
     977                 : 
     978               0 :     *offset = state.strokeOptions.mDashOffset;
     979                 : 
     980               0 :     return true;
     981                 :   }
     982                 : }
     983                 : 
     984                 : gfxFloat
     985               0 : gfxContext::CurrentDashOffset() const
     986                 : {
     987               0 :   if (mCairo) {
     988               0 :     if (cairo_get_dash_count(mCairo) <= 0) {
     989               0 :         return 0.0;
     990                 :     }
     991                 :     gfxFloat offset;
     992               0 :     cairo_get_dash(mCairo, NULL, &offset);
     993               0 :     return offset;
     994                 :   } else {
     995               0 :     return CurrentState().strokeOptions.mDashOffset;
     996                 :   }
     997                 : }
     998                 : 
     999                 : void
    1000               0 : gfxContext::SetLineWidth(gfxFloat width)
    1001                 : {
    1002               0 :   if (mCairo) {
    1003               0 :     cairo_set_line_width(mCairo, width);
    1004                 :   } else {
    1005               0 :     CurrentState().strokeOptions.mLineWidth = Float(width);
    1006                 :   }
    1007               0 : }
    1008                 : 
    1009                 : gfxFloat
    1010               0 : gfxContext::CurrentLineWidth() const
    1011                 : {
    1012               0 :   if (mCairo) {
    1013               0 :     return cairo_get_line_width(mCairo);
    1014                 :   } else {
    1015               0 :     return CurrentState().strokeOptions.mLineWidth;
    1016                 :   }
    1017                 : }
    1018                 : 
    1019                 : void
    1020              64 : gfxContext::SetOperator(GraphicsOperator op)
    1021                 : {
    1022              64 :   if (mCairo) {
    1023              64 :     if (mFlags & FLAG_SIMPLIFY_OPERATORS) {
    1024               0 :         if (op != OPERATOR_SOURCE &&
    1025                 :             op != OPERATOR_CLEAR &&
    1026                 :             op != OPERATOR_OVER)
    1027               0 :             op = OPERATOR_OVER;
    1028                 :     }
    1029                 : 
    1030              64 :     cairo_set_operator(mCairo, (cairo_operator_t)op);
    1031                 :   } else {
    1032               0 :     if (op == OPERATOR_CLEAR) {
    1033               0 :       CurrentState().opIsClear = true;
    1034               0 :       return;
    1035                 :     }
    1036               0 :     CurrentState().opIsClear = false;
    1037               0 :     CurrentState().op = CompositionOpForOp(op);
    1038                 :   }
    1039                 : }
    1040                 : 
    1041                 : gfxContext::GraphicsOperator
    1042               0 : gfxContext::CurrentOperator() const
    1043                 : {
    1044               0 :   if (mCairo) {
    1045               0 :     return (GraphicsOperator)cairo_get_operator(mCairo);
    1046                 :   } else {
    1047               0 :     return ThebesOp(CurrentState().op);
    1048                 :   }
    1049                 : }
    1050                 : 
    1051                 : void
    1052               0 : gfxContext::SetLineCap(GraphicsLineCap cap)
    1053                 : {
    1054               0 :   if (mCairo) {
    1055               0 :     cairo_set_line_cap(mCairo, (cairo_line_cap_t)cap);
    1056                 :   } else {
    1057               0 :     CurrentState().strokeOptions.mLineCap = ToCapStyle(cap);
    1058                 :   }
    1059               0 : }
    1060                 : 
    1061                 : gfxContext::GraphicsLineCap
    1062               0 : gfxContext::CurrentLineCap() const
    1063                 : {
    1064               0 :   if (mCairo) {
    1065               0 :     return (GraphicsLineCap)cairo_get_line_cap(mCairo);
    1066                 :   } else {
    1067               0 :     return ThebesLineCap(CurrentState().strokeOptions.mLineCap);
    1068                 :   }
    1069                 : }
    1070                 : 
    1071                 : void
    1072               0 : gfxContext::SetLineJoin(GraphicsLineJoin join)
    1073                 : {
    1074               0 :   if (mCairo) {
    1075               0 :     cairo_set_line_join(mCairo, (cairo_line_join_t)join);
    1076                 :   } else {
    1077               0 :     CurrentState().strokeOptions.mLineJoin = ToJoinStyle(join);
    1078                 :   }
    1079               0 : }
    1080                 : 
    1081                 : gfxContext::GraphicsLineJoin
    1082               0 : gfxContext::CurrentLineJoin() const
    1083                 : {
    1084               0 :   if (mCairo) {
    1085               0 :     return (GraphicsLineJoin)cairo_get_line_join(mCairo);
    1086                 :   } else {
    1087               0 :     return ThebesLineJoin(CurrentState().strokeOptions.mLineJoin);
    1088                 :   }
    1089                 : }
    1090                 : 
    1091                 : void
    1092               0 : gfxContext::SetMiterLimit(gfxFloat limit)
    1093                 : {
    1094               0 :   if (mCairo) {
    1095               0 :     cairo_set_miter_limit(mCairo, limit);
    1096                 :   } else {
    1097               0 :     CurrentState().strokeOptions.mMiterLimit = Float(limit);
    1098                 :   }
    1099               0 : }
    1100                 : 
    1101                 : gfxFloat
    1102               0 : gfxContext::CurrentMiterLimit() const
    1103                 : {
    1104               0 :   if (mCairo) {
    1105               0 :     return cairo_get_miter_limit(mCairo);
    1106                 :   } else {
    1107               0 :     return CurrentState().strokeOptions.mMiterLimit;
    1108                 :   }
    1109                 : }
    1110                 : 
    1111                 : void
    1112               0 : gfxContext::SetFillRule(FillRule rule)
    1113                 : {
    1114               0 :   if (mCairo) {
    1115               0 :     cairo_set_fill_rule(mCairo, (cairo_fill_rule_t)rule);
    1116                 :   } else {
    1117               0 :     CurrentState().fillRule = rule == FILL_RULE_WINDING ? FILL_WINDING : FILL_EVEN_ODD;
    1118                 :   }
    1119               0 : }
    1120                 : 
    1121                 : gfxContext::FillRule
    1122               0 : gfxContext::CurrentFillRule() const
    1123                 : {
    1124               0 :   if (mCairo) {
    1125               0 :     return (FillRule)cairo_get_fill_rule(mCairo);
    1126                 :   } else {
    1127               0 :     return FILL_RULE_WINDING;
    1128                 :   }
    1129                 : }
    1130                 : 
    1131                 : // clipping
    1132                 : void
    1133               0 : gfxContext::Clip(const gfxRect& rect)
    1134                 : {
    1135               0 :   if (mCairo) {
    1136               0 :     cairo_new_path(mCairo);
    1137               0 :     cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
    1138               0 :     cairo_clip(mCairo);
    1139                 :   } else {
    1140               0 :     AzureState::PushedClip clip = { NULL, ToRect(rect), mDT->GetTransform() };
    1141               0 :     CurrentState().pushedClips.AppendElement(clip);
    1142               0 :     mDT->PushClipRect(ToRect(rect));
    1143               0 :     NewPath();
    1144                 :   }
    1145               0 : }
    1146                 : 
    1147                 : void
    1148               0 : gfxContext::Clip()
    1149                 : {
    1150               0 :   if (mCairo) {
    1151               0 :     cairo_clip_preserve(mCairo);
    1152                 :   } else {
    1153               0 :     if (mPathIsRect && !mTransformChanged) {
    1154               0 :       AzureState::PushedClip clip = { NULL, mRect, mDT->GetTransform() };
    1155               0 :       CurrentState().pushedClips.AppendElement(clip);
    1156               0 :       mDT->PushClipRect(mRect);
    1157                 :     } else {
    1158               0 :       EnsurePath();
    1159               0 :       mDT->PushClip(mPath);
    1160               0 :       AzureState::PushedClip clip = { mPath, Rect(), mDT->GetTransform() };
    1161               0 :       CurrentState().pushedClips.AppendElement(clip);
    1162                 :     }
    1163                 :   }
    1164               0 : }
    1165                 : 
    1166                 : void
    1167               0 : gfxContext::ResetClip()
    1168                 : {
    1169               0 :   if (mCairo) {
    1170               0 :     cairo_reset_clip(mCairo);
    1171                 :   } else {
    1172               0 :     for (int i = mStateStack.Length() - 1; i >= 0; i--) {
    1173               0 :       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
    1174               0 :         mDT->PopClip();
    1175                 :       }
    1176                 : 
    1177               0 :       if (mStateStack[i].clipWasReset) {
    1178               0 :         break;
    1179                 :       }
    1180                 :     }
    1181               0 :     CurrentState().pushedClips.Clear();
    1182               0 :     CurrentState().clipWasReset = true;
    1183                 :   }
    1184               0 : }
    1185                 : 
    1186                 : void
    1187               0 : gfxContext::UpdateSurfaceClip()
    1188                 : {
    1189               0 :   if (mCairo) {
    1190               0 :     NewPath();
    1191                 :     // we paint an empty rectangle to ensure the clip is propagated to
    1192                 :     // the destination surface
    1193               0 :     SetDeviceColor(gfxRGBA(0,0,0,0));
    1194               0 :     Rectangle(gfxRect(0,1,1,0));
    1195               0 :     Fill();
    1196                 :   }
    1197               0 : }
    1198                 : 
    1199                 : gfxRect
    1200               0 : gfxContext::GetClipExtents()
    1201                 : {
    1202               0 :   if (mCairo) {
    1203                 :     double xmin, ymin, xmax, ymax;
    1204               0 :     cairo_clip_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
    1205               0 :     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
    1206                 :   } else {
    1207               0 :     unsigned int lastReset = 0;
    1208               0 :     for (int i = mStateStack.Length() - 1; i > 0; i--) {
    1209               0 :       if (mStateStack[i].clipWasReset) {
    1210               0 :         lastReset = i;
    1211                 :       }
    1212                 :     }
    1213                 : 
    1214               0 :     Rect rect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
    1215               0 :     for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
    1216               0 :       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
    1217               0 :         AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
    1218               0 :         if (clip.path) {
    1219               0 :           Rect bounds = clip.path->GetBounds(clip.transform);
    1220               0 :           rect.IntersectRect(rect, bounds);
    1221                 :         } else {
    1222               0 :           rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
    1223                 :         }
    1224                 :       }
    1225                 :     }
    1226                 : 
    1227               0 :     if (rect.width == 0 || rect.height == 0) {
    1228               0 :       return gfxRect(0, 0, 0, 0);
    1229                 :     }
    1230                 : 
    1231               0 :     Matrix mat = mDT->GetTransform();
    1232               0 :     mat.Invert();
    1233               0 :     rect = mat.TransformBounds(rect);
    1234                 : 
    1235               0 :     return ThebesRect(rect);
    1236                 :   }
    1237                 : }
    1238                 : 
    1239                 : bool
    1240               0 : gfxContext::ClipContainsRect(const gfxRect& aRect)
    1241                 : {
    1242               0 :   if (mCairo) {
    1243                 :     cairo_rectangle_list_t *clip =
    1244               0 :         cairo_copy_clip_rectangle_list(mCairo);
    1245                 : 
    1246               0 :     bool result = false;
    1247                 : 
    1248               0 :     if (clip->status == CAIRO_STATUS_SUCCESS) {
    1249               0 :         for (int i = 0; i < clip->num_rectangles; i++) {
    1250               0 :             gfxRect rect(clip->rectangles[i].x, clip->rectangles[i].y,
    1251               0 :                          clip->rectangles[i].width, clip->rectangles[i].height);
    1252               0 :             if (rect.Contains(aRect)) {
    1253               0 :                 result = true;
    1254               0 :                 break;
    1255                 :             }
    1256                 :         }
    1257                 :     }
    1258                 : 
    1259               0 :     cairo_rectangle_list_destroy(clip);
    1260               0 :     return result;
    1261                 :   } else {
    1262               0 :     unsigned int lastReset = 0;
    1263               0 :     for (int i = mStateStack.Length() - 2; i > 0; i--) {
    1264               0 :       if (mStateStack[i].clipWasReset) {
    1265               0 :         lastReset = i;
    1266                 :       }
    1267                 :     }
    1268                 : 
    1269                 :     // Since we always return false when the clip list contains a
    1270                 :     // non-rectangular clip or a non-rectilinear transform, our 'total' clip
    1271                 :     // is always a rectangle if we hit the end of this function.
    1272               0 :     Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
    1273                 : 
    1274               0 :     for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
    1275               0 :       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
    1276               0 :         AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
    1277               0 :         if (clip.path || !clip.transform.IsRectilinear()) {
    1278                 :           // Cairo behavior is we return false if the clip contains a non-
    1279                 :           // rectangle.
    1280               0 :           return false;
    1281                 :         } else {
    1282               0 :           Rect clipRect = mTransform.TransformBounds(clip.rect);
    1283                 : 
    1284               0 :           clipBounds.IntersectRect(clipBounds, clipRect);
    1285                 :         }
    1286                 :       }
    1287                 :     }
    1288                 : 
    1289               0 :     return clipBounds.Contains(ToRect(aRect));
    1290                 :   }
    1291                 : }
    1292                 : 
    1293                 : // rendering sources
    1294                 : 
    1295                 : void
    1296               0 : gfxContext::SetColor(const gfxRGBA& c)
    1297                 : {
    1298               0 :   if (mCairo) {
    1299               0 :     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
    1300                 : 
    1301               0 :         gfxRGBA cms;
    1302               0 :         gfxPlatform::TransformPixel(c, cms, gfxPlatform::GetCMSRGBTransform());
    1303                 : 
    1304                 :         // Use the original alpha to avoid unnecessary float->byte->float
    1305                 :         // conversion errors
    1306               0 :         cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, c.a);
    1307                 :     }
    1308                 :     else
    1309               0 :         cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
    1310                 :   } else {
    1311               0 :     CurrentState().pattern = NULL;
    1312               0 :     CurrentState().sourceSurface = NULL;
    1313                 : 
    1314               0 :     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
    1315                 : 
    1316               0 :         gfxRGBA cms;
    1317               0 :         gfxPlatform::TransformPixel(c, cms, gfxPlatform::GetCMSRGBTransform());
    1318                 : 
    1319                 :         // Use the original alpha to avoid unnecessary float->byte->float
    1320                 :         // conversion errors
    1321               0 :         CurrentState().color = ToColor(cms);
    1322                 :     }
    1323                 :     else
    1324               0 :         CurrentState().color = ToColor(c);
    1325                 :   }
    1326               0 : }
    1327                 : 
    1328                 : void
    1329               0 : gfxContext::SetDeviceColor(const gfxRGBA& c)
    1330                 : {
    1331               0 :   if (mCairo) {
    1332               0 :     cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
    1333                 :   } else {
    1334               0 :     CurrentState().pattern = NULL;
    1335               0 :     CurrentState().sourceSurface = NULL;
    1336               0 :     CurrentState().color = ToColor(c);
    1337                 :   }
    1338               0 : }
    1339                 : 
    1340                 : bool
    1341               0 : gfxContext::GetDeviceColor(gfxRGBA& c)
    1342                 : {
    1343               0 :   if (mCairo) {
    1344                 :     return cairo_pattern_get_rgba(cairo_get_source(mCairo),
    1345                 :                                   &c.r,
    1346                 :                                   &c.g,
    1347                 :                                   &c.b,
    1348               0 :                                   &c.a) == CAIRO_STATUS_SUCCESS;
    1349                 :   } else {
    1350               0 :     if (CurrentState().sourceSurface) {
    1351               0 :       return false;
    1352                 :     }
    1353               0 :     if (CurrentState().pattern) {
    1354               0 :       gfxRGBA color;
    1355               0 :       return CurrentState().pattern->GetSolidColor(c);
    1356                 :     }
    1357                 : 
    1358               0 :     c = ThebesRGBA(CurrentState().color);
    1359               0 :     return true;
    1360                 :   }
    1361                 : }
    1362                 : 
    1363                 : void
    1364              28 : gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
    1365                 : {
    1366              28 :   if (mCairo) {
    1367              28 :     NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!");
    1368              28 :     cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
    1369                 :   } else {
    1370               0 :     CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
    1371               0 :     CurrentState().pattern = NULL;
    1372               0 :     CurrentState().sourceSurface =
    1373               0 :       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
    1374                 :   }
    1375              28 : }
    1376                 : 
    1377                 : void
    1378              19 : gfxContext::SetPattern(gfxPattern *pattern)
    1379                 : {
    1380              19 :   if (mCairo) {
    1381              19 :     cairo_set_source(mCairo, pattern->CairoPattern());
    1382                 :   } else {
    1383               0 :     CurrentState().sourceSurface = NULL;
    1384               0 :     CurrentState().pattern = pattern;
    1385                 :   }
    1386              19 : }
    1387                 : 
    1388                 : already_AddRefed<gfxPattern>
    1389               0 : gfxContext::GetPattern()
    1390                 : {
    1391               0 :   if (mCairo) {
    1392               0 :     cairo_pattern_t *pat = cairo_get_source(mCairo);
    1393               0 :     NS_ASSERTION(pat, "I was told this couldn't be null");
    1394                 : 
    1395               0 :     gfxPattern *wrapper = nsnull;
    1396               0 :     if (pat)
    1397               0 :         wrapper = new gfxPattern(pat);
    1398                 :     else
    1399               0 :         wrapper = new gfxPattern(gfxRGBA(0,0,0,0));
    1400                 : 
    1401               0 :     NS_IF_ADDREF(wrapper);
    1402               0 :     return wrapper;
    1403                 :   } else {
    1404               0 :     nsRefPtr<gfxPattern> pat;
    1405                 :     
    1406               0 :     AzureState &state = CurrentState();
    1407               0 :     if (state.pattern) {
    1408               0 :       pat = state.pattern;
    1409               0 :     } else if (state.sourceSurface) {
    1410               0 :       NS_ASSERTION(false, "Ugh, this isn't good.");
    1411                 :     } else {
    1412               0 :       pat = new gfxPattern(ThebesRGBA(state.color));
    1413                 :     }
    1414               0 :     return pat.forget();
    1415                 :   }
    1416                 : }
    1417                 : 
    1418                 : 
    1419                 : // masking
    1420                 : 
    1421                 : void
    1422               0 : gfxContext::Mask(gfxPattern *pattern)
    1423                 : {
    1424               0 :   if (mCairo) {
    1425               0 :     cairo_mask(mCairo, pattern->CairoPattern());
    1426                 :   } else {
    1427               0 :     mDT->Mask(GeneralPattern(this), *pattern->GetPattern(mDT), DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
    1428                 :   }
    1429               0 : }
    1430                 : 
    1431                 : void
    1432               0 : gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset)
    1433                 : {
    1434               0 :   if (mCairo) {
    1435               0 :     cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
    1436                 :   } else {
    1437                 :     RefPtr<SourceSurface> sourceSurf =
    1438               0 :       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
    1439                 : 
    1440               0 :     gfxPoint pt = surface->GetDeviceOffset();
    1441               0 :     mDT->Mask(GeneralPattern(this), 
    1442                 :               SurfacePattern(sourceSurf, EXTEND_CLAMP,
    1443               0 :                              Matrix(1.0f, 0, 0, 1.0f, Float(offset.x - pt.x), Float(offset.y - pt.y))),
    1444               0 :                              DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
    1445                 :   }
    1446               0 : }
    1447                 : 
    1448                 : void
    1449              43 : gfxContext::Paint(gfxFloat alpha)
    1450                 : {
    1451              43 :   if (mCairo) {
    1452              43 :     cairo_paint_with_alpha(mCairo, alpha);
    1453                 :   } else {
    1454               0 :     AzureState &state = CurrentState();
    1455                 : 
    1456               0 :     Matrix mat = mDT->GetTransform();
    1457               0 :     mat.Invert();
    1458               0 :     Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
    1459                 : 
    1460               0 :     if (state.opIsClear) {
    1461               0 :       mDT->ClearRect(paintRect);
    1462                 :     } else {
    1463               0 :       mDT->FillRect(paintRect, GeneralPattern(this),
    1464               0 :                     DrawOptions(Float(alpha), GetOp()));
    1465                 :     }
    1466                 :   }
    1467              43 : }
    1468                 : 
    1469                 : // groups
    1470                 : 
    1471                 : void
    1472               0 : gfxContext::PushGroup(gfxASurface::gfxContentType content)
    1473                 : {
    1474               0 :   if (mCairo) {
    1475               0 :     cairo_push_group_with_content(mCairo, (cairo_content_t) content);
    1476                 :   } else {
    1477                 :     RefPtr<DrawTarget> newDT =
    1478               0 :       mDT->CreateSimilarDrawTarget(mDT->GetSize(), FormatForContent(content));
    1479                 : 
    1480               0 :     Save();
    1481                 : 
    1482               0 :     CurrentState().drawTarget = newDT;
    1483                 : 
    1484               0 :     PushClipsToDT(newDT);
    1485               0 :     newDT->SetTransform(mDT->GetTransform());
    1486               0 :     mDT = newDT;
    1487                 :   }
    1488               0 : }
    1489                 : 
    1490                 : static gfxRect
    1491               0 : GetRoundOutDeviceClipExtents(gfxContext* aCtx)
    1492                 : {
    1493               0 :   gfxContextMatrixAutoSaveRestore save(aCtx);
    1494               0 :   aCtx->IdentityMatrix();
    1495               0 :   gfxRect r = aCtx->GetClipExtents();
    1496               0 :   r.RoundOut();
    1497                 :   return r;
    1498                 : }
    1499                 : 
    1500                 : /**
    1501                 :  * Copy the contents of aSrc to aDest, translated by aTranslation.
    1502                 :  */
    1503                 : static void
    1504               0 : CopySurface(gfxASurface* aSrc, gfxASurface* aDest, const gfxPoint& aTranslation)
    1505                 : {
    1506               0 :   cairo_t *cr = cairo_create(aDest->CairoSurface());
    1507               0 :   cairo_set_source_surface(cr, aSrc->CairoSurface(), aTranslation.x, aTranslation.y);
    1508               0 :   cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
    1509               0 :   cairo_paint(cr);
    1510               0 :   cairo_destroy(cr);
    1511               0 : }
    1512                 : 
    1513                 : void
    1514               0 : gfxContext::PushGroupAndCopyBackground(gfxASurface::gfxContentType content)
    1515                 : {
    1516               0 :   if (mCairo) {
    1517               0 :     if (content == gfxASurface::CONTENT_COLOR_ALPHA &&
    1518               0 :         !(GetFlags() & FLAG_DISABLE_COPY_BACKGROUND)) {
    1519               0 :         nsRefPtr<gfxASurface> s = CurrentSurface();
    1520               0 :         if ((s->GetAllowUseAsSource() || s->GetType() == gfxASurface::SurfaceTypeTee) &&
    1521               0 :             (s->GetContentType() == gfxASurface::CONTENT_COLOR ||
    1522               0 :              s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this)))) {
    1523               0 :             cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR);
    1524               0 :             nsRefPtr<gfxASurface> d = CurrentSurface();
    1525                 : 
    1526               0 :             if (d->GetType() == gfxASurface::SurfaceTypeTee) {
    1527               0 :                 NS_ASSERTION(s->GetType() == gfxASurface::SurfaceTypeTee, "Mismatched types");
    1528               0 :                 nsAutoTArray<nsRefPtr<gfxASurface>,2> ss;
    1529               0 :                 nsAutoTArray<nsRefPtr<gfxASurface>,2> ds;
    1530               0 :                 static_cast<gfxTeeSurface*>(s.get())->GetSurfaces(&ss);
    1531               0 :                 static_cast<gfxTeeSurface*>(d.get())->GetSurfaces(&ds);
    1532               0 :                 NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths");
    1533               0 :                 gfxPoint translation = d->GetDeviceOffset() - s->GetDeviceOffset();
    1534               0 :                 for (PRUint32 i = 0; i < ss.Length(); ++i) {
    1535               0 :                     CopySurface(ss[i], ds[i], translation);
    1536                 :                 }
    1537                 :             } else {
    1538               0 :                 CopySurface(s, d, gfxPoint(0, 0));
    1539                 :             }
    1540               0 :             d->SetOpaqueRect(s->GetOpaqueRect());
    1541                 :             return;
    1542                 :         }
    1543                 :     }
    1544               0 :     cairo_push_group_with_content(mCairo, (cairo_content_t) content);
    1545                 :   } else {
    1546               0 :     RefPtr<SourceSurface> source = mDT->Snapshot();
    1547               0 :     PushGroup(content);
    1548               0 :     Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
    1549               0 :     mDT->DrawSurface(source, surfRect, surfRect); 
    1550                 :   }
    1551                 : }
    1552                 : 
    1553                 : already_AddRefed<gfxPattern>
    1554               0 : gfxContext::PopGroup()
    1555                 : {
    1556               0 :   if (mCairo) {
    1557               0 :     cairo_pattern_t *pat = cairo_pop_group(mCairo);
    1558               0 :     gfxPattern *wrapper = new gfxPattern(pat);
    1559               0 :     cairo_pattern_destroy(pat);
    1560               0 :     NS_IF_ADDREF(wrapper);
    1561               0 :     return wrapper;
    1562                 :   } else {
    1563               0 :     RefPtr<SourceSurface> src = mDT->Snapshot();
    1564                 : 
    1565               0 :     Restore();
    1566                 : 
    1567               0 :     Matrix mat = mDT->GetTransform();
    1568               0 :     mat.Invert();
    1569               0 :     nsRefPtr<gfxPattern> pat = new gfxPattern(src, mat);
    1570                 : 
    1571               0 :     return pat.forget();
    1572                 :   }
    1573                 : }
    1574                 : 
    1575                 : void
    1576               0 : gfxContext::PopGroupToSource()
    1577                 : {
    1578               0 :   if (mCairo) {
    1579               0 :     cairo_pop_group_to_source(mCairo);
    1580                 :   } else {
    1581               0 :     RefPtr<SourceSurface> src = mDT->Snapshot();
    1582               0 :     Restore();
    1583               0 :     CurrentState().sourceSurface = src;
    1584               0 :     CurrentState().pattern = NULL;
    1585                 : 
    1586               0 :     Matrix mat = mDT->GetTransform();
    1587               0 :     mat.Invert();
    1588               0 :     CurrentState().surfTransform = mat;
    1589                 :   }
    1590               0 : }
    1591                 : 
    1592                 : bool
    1593               0 : gfxContext::PointInFill(const gfxPoint& pt)
    1594                 : {
    1595               0 :   if (mCairo) {
    1596               0 :     return cairo_in_fill(mCairo, pt.x, pt.y);
    1597                 :   } else {
    1598               0 :     return mPath->ContainsPoint(ToPoint(pt), mTransform);
    1599                 :   }
    1600                 : }
    1601                 : 
    1602                 : bool
    1603               0 : gfxContext::PointInStroke(const gfxPoint& pt)
    1604                 : {
    1605               0 :   if (mCairo) {
    1606               0 :     return cairo_in_stroke(mCairo, pt.x, pt.y);
    1607                 :   } else {
    1608                 :     // XXX - Used by SVG, needs fixing.
    1609               0 :     return false;
    1610                 :   }
    1611                 : }
    1612                 : 
    1613                 : gfxRect
    1614               0 : gfxContext::GetUserPathExtent()
    1615                 : {
    1616               0 :   if (mCairo) {
    1617                 :     double xmin, ymin, xmax, ymax;
    1618               0 :     cairo_path_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
    1619               0 :     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
    1620                 :   } else {
    1621               0 :     return ThebesRect(mPath->GetBounds());
    1622                 :   }
    1623                 : }
    1624                 : 
    1625                 : gfxRect
    1626               0 : gfxContext::GetUserFillExtent()
    1627                 : {
    1628               0 :   if (mCairo) {
    1629                 :     double xmin, ymin, xmax, ymax;
    1630               0 :     cairo_fill_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
    1631               0 :     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
    1632                 :   } else {
    1633               0 :     return ThebesRect(mPath->GetBounds());
    1634                 :   }
    1635                 : }
    1636                 : 
    1637                 : gfxRect
    1638               0 : gfxContext::GetUserStrokeExtent()
    1639                 : {
    1640               0 :   if (mCairo) {
    1641                 :     double xmin, ymin, xmax, ymax;
    1642               0 :     cairo_stroke_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
    1643               0 :     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
    1644                 :   } else {
    1645               0 :     return ThebesRect(mPath->GetStrokedBounds(CurrentState().strokeOptions, mTransform));
    1646                 :   }
    1647                 : }
    1648                 : 
    1649                 : already_AddRefed<gfxFlattenedPath>
    1650               0 : gfxContext::GetFlattenedPath()
    1651                 : {
    1652               0 :   if (mCairo) {
    1653                 :     gfxFlattenedPath *path =
    1654               0 :         new gfxFlattenedPath(cairo_copy_path_flat(mCairo));
    1655               0 :     NS_IF_ADDREF(path);
    1656               0 :     return path;
    1657                 :   } else {
    1658                 :     // XXX - Used by SVG, needs fixing.
    1659               0 :     return NULL;
    1660                 :   }
    1661                 : }
    1662                 : 
    1663                 : bool
    1664               0 : gfxContext::HasError()
    1665                 : {
    1666               0 :   if (mCairo) {
    1667               0 :     return cairo_status(mCairo) != CAIRO_STATUS_SUCCESS;
    1668                 :   } else {
    1669                 :     // As far as this is concerned, an Azure context is never in error.
    1670               0 :     return false;
    1671                 :   }
    1672                 : }
    1673                 : 
    1674                 : void
    1675               0 : gfxContext::RoundedRectangle(const gfxRect& rect,
    1676                 :                              const gfxCornerSizes& corners,
    1677                 :                              bool draw_clockwise)
    1678                 : {
    1679                 :     //
    1680                 :     // For CW drawing, this looks like:
    1681                 :     //
    1682                 :     //  ...******0**      1    C
    1683                 :     //              ****
    1684                 :     //                  ***    2
    1685                 :     //                     **
    1686                 :     //                       *
    1687                 :     //                        *
    1688                 :     //                         3
    1689                 :     //                         *
    1690                 :     //                         *
    1691                 :     //
    1692                 :     // Where 0, 1, 2, 3 are the control points of the Bezier curve for
    1693                 :     // the corner, and C is the actual corner point.
    1694                 :     //
    1695                 :     // At the start of the loop, the current point is assumed to be
    1696                 :     // the point adjacent to the top left corner on the top
    1697                 :     // horizontal.  Note that corner indices start at the top left and
    1698                 :     // continue clockwise, whereas in our loop i = 0 refers to the top
    1699                 :     // right corner.
    1700                 :     //
    1701                 :     // When going CCW, the control points are swapped, and the first
    1702                 :     // corner that's drawn is the top left (along with the top segment).
    1703                 :     //
    1704                 :     // There is considerable latitude in how one chooses the four
    1705                 :     // control points for a Bezier curve approximation to an ellipse.
    1706                 :     // For the overall path to be continuous and show no corner at the
    1707                 :     // endpoints of the arc, points 0 and 3 must be at the ends of the
    1708                 :     // straight segments of the rectangle; points 0, 1, and C must be
    1709                 :     // collinear; and points 3, 2, and C must also be collinear.  This
    1710                 :     // leaves only two free parameters: the ratio of the line segments
    1711                 :     // 01 and 0C, and the ratio of the line segments 32 and 3C.  See
    1712                 :     // the following papers for extensive discussion of how to choose
    1713                 :     // these ratios:
    1714                 :     //
    1715                 :     //   Dokken, Tor, et al. "Good approximation of circles by
    1716                 :     //      curvature-continuous Bezier curves."  Computer-Aided
    1717                 :     //      Geometric Design 7(1990) 33--41.
    1718                 :     //   Goldapp, Michael. "Approximation of circular arcs by cubic
    1719                 :     //      polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
    1720                 :     //   Maisonobe, Luc. "Drawing an elliptical arc using polylines,
    1721                 :     //      quadratic, or cubic Bezier curves."
    1722                 :     //      http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
    1723                 :     //
    1724                 :     // We follow the approach in section 2 of Goldapp (least-error,
    1725                 :     // Hermite-type approximation) and make both ratios equal to
    1726                 :     //
    1727                 :     //          2   2 + n - sqrt(2n + 28)
    1728                 :     //  alpha = - * ---------------------
    1729                 :     //          3           n - 4
    1730                 :     //
    1731                 :     // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
    1732                 :     //
    1733                 :     // This is the result of Goldapp's equation (10b) when the angle
    1734                 :     // swept out by the arc is pi/2, and the parameter "a-bar" is the
    1735                 :     // expression given immediately below equation (21).
    1736                 :     //
    1737                 :     // Using this value, the maximum radial error for a circle, as a
    1738                 :     // fraction of the radius, is on the order of 0.2 x 10^-3.
    1739                 :     // Neither Dokken nor Goldapp discusses error for a general
    1740                 :     // ellipse; Maisonobe does, but his choice of control points
    1741                 :     // follows different constraints, and Goldapp's expression for
    1742                 :     // 'alpha' gives much smaller radial error, even for very flat
    1743                 :     // ellipses, than Maisonobe's equivalent.
    1744                 :     //
    1745                 :     // For the various corners and for each axis, the sign of this
    1746                 :     // constant changes, or it might be 0 -- it's multiplied by the
    1747                 :     // appropriate multiplier from the list before using.
    1748                 : 
    1749               0 :   if (mCairo) {
    1750               0 :     const gfxFloat alpha = 0.55191497064665766025;
    1751                 : 
    1752                 :     typedef struct { gfxFloat a, b; } twoFloats;
    1753                 : 
    1754                 :     twoFloats cwCornerMults[4] = { { -1,  0 },
    1755                 :                                    {  0, -1 },
    1756                 :                                    { +1,  0 },
    1757               0 :                                    {  0, +1 } };
    1758                 :     twoFloats ccwCornerMults[4] = { { +1,  0 },
    1759                 :                                     {  0, -1 },
    1760                 :                                     { -1,  0 },
    1761               0 :                                     {  0, +1 } };
    1762                 : 
    1763               0 :     twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
    1764                 : 
    1765               0 :     gfxPoint pc, p0, p1, p2, p3;
    1766                 : 
    1767               0 :     if (draw_clockwise)
    1768               0 :         cairo_move_to(mCairo, rect.X() + corners[NS_CORNER_TOP_LEFT].width, rect.Y());
    1769                 :     else
    1770               0 :         cairo_move_to(mCairo, rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width, rect.Y());
    1771                 : 
    1772               0 :     NS_FOR_CSS_CORNERS(i) {
    1773                 :         // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
    1774               0 :         mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
    1775                 : 
    1776                 :         // i+2 and i+3 respectively.  These are used to index into the corner
    1777                 :         // multiplier table, and were deduced by calculating out the long form
    1778                 :         // of each corner and finding a pattern in the signs and values.
    1779               0 :         int i2 = (i+2) % 4;
    1780               0 :         int i3 = (i+3) % 4;
    1781                 : 
    1782               0 :         pc = rect.AtCorner(c);
    1783                 : 
    1784               0 :         if (corners[c].width > 0.0 && corners[c].height > 0.0) {
    1785               0 :             p0.x = pc.x + cornerMults[i].a * corners[c].width;
    1786               0 :             p0.y = pc.y + cornerMults[i].b * corners[c].height;
    1787                 : 
    1788               0 :             p3.x = pc.x + cornerMults[i3].a * corners[c].width;
    1789               0 :             p3.y = pc.y + cornerMults[i3].b * corners[c].height;
    1790                 : 
    1791               0 :             p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
    1792               0 :             p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
    1793                 : 
    1794               0 :             p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
    1795               0 :             p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
    1796                 : 
    1797               0 :             cairo_line_to (mCairo, p0.x, p0.y);
    1798                 :             cairo_curve_to (mCairo,
    1799                 :                             p1.x, p1.y,
    1800                 :                             p2.x, p2.y,
    1801               0 :                             p3.x, p3.y);
    1802                 :         } else {
    1803               0 :             cairo_line_to (mCairo, pc.x, pc.y);
    1804                 :         }
    1805                 :     }
    1806                 : 
    1807               0 :     cairo_close_path (mCairo);
    1808                 :   } else {
    1809               0 :     EnsurePathBuilder();
    1810                 : 
    1811               0 :     const gfxFloat alpha = 0.55191497064665766025;
    1812                 : 
    1813                 :     typedef struct { gfxFloat a, b; } twoFloats;
    1814                 : 
    1815                 :     twoFloats cwCornerMults[4] = { { -1,  0 },
    1816                 :                                    {  0, -1 },
    1817                 :                                    { +1,  0 },
    1818               0 :                                    {  0, +1 } };
    1819                 :     twoFloats ccwCornerMults[4] = { { +1,  0 },
    1820                 :                                     {  0, -1 },
    1821                 :                                     { -1,  0 },
    1822               0 :                                     {  0, +1 } };
    1823                 : 
    1824               0 :     twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
    1825                 : 
    1826               0 :     gfxPoint pc, p0, p1, p2, p3;
    1827                 : 
    1828               0 :     if (draw_clockwise)
    1829               0 :         mPathBuilder->MoveTo(Point(Float(rect.X() + corners[NS_CORNER_TOP_LEFT].width), Float(rect.Y())));
    1830                 :     else
    1831               0 :         mPathBuilder->MoveTo(Point(Float(rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width), Float(rect.Y())));
    1832                 : 
    1833               0 :     NS_FOR_CSS_CORNERS(i) {
    1834                 :         // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
    1835               0 :         mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
    1836                 : 
    1837                 :         // i+2 and i+3 respectively.  These are used to index into the corner
    1838                 :         // multiplier table, and were deduced by calculating out the long form
    1839                 :         // of each corner and finding a pattern in the signs and values.
    1840               0 :         int i2 = (i+2) % 4;
    1841               0 :         int i3 = (i+3) % 4;
    1842                 : 
    1843               0 :         pc = rect.AtCorner(c);
    1844                 : 
    1845               0 :         if (corners[c].width > 0.0 && corners[c].height > 0.0) {
    1846               0 :             p0.x = pc.x + cornerMults[i].a * corners[c].width;
    1847               0 :             p0.y = pc.y + cornerMults[i].b * corners[c].height;
    1848                 : 
    1849               0 :             p3.x = pc.x + cornerMults[i3].a * corners[c].width;
    1850               0 :             p3.y = pc.y + cornerMults[i3].b * corners[c].height;
    1851                 : 
    1852               0 :             p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
    1853               0 :             p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
    1854                 : 
    1855               0 :             p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
    1856               0 :             p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
    1857                 : 
    1858               0 :             mPathBuilder->LineTo(ToPoint(p0));
    1859               0 :             mPathBuilder->BezierTo(ToPoint(p1), ToPoint(p2), ToPoint(p3));
    1860                 :         } else {
    1861               0 :             mPathBuilder->LineTo(ToPoint(pc));
    1862                 :         }
    1863                 :     }
    1864                 : 
    1865               0 :     mPathBuilder->Close();
    1866                 :   }
    1867               0 : }
    1868                 : 
    1869                 : #ifdef MOZ_DUMP_PAINTING
    1870                 : void
    1871               0 : gfxContext::WriteAsPNG(const char* aFile)
    1872                 : { 
    1873               0 :   nsRefPtr<gfxASurface> surf = CurrentSurface();
    1874               0 :   if (surf) {
    1875               0 :     surf->WriteAsPNG(aFile);
    1876                 :   } else {
    1877               0 :     NS_WARNING("No surface found!");
    1878                 :   }
    1879               0 : }
    1880                 : 
    1881                 : void 
    1882               0 : gfxContext::DumpAsDataURL()
    1883                 : { 
    1884               0 :   nsRefPtr<gfxASurface> surf = CurrentSurface();
    1885               0 :   if (surf) {
    1886               0 :     surf->DumpAsDataURL();
    1887                 :   } else {
    1888               0 :     NS_WARNING("No surface found!");
    1889                 :   }
    1890               0 : }
    1891                 : 
    1892                 : void 
    1893               0 : gfxContext::CopyAsDataURL()
    1894                 : { 
    1895               0 :   nsRefPtr<gfxASurface> surf = CurrentSurface();
    1896               0 :   if (surf) {
    1897               0 :     surf->CopyAsDataURL();
    1898                 :   } else {
    1899               0 :     NS_WARNING("No surface found!");
    1900                 :   }
    1901               0 : }
    1902                 : #endif
    1903                 : 
    1904                 : void
    1905               0 : gfxContext::EnsurePath()
    1906                 : {
    1907               0 :   if (mPathBuilder) {
    1908               0 :     mPath = mPathBuilder->Finish();
    1909               0 :     mPathBuilder = NULL;
    1910                 :   }
    1911                 : 
    1912               0 :   if (mPath) {
    1913               0 :     if (mTransformChanged) {
    1914               0 :       Matrix mat = mDT->GetTransform();
    1915               0 :       mat.Invert();
    1916               0 :       mat = mPathTransform * mat;
    1917               0 :       mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule);
    1918               0 :       mPath = mPathBuilder->Finish();
    1919               0 :       mPathBuilder = NULL;
    1920                 : 
    1921               0 :       mTransformChanged = false;
    1922                 :     }
    1923                 : 
    1924               0 :     if (CurrentState().fillRule == mPath->GetFillRule()) {
    1925               0 :       return;
    1926                 :     }
    1927                 : 
    1928               0 :     mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
    1929                 : 
    1930               0 :     mPath = mPathBuilder->Finish();
    1931               0 :     mPathBuilder = NULL;
    1932               0 :     return;
    1933                 :   }
    1934                 : 
    1935               0 :   EnsurePathBuilder();
    1936               0 :   mPath = mPathBuilder->Finish();
    1937               0 :   mPathBuilder = NULL;
    1938                 : }
    1939                 : 
    1940                 : void
    1941               0 : gfxContext::EnsurePathBuilder()
    1942                 : {
    1943               0 :   if (mPathBuilder) {
    1944               0 :     return;
    1945                 :   }
    1946                 : 
    1947               0 :   if (mPath) {
    1948               0 :     mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
    1949               0 :     mPath = NULL;
    1950                 :   }
    1951                 : 
    1952               0 :   mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
    1953                 : 
    1954               0 :   if (mPathIsRect && !mTransformChanged) {
    1955               0 :     mPathBuilder->MoveTo(mRect.TopLeft());
    1956               0 :     mPathBuilder->LineTo(mRect.TopRight());
    1957               0 :     mPathBuilder->LineTo(mRect.BottomRight());
    1958               0 :     mPathBuilder->LineTo(mRect.BottomLeft());
    1959               0 :     mPathBuilder->Close();
    1960               0 :   } else if (mPathIsRect) {
    1961               0 :     mTransformChanged = false;
    1962               0 :     Matrix mat = mDT->GetTransform();
    1963               0 :     mat.Invert();
    1964               0 :     mat = mPathTransform * mat;
    1965               0 :     mPathBuilder->MoveTo(mat * mRect.TopLeft());
    1966               0 :     mPathBuilder->LineTo(mat * mRect.TopRight());
    1967               0 :     mPathBuilder->LineTo(mat * mRect.BottomRight());
    1968               0 :     mPathBuilder->LineTo(mat * mRect.BottomLeft());
    1969               0 :     mPathBuilder->Close();
    1970                 :   }
    1971                 : 
    1972               0 :   mPathIsRect = false;
    1973                 : }
    1974                 : 
    1975                 : void
    1976               0 : gfxContext::FillAzure(Float aOpacity)
    1977                 : {
    1978               0 :   AzureState &state = CurrentState();
    1979                 : 
    1980               0 :   CompositionOp op = GetOp();
    1981                 : 
    1982               0 :   if (mPathIsRect && !mTransformChanged) {
    1983               0 :     if (state.opIsClear) {
    1984               0 :       mDT->ClearRect(mRect);
    1985               0 :     } else if (op == OP_SOURCE) {
    1986                 :       // Emulate cairo operator source which is bound by mask!
    1987               0 :       mDT->ClearRect(mRect);
    1988               0 :       mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity));
    1989                 :     } else {
    1990               0 :       mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
    1991                 :     }
    1992                 :   } else {
    1993               0 :     EnsurePath();
    1994                 : 
    1995               0 :     NS_ASSERTION(!state.opIsClear, "We shouldn't be clearing complex paths!");
    1996                 : 
    1997               0 :     mDT->Fill(mPath, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
    1998                 :   }
    1999               0 : }
    2000                 : 
    2001                 : void
    2002               0 : gfxContext::PushClipsToDT(DrawTarget *aDT)
    2003                 : {
    2004                 :   // Tricky, we have to restore all clips -since the last time- the clip
    2005                 :   // was reset. If we didn't reset the clip, just popping the clips we
    2006                 :   // added was fine.
    2007               0 :   unsigned int lastReset = 0;
    2008               0 :   for (int i = mStateStack.Length() - 2; i > 0; i--) {
    2009               0 :     if (mStateStack[i].clipWasReset) {
    2010               0 :       lastReset = i;
    2011                 :     }
    2012                 :   }
    2013                 : 
    2014                 :   // Don't need to save the old transform, we'll be setting a new one soon!
    2015                 : 
    2016                 :   // Push all clips from the last state on the stack where the clip was
    2017                 :   // reset to the clip before ours.
    2018               0 :   for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) {
    2019               0 :     for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
    2020               0 :       aDT->SetTransform(mStateStack[i].pushedClips[c].transform);
    2021               0 :       if (mStateStack[i].pushedClips[c].path) {
    2022               0 :         aDT->PushClip(mStateStack[i].pushedClips[c].path);
    2023                 :       } else {
    2024               0 :         aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
    2025                 :       }
    2026                 :     }
    2027                 :   }
    2028               0 : }
    2029                 : 
    2030                 : CompositionOp
    2031               0 : gfxContext::GetOp()
    2032                 : {
    2033               0 :   if (CurrentState().op != OP_SOURCE) {
    2034               0 :     return CurrentState().op;
    2035                 :   }
    2036                 : 
    2037               0 :   AzureState &state = CurrentState();
    2038               0 :   if (state.pattern) {
    2039               0 :     if (state.pattern->IsOpaque()) {
    2040               0 :       return OP_OVER;
    2041                 :     } else {
    2042               0 :       return OP_SOURCE;
    2043                 :     }
    2044               0 :   } else if (state.sourceSurface) {
    2045               0 :     if (state.sourceSurface->GetFormat() == FORMAT_B8G8R8X8) {
    2046               0 :       return OP_OVER;
    2047                 :     } else {
    2048               0 :       return OP_SOURCE;
    2049                 :     }
    2050                 :   } else {
    2051               0 :     if (state.color.a > 0.999) {
    2052               0 :       return OP_OVER;
    2053                 :     } else {
    2054               0 :       return OP_SOURCE;
    2055                 :     }
    2056                 :   }
    2057                 : }

Generated by: LCOV version 1.7