Back to index

lightning-sunbird  0.9+nobinonly
nsCairoRenderingContext.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * mozilla.org.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Stuart Parmenter <pavlov@pavlov.net>
00024  *   Joe Hewitt <hewitt@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsCairoRenderingContext.h"
00041 #include "nsCairoDeviceContext.h"
00042 #include "nsCairoSurfaceManager.h"
00043 
00044 #include "nsRect.h"
00045 #include "nsString.h"
00046 #include "nsTransform2D.h"
00047 #include "nsIRegion.h"
00048 #include "nsIServiceManager.h"
00049 #include "nsIInterfaceRequestorUtils.h"
00050 #include "nsGfxCIID.h"
00051 
00052 #include "imgIContainer.h"
00053 #include "gfxIImageFrame.h"
00054 #include "nsIImage.h"
00055 
00056 #include "nsCairoFontMetrics.h"
00057 #include "nsCairoDrawingSurface.h"
00058 #include "nsCairoRegion.h"
00059 
00060 #include <sys/types.h>
00061 #include <sys/stat.h>
00062 #include <fcntl.h>
00063 
00064 static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
00065 
00066 
00068 
00069 NS_IMPL_ISUPPORTS1(nsCairoRenderingContext, nsIRenderingContext)
00070 
00071 nsCairoRenderingContext::nsCairoRenderingContext() :
00072     mLineStyle(nsLineStyle_kNone),
00073     mCairo(nsnull)
00074 {
00075 }
00076 
00077 nsCairoRenderingContext::~nsCairoRenderingContext()
00078 {
00079     if (mCairo)
00080         cairo_destroy(mCairo);
00081 }
00082 
00085 
00086 NS_IMETHODIMP
00087 nsCairoRenderingContext::Init(nsIDeviceContext* aContext, nsIWidget *aWidget)
00088 {
00089     nsCairoDeviceContext *cairoDC = NS_STATIC_CAST(nsCairoDeviceContext*, aContext);
00090 
00091     mDeviceContext = aContext;
00092     mWidget = aWidget;
00093 
00094     mDrawingSurface = new nsCairoDrawingSurface();
00095     nsNativeWidget nativeWidget = aWidget->GetNativeData(NS_NATIVE_WIDGET);
00096     mDrawingSurface->Init (cairoDC, nativeWidget);
00097 
00098     mCairo = cairo_create ();
00099     cairo_set_target_surface (mCairo, mDrawingSurface->GetCairoSurface());
00100 
00101     return (CommonInit());
00102 }
00103 
00104 NS_IMETHODIMP
00105 nsCairoRenderingContext::Init(nsIDeviceContext* aContext, nsIDrawingSurface *aSurface)
00106 {
00107     nsCairoDrawingSurface *cds = (nsCairoDrawingSurface *) aSurface;
00108 
00109     mDeviceContext = aContext;
00110     mWidget = nsnull;
00111 
00112     mDrawingSurface = (nsCairoDrawingSurface *) cds;
00113 
00114     mCairo = cairo_create ();
00115     cairo_set_target_surface (mCairo, mDrawingSurface->GetCairoSurface());
00116 
00117     return (CommonInit());
00118 }
00119 
00120 NS_IMETHODIMP
00121 nsCairoRenderingContext::CommonInit(void)
00122 {
00123     mClipRegion = new nsCairoRegion();
00124 
00125     float app2dev;
00126     app2dev = mDeviceContext->AppUnitsToDevUnits();
00127     Scale(app2dev, app2dev);
00128 
00129     return NS_OK;
00130 }
00131 
00132 NS_IMETHODIMP
00133 nsCairoRenderingContext::Reset(void)
00134 {
00135     cairo_surface_t *surf = cairo_current_target_surface (mCairo);
00136     cairo_t *cairo = cairo_create ();
00137     cairo_set_target_surface (cairo, surf);
00138     cairo_destroy (mCairo);
00139     mCairo = cairo;
00140 
00141     mClipRegion = new nsCairoRegion();
00142 
00143     return (CommonInit());
00144 }
00145 
00146 NS_IMETHODIMP
00147 nsCairoRenderingContext::GetDeviceContext(nsIDeviceContext *& aDeviceContext)
00148 {
00149     aDeviceContext = mDeviceContext;
00150     NS_IF_ADDREF(aDeviceContext);
00151     return NS_OK;
00152 }
00153 
00154 NS_IMETHODIMP
00155 nsCairoRenderingContext::LockDrawingSurface(PRInt32 aX, PRInt32 aY,
00156                                             PRUint32 aWidth, PRUint32 aHeight,
00157                                             void **aBits, PRInt32 *aStride,
00158                                             PRInt32 *aWidthBytes,
00159                                             PRUint32 aFlags)
00160 {
00161     if (mOffscreenSurface)
00162         return mOffscreenSurface->Lock(aX, aY, aWidth, aHeight, aBits, aStride, aWidthBytes, aFlags);
00163     else
00164         return mDrawingSurface->Lock(aX, aY, aWidth, aHeight, aBits, aStride, aWidthBytes, aFlags);
00165 
00166     return NS_OK;
00167 }
00168 
00169 NS_IMETHODIMP
00170 nsCairoRenderingContext::UnlockDrawingSurface(void)
00171 {
00172     if (mOffscreenSurface)
00173         return mOffscreenSurface->Unlock();
00174     else
00175         return mDrawingSurface->Unlock();
00176 
00177     return NS_OK;
00178 }
00179 
00180 NS_IMETHODIMP
00181 nsCairoRenderingContext::SelectOffScreenDrawingSurface(nsIDrawingSurface *aSurface)
00182 {
00183     NS_WARNING("not implemented, because I don't know how to unselect the offscreen surface");
00184     return NS_ERROR_FAILURE;
00185 
00186     mOffscreenSurface = NS_STATIC_CAST(nsCairoDrawingSurface *, aSurface);
00187     return NS_OK;
00188 }
00189 
00190 NS_IMETHODIMP
00191 nsCairoRenderingContext::GetDrawingSurface(nsIDrawingSurface **aSurface)
00192 {
00193     if (mOffscreenSurface)
00194         *aSurface = mOffscreenSurface;
00195     else if (mDrawingSurface)
00196         *aSurface = mDrawingSurface;
00197 
00198     NS_IF_ADDREF (*aSurface);
00199     
00200     return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP 
00204 nsCairoRenderingContext::PushTranslation(PushedTranslation* aState)
00205 {
00206     // XXX this is slow!
00207     PushState();
00208     return NS_OK;
00209 }
00210 
00211 NS_IMETHODIMP
00212 nsCairoRenderingContext::PopTranslation(PushedTranslation* aState)
00213 {
00214     // XXX this is slow!
00215     PopState();
00216     return NS_OK;
00217 }
00218 
00219 NS_IMETHODIMP
00220 nsCairoRenderingContext::GetHints(PRUint32& aResult)
00221 {
00222     aResult = 0;
00223 
00224     return NS_OK;
00225 }
00226 
00227 NS_IMETHODIMP
00228 nsCairoRenderingContext::PushState()
00229 {
00230     cairo_save (mCairo);
00231     return NS_OK;
00232 }
00233 
00234 NS_IMETHODIMP
00235 nsCairoRenderingContext::PopState()
00236 {
00237     cairo_restore (mCairo);
00238     return NS_OK;
00239 }
00240 
00241 //
00242 // clipping
00243 //
00244 
00245 NS_IMETHODIMP
00246 nsCairoRenderingContext::IsVisibleRect(const nsRect& aRect, PRBool &aIsVisible)
00247 {
00248     // XXX
00249     aIsVisible = PR_TRUE;
00250     return NS_OK;
00251 }
00252 
00253 NS_IMETHODIMP
00254 nsCairoRenderingContext::GetClipRect(nsRect &aRect, PRBool &aHasLocalClip)
00255 {
00256     NS_ERROR("not used anywhere");
00257     return NS_OK;
00258 }
00259 
00260 void
00261 nsCairoRenderingContext::DoCairoClip()
00262 {
00263     nsRegionComplexity cplx;
00264     mClipRegion->GetRegionComplexity(cplx);
00265 
00266     cairo_init_clip (mCairo);
00267 
00268     float pixToTwip = mDeviceContext->DevUnitsToAppUnits();
00269 
00270     if (cplx == eRegionComplexity_rect) {
00271         PRInt32 x, y, w, h;
00272         mClipRegion->GetBoundingBox(&x, &y, &w, &h);
00273         cairo_new_path (mCairo);
00274         cairo_rectangle (mCairo,
00275                          double(x * pixToTwip),
00276                          double(y * pixToTwip),
00277                          double(w * pixToTwip),
00278                          double(h * pixToTwip));
00279         cairo_clip (mCairo);
00280     } else if (cplx == eRegionComplexity_complex) {
00281         nsRegionRectSet *rects = nsnull;
00282         nsresult rv = mClipRegion->GetRects (&rects);
00283         if (NS_FAILED(rv) || !rects)
00284             return;
00285 
00286         cairo_new_path (mCairo);
00287         for (PRUint32 i = 0; i < rects->mRectsLen; i++) {
00288             cairo_rectangle (mCairo,
00289                              double (rects->mRects[i].x * pixToTwip),
00290                              double (rects->mRects[i].y * pixToTwip),
00291                              double (rects->mRects[i].width * pixToTwip),
00292                              double (rects->mRects[i].height * pixToTwip));
00293         }
00294 
00295         cairo_clip (mCairo);
00296 
00297         mClipRegion->FreeRects (rects);
00298     }
00299 }
00300 
00301 NS_IMETHODIMP
00302 nsCairoRenderingContext::SetClipRect(const nsRect& aRect, nsClipCombine aCombine)
00303 {
00304     // transform rect by current transform matrix and clip
00305     return NS_OK;
00306 }
00307 
00308 NS_IMETHODIMP
00309 nsCairoRenderingContext::SetClipRegion(const nsIRegion& aRegion,
00310                                        nsClipCombine aCombine)
00311 {
00312     // region is in device coords, no transformation!
00313     // how do we do that with cairo?
00314 
00315     switch(aCombine)
00316     {
00317     case nsClipCombine_kIntersect:
00318         mClipRegion->Intersect (aRegion);
00319         break;
00320     case nsClipCombine_kReplace:
00321         mClipRegion->SetTo (aRegion);
00322         break;
00323     case nsClipCombine_kUnion:
00324     case nsClipCombine_kSubtract:
00325         // these two are never used in mozilla
00326     default:
00327         NS_WARNING("aCombine type passed that should never be used\n");
00328         break;
00329     }
00330 
00331     DoCairoClip();
00332 
00333     return NS_OK;
00334 }
00335 
00336 NS_IMETHODIMP
00337 nsCairoRenderingContext::CopyClipRegion(nsIRegion &aRegion)
00338 {
00339     aRegion.SetTo(*mClipRegion);
00340     return NS_OK;
00341 }
00342 
00343 NS_IMETHODIMP
00344 nsCairoRenderingContext::GetClipRegion(nsIRegion **aRegion)
00345 {
00346     *aRegion = new nsCairoRegion();
00347     (*aRegion)->SetTo(*mClipRegion);
00348     NS_ADDREF(*aRegion);
00349     return NS_OK;
00350 }
00351 
00352 // only gets called from the caret blinker.
00353 NS_IMETHODIMP
00354 nsCairoRenderingContext::FlushRect(const nsRect& aRect)
00355 {
00356     return FlushRect(aRect.x, aRect.y, aRect.width, aRect.height);
00357 }
00358 
00359 NS_IMETHODIMP
00360 nsCairoRenderingContext::FlushRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00361 {
00362     return NS_OK;
00363 }
00364 
00365 //
00366 // other junk
00367 //
00368 
00369 NS_IMETHODIMP
00370 nsCairoRenderingContext::SetLineStyle(nsLineStyle aLineStyle)
00371 {
00372     static double dash[] = {5.0, 5.0};
00373     static double dot[] = {1.0, 1.0};
00374 
00375     switch (aLineStyle) {
00376         case nsLineStyle_kNone:
00377             // XX what is this supposed to be? invisible line?
00378             break;
00379         case nsLineStyle_kSolid:
00380             cairo_set_dash (mCairo, nsnull, 0, 0);
00381             break;
00382         case nsLineStyle_kDashed:
00383             cairo_set_dash (mCairo, dash, 2, 0);
00384             break;
00385         case nsLineStyle_kDotted:
00386             cairo_set_dash (mCairo, dot, 2, 0);
00387             break;
00388         default:
00389             NS_ERROR("SetLineStyle: Invalid line style");
00390             break;
00391     }
00392 
00393     mLineStyle = aLineStyle;
00394     return NS_OK;
00395 }
00396 
00397 NS_IMETHODIMP
00398 nsCairoRenderingContext::GetLineStyle(nsLineStyle &aLineStyle)
00399 {
00400     NS_ERROR("not used anywhere");
00401     aLineStyle = mLineStyle;
00402     return NS_OK;
00403 }
00404 
00405 NS_IMETHODIMP
00406 nsCairoRenderingContext::SetPenMode(nsPenMode aPenMode)
00407 {
00408     // aPenMode == nsPenMode_kNone, nsPenMode_kInverted.
00409     NS_ERROR("not used anywhere");
00410     return NS_ERROR_NOT_IMPLEMENTED;
00411 }
00412 
00413 NS_IMETHODIMP
00414 nsCairoRenderingContext::GetPenMode(nsPenMode &aPenMode)
00415 {
00416     //NS_ERROR("not used anywhere, but used in a debug thing");
00417     return NS_ERROR_NOT_IMPLEMENTED;
00418 }
00419 
00420 NS_IMETHODIMP
00421 nsCairoRenderingContext::SetColor(nscolor aColor)
00422 {
00423     mColor = aColor;
00424 
00425     PRUint8 r = NS_GET_R(aColor);
00426     PRUint8 g = NS_GET_G(aColor);
00427     PRUint8 b = NS_GET_B(aColor);
00428     PRUint8 a = NS_GET_A(aColor);
00429 
00430     cairo_set_rgb_color (mCairo,
00431                          (double) r / 255.0,
00432                          (double) g / 255.0,
00433                          (double) b / 255.0);
00434     cairo_set_alpha (mCairo, (double) a / 255.0);
00435 
00436     return NS_OK;
00437 }
00438 
00439 NS_IMETHODIMP
00440 nsCairoRenderingContext::GetColor(nscolor &aColor) const
00441 {
00442     aColor = mColor;
00443     return NS_OK;
00444 }
00445 
00446 NS_IMETHODIMP
00447 nsCairoRenderingContext::Translate(nscoord aX, nscoord aY)
00448 {
00449 //    fprintf (stderr, "++ Xlate: %g %g\n", (double)aX, (double)aY);
00450     cairo_translate (mCairo, (double) aX, (double) aY);
00451     return NS_OK;
00452 }
00453 
00454 NS_IMETHODIMP
00455 nsCairoRenderingContext::Scale(float aSx, float aSy)
00456 {
00457 //    fprintf (stderr, "++ Scale: %g %g\n", (double)aSx, (double)aSy);
00458     cairo_scale (mCairo, (double) aSx, (double) aSy);
00459     return NS_OK;
00460 }
00461 
00462 void
00463 nsCairoRenderingContext::UpdateTempTransformMatrix()
00464 {
00465     double a, b, c, d, tx, ty;
00466     cairo_matrix_t *mat = cairo_matrix_create();
00467     cairo_current_matrix (mCairo, mat);
00468     cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty);
00469     /*****
00470      * Cairo matrix layout:   gfx matrix layout:
00471      * | a  b  0 |            | m00 m01  0 |
00472      * | c  d  0 |            | m10 m11  0 |
00473      * | tx ty 1 |            | m20 m21  1 |
00474      *****/
00475 
00476     cairo_matrix_destroy (mat);
00477 
00478     NS_ASSERTION(b == 0 && c == 0, "Can't represent Cairo matrix to Gfx");
00479     mTempTransform.SetToTranslate(tx, ty);
00480     mTempTransform.AddScale(a, d);
00481 }
00482 
00483 nsTransform2D&
00484 nsCairoRenderingContext::CurrentTransform()
00485 {
00486     UpdateTempTransformMatrix();
00487     return mTempTransform;
00488 }
00489 
00490 NS_IMETHODIMP
00491 nsCairoRenderingContext::GetCurrentTransform(nsTransform2D *&aTransform)
00492 {
00493     UpdateTempTransformMatrix();
00494     aTransform = &mTempTransform;
00495     return NS_OK;
00496 }
00497 
00498 void
00499 nsCairoRenderingContext::TransformCoord (nscoord *aX, nscoord *aY)
00500 {
00501     double x = double(*aX);
00502     double y = double(*aY);
00503     cairo_transform_point (mCairo, &x, &y);
00504     *aX = nscoord(x);
00505     *aY = nscoord(y);
00506 }
00507 
00508 NS_IMETHODIMP
00509 nsCairoRenderingContext::CreateDrawingSurface(const nsRect &aBounds,
00510                                               PRUint32 aSurfFlags,
00511                                               nsIDrawingSurface* &aSurface)
00512 {
00513     nsCairoDrawingSurface *cds = new nsCairoDrawingSurface();
00514     nsIDeviceContext *dc = mDeviceContext.get();
00515     cds->Init (NS_STATIC_CAST(nsCairoDeviceContext *, dc),
00516                aBounds.width, aBounds.height,
00517                PR_FALSE);
00518     aSurface = (nsIDrawingSurface*) cds;
00519     NS_ADDREF(aSurface);
00520 
00521     return NS_OK;
00522 }
00523 
00524 NS_IMETHODIMP
00525 nsCairoRenderingContext::DestroyDrawingSurface(nsIDrawingSurface *aDS)
00526 {
00527     NS_RELEASE(aDS);
00528 
00529     return NS_OK;
00530 }
00531 
00532 NS_IMETHODIMP
00533 nsCairoRenderingContext::DrawLine(nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1)
00534 {
00535     // XXX are these twips?
00536     cairo_new_path (mCairo);
00537     cairo_move_to (mCairo, (double) aX0, (double) aY0);
00538     cairo_line_to (mCairo, (double) aX1, (double) aY1);
00539     cairo_stroke (mCairo);
00540 
00541     return NS_OK;
00542 }
00543 
00544 NS_IMETHODIMP
00545 nsCairoRenderingContext::DrawRect(const nsRect& aRect)
00546 {
00547     return DrawRect(aRect.x, aRect.y, aRect.width, aRect.height);
00548 }
00549 
00550 NS_IMETHODIMP
00551 nsCairoRenderingContext::DrawRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00552 {
00553     cairo_new_path (mCairo);
00554     cairo_rectangle (mCairo, (double) aX, (double) aY, (double) aWidth, (double) aHeight);
00555     cairo_stroke (mCairo);
00556     return NS_OK;
00557 }
00558 
00559 NS_IMETHODIMP
00560 nsCairoRenderingContext::FillRect(const nsRect& aRect)
00561 {
00562     return FillRect(aRect.x, aRect.y, aRect.width, aRect.height);
00563 }
00564 
00565 NS_IMETHODIMP
00566 nsCairoRenderingContext::FillRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00567 {
00568     cairo_new_path (mCairo);
00569     cairo_rectangle (mCairo, (double) aX, (double) aY, (double) aWidth, (double) aHeight);
00570     cairo_fill (mCairo);
00571     return NS_OK;
00572 }
00573 
00574 NS_IMETHODIMP
00575 nsCairoRenderingContext::InvertRect(const nsRect& aRect)
00576 {
00577     return InvertRect(aRect.x, aRect.y, aRect.width, aRect.height);
00578 }
00579 
00580 NS_IMETHODIMP
00581 nsCairoRenderingContext::InvertRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00582 {
00583     cairo_operator_t op = cairo_current_operator(mCairo);
00584     cairo_set_operator(mCairo, CAIRO_OPERATOR_XOR);
00585     nsresult rv = FillRect(aX, aY, aWidth, aHeight);
00586     cairo_set_operator(mCairo, op);
00587     return rv;
00588 }
00589 
00590 PRBool
00591 nsCairoRenderingContext::DoCairoDrawPolygon (const nsPoint aPoints[],
00592                                              PRInt32 aNumPoints)
00593 {
00594     if (aNumPoints < 2)
00595         return PR_FALSE;
00596 
00597     cairo_new_path (mCairo);
00598     cairo_move_to (mCairo, (double) aPoints[0].x, (double) aPoints[0].y);
00599     for (PRInt32 i = 1; i < aNumPoints; ++i) {
00600         cairo_line_to (mCairo, (double) aPoints[i].x, (double) aPoints[i].y);
00601     }
00602     return PR_TRUE;
00603 }
00604 
00605 
00606 NS_IMETHODIMP
00607 nsCairoRenderingContext::DrawPolyline(const nsPoint aPoints[],
00608                                       PRInt32 aNumPoints)
00609 {
00610     if (!DoCairoDrawPolygon(aPoints, aNumPoints))
00611         return NS_OK;
00612 
00613     cairo_stroke (mCairo);
00614     return NS_OK;
00615 }
00616 
00617 NS_IMETHODIMP
00618 nsCairoRenderingContext::DrawPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
00619 {
00620     // I'm assuming the difference between a polygon and a polyline is that this one
00621     // implicitly closes
00622 
00623     if (!DoCairoDrawPolygon(aPoints, aNumPoints))
00624         return NS_OK;
00625 
00626     cairo_close_path(mCairo);
00627     cairo_stroke(mCairo);
00628     return NS_OK;
00629 }
00630 
00631 NS_IMETHODIMP
00632 nsCairoRenderingContext::FillPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
00633 {
00634     if (!DoCairoDrawPolygon(aPoints, aNumPoints))
00635         return NS_OK;
00636 
00637     cairo_close_path(mCairo);
00638     cairo_fill(mCairo);
00639 
00640     return NS_OK;
00641 }
00642 
00643 void
00644 nsCairoRenderingContext::DoCairoDrawEllipse (double aX, double aY, double aWidth, double aHeight)
00645 {
00646     cairo_new_path (mCairo);
00647     cairo_move_to (mCairo, aX + aWidth/2.0, aY);
00648     cairo_curve_to (mCairo,
00649                     aX + aWidth, aY,
00650                     aX + aWidth, aY,
00651                     aX + aWidth, aY + aHeight/2.0);
00652     cairo_curve_to (mCairo,
00653                     aX + aWidth, aY + aHeight,
00654                     aX + aWidth, aY + aHeight,
00655                     aX + aWidth/2.0, aY);
00656     cairo_curve_to (mCairo,
00657                     aX, aY + aHeight,
00658                     aX, aY + aHeight,
00659                     aX, aY + aHeight/2.0);
00660     cairo_curve_to (mCairo,
00661                     aX, aY,
00662                     aX, aY,
00663                     aX + aWidth/2.0, aY);
00664 }
00665 
00666 NS_IMETHODIMP
00667 nsCairoRenderingContext::DrawEllipse(const nsRect& aRect)
00668 {
00669     return DrawEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
00670 }
00671 
00672 NS_IMETHODIMP
00673 nsCairoRenderingContext::DrawEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00674 {
00675     DoCairoDrawEllipse ((double) aX, (double) aY, (double) aWidth, (double) aHeight);
00676     cairo_stroke (mCairo);
00677 
00678     return NS_OK;
00679 }
00680 
00681 NS_IMETHODIMP
00682 nsCairoRenderingContext::FillEllipse(const nsRect& aRect)
00683 {
00684     return FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
00685 }
00686 
00687 NS_IMETHODIMP
00688 nsCairoRenderingContext::FillEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00689 {
00690     DoCairoDrawEllipse ((double) aX, (double) aY, (double) aWidth, (double) aHeight);
00691     cairo_fill (mCairo);
00692 
00693     return NS_OK;
00694 }
00695 
00696 void
00697 nsCairoRenderingContext::DoCairoDrawArc (double aX, double aY, double aWidth, double aHeight,
00698                                          double aStartAngle, double aEndAngle)
00699 {
00700     cairo_new_path (mCairo);
00701 
00702     // XXX - cairo can't do arcs with differing start and end radii.
00703     // I guess it's good that no code ever draws arcs.
00704 }
00705 
00706 NS_IMETHODIMP
00707 nsCairoRenderingContext::DrawArc(const nsRect& aRect,
00708                                  float aStartAngle, float aEndAngle)
00709 {
00710     return DrawArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle);
00711 }
00712 
00713 NS_IMETHODIMP
00714 nsCairoRenderingContext::DrawArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
00715                                  float aStartAngle, float aEndAngle)
00716 {
00717     NS_ERROR("not used");
00718     return NS_OK;
00719 }
00720 
00721 NS_IMETHODIMP
00722 nsCairoRenderingContext::FillArc(const nsRect& aRect,
00723                                  float aStartAngle, float aEndAngle)
00724 {
00725     return FillArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle);
00726 }
00727 
00728 NS_IMETHODIMP
00729 nsCairoRenderingContext::FillArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
00730                                  float aStartAngle, float aEndAngle)
00731 {
00732     NS_ERROR("not used");
00733     return NS_OK;
00734 }
00735 
00736 NS_IMETHODIMP
00737 nsCairoRenderingContext::CopyOffScreenBits(nsIDrawingSurface *aSrcSurf,
00738                                            PRInt32 aSrcX, PRInt32 aSrcY,
00739                                            const nsRect &aDestBounds,
00740                                            PRUint32 aCopyFlags)
00741 {
00742     nsCairoDrawingSurface *cds = (nsCairoDrawingSurface *) aSrcSurf;
00743 
00744     fprintf (stderr, "***** nsCairoRenderingContext::CopyOffScreenBits: [%p] %d,%d -> %d,%d %dx%d\n",
00745              aSrcSurf, aSrcX, aSrcY, aDestBounds.x, aDestBounds.y, aDestBounds.width, aDestBounds.height);
00746 
00747     PRUint32 srcWidth, srcHeight;
00748     cds->GetDimensions (&srcWidth, &srcHeight);
00749 
00750     double sx, sy, dx, dy, dw, dh;
00751     // figure out pixel positions
00752     sx = double(aSrcX);
00753     sy = double(aSrcY);
00754     dx = double(aDestBounds.x);
00755     dy = double(aDestBounds.y);
00756     dw = double(aDestBounds.width);
00757     dh = double(aDestBounds.height);
00758 
00759     if (aCopyFlags & NS_COPYBITS_XFORM_SOURCE_VALUES) {
00760         cairo_transform_point (mCairo, &sx, &sy);
00761     }
00762 
00763     if (aCopyFlags & NS_COPYBITS_XFORM_DEST_VALUES) {
00764         cairo_transform_point (mCairo, &dx, &dy);
00765         cairo_transform_distance (mCairo, &dw, &dh);
00766     }
00767 
00768     fprintf (stderr, "***** --> %g,%g [%d,%d] -> %g,%g %gx%g\n", sx, sy, srcWidth, srcHeight, dx, dy, dw, dh);
00769     cairo_save (mCairo);
00770     cairo_init_clip (mCairo);
00771 
00772     cairo_set_rgb_color (mCairo, 0.0, 0.0, 1.0);
00773     cairo_set_alpha (mCairo, 0.5);
00774 
00775     cairo_rectangle(mCairo, dx, dy, dw, dh);
00776     cairo_fill(mCairo);
00777 
00778     // args are in pixels!!
00779     cairo_identity_matrix (mCairo);
00780 
00781     // clip to target
00782     cairo_rectangle(mCairo, dx, dy, dw, dh);
00783     cairo_clip(mCairo);
00784 
00785     cairo_scale(mCairo, dw / double(srcWidth), dh / double(srcHeight));
00786     cairo_translate(mCairo, dx - sx, dy - sy);
00787 
00788 //    cairo_show_surface(mCairo, src, srcWidth, srcHeight);
00789 
00790     cairo_restore (mCairo);
00791 
00792     return NS_OK;
00793 }
00794 
00795 NS_IMETHODIMP
00796 nsCairoRenderingContext::RetrieveCurrentNativeGraphicData(PRUint32 * ngd)
00797 {
00798     NS_WARNING ("not implemented");
00799     return NS_OK;
00800 }
00801 
00802 NS_IMETHODIMP
00803 nsCairoRenderingContext::UseBackbuffer(PRBool* aUseBackbuffer)
00804 {
00805     *aUseBackbuffer = PR_FALSE;
00806     // *aUseBackbuffer = PR_TRUE;
00807     return NS_OK;
00808 }
00809 
00810 NS_IMETHODIMP
00811 nsCairoRenderingContext::GetBackbuffer(const nsRect &aRequestedSize,
00812                                        const nsRect &aMaxSize,
00813                                        PRBool aForBlending,
00814                                        nsIDrawingSurface* &aBackbuffer)
00815 {
00816     if (!mBackBufferSurface) {
00817 #if 1
00818         nsIDeviceContext *dc = mDeviceContext.get();
00819         mBackBufferSurface = new nsCairoDrawingSurface ();
00820         mBackBufferSurface->Init (NS_STATIC_CAST(nsCairoDeviceContext*, dc),
00821                                   aMaxSize.width, aMaxSize.height,
00822                                   PR_FALSE);
00823 #else
00824         mBackBufferSurface = mDrawingSurface;
00825 #endif
00826     }
00827 
00828     aBackbuffer = mBackBufferSurface;
00829     return NS_OK;
00830 }
00831 
00832 NS_IMETHODIMP
00833 nsCairoRenderingContext::ReleaseBackbuffer(void)
00834 {
00835     mBackBufferSurface = NULL;
00836     return NS_OK;
00837 //    NS_WARNING("ReleaseBackbuffer: not implemented");
00838 //    return NS_ERROR_NOT_IMPLEMENTED;
00839 }
00840 
00841 NS_IMETHODIMP
00842 nsCairoRenderingContext::DestroyCachedBackbuffer(void)
00843 {
00844     mBackBufferSurface = NULL;
00845     return NS_OK;
00846 //    NS_WARNING("DestroyCachedBackbuffer: not implemented");
00847 //    return NS_ERROR_NOT_IMPLEMENTED;
00848 }
00849 
00850 NS_IMETHODIMP
00851 nsCairoRenderingContext::DrawImage(imgIContainer *aImage,
00852                                    const nsRect &aSrcRect,
00853                                    const nsRect &aDestRect)
00854 {
00855     nsCOMPtr<gfxIImageFrame> iframe;
00856     aImage->GetCurrentFrame(getter_AddRefs(iframe));
00857     if (!iframe) return NS_ERROR_FAILURE;
00858 
00859     nsCOMPtr<nsIImage> img(do_GetInterface(iframe));
00860     if (!img) return NS_ERROR_FAILURE;
00861 
00862     // For Bug 87819
00863     // iframe may want image to start at different position, so adjust
00864     nsRect iframeRect;
00865     iframe->GetRect(iframeRect);
00866   
00867     // aSrcRect is always in appunits, it has
00868     // nothing to do with the current transform
00869     float app2dev;
00870     app2dev = mDeviceContext->AppUnitsToDevUnits();
00871     nsRect sr;
00872     sr.x = NSToIntRound(app2dev*aSrcRect.x);
00873     sr.y = NSToIntRound(app2dev*aSrcRect.y);
00874     sr.width = NSToIntRound(app2dev*aSrcRect.XMost()) - sr.x;
00875     sr.height = NSToIntRound(app2dev*aSrcRect.YMost()) - sr.y;
00876     
00877     nsRect dr = aDestRect;
00878     if (iframeRect != sr) {
00879         double xscale = double(aDestRect.width)/sr.width;
00880         double yscale = double(aDestRect.height)/sr.height;
00881         nsRect scaledUpIRect;
00882         scaledUpIRect.x = NSToCoordRound(iframeRect.x*xscale);
00883         scaledUpIRect.y = NSToCoordRound(iframeRect.y*yscale);
00884         scaledUpIRect.width = NSToCoordRound(iframeRect.XMost()*xscale) - scaledUpIRect.x;
00885         scaledUpIRect.height = NSToCoordRound(iframeRect.YMost()*yscale) - scaledUpIRect.y;
00886 
00887         sr.IntersectRect(sr, iframeRect);
00888         dr.IntersectRect(dr, scaledUpIRect);
00889     }
00890 
00891     return img->Draw(*this, mDrawingSurface, sr.x - iframeRect.x, sr.y - iframeRect.y,
00892                      sr.width, sr.height,
00893                      dr.x, dr.y, dr.width, dr.height);
00894 }
00895 
00896 NS_IMETHODIMP
00897 nsCairoRenderingContext::DrawTile(imgIContainer *aImage,
00898                                   nscoord aXOffset, nscoord aYOffset,
00899                                   const nsRect * aTargetRect)
00900 {
00901     nscoord width, height;
00902     aImage->GetWidth(&width);
00903     aImage->GetHeight(&height);
00904 
00905     if (width == 0 || height == 0)
00906         return PR_FALSE;
00907 
00908     nsCOMPtr<gfxIImageFrame> iframe;
00909     aImage->GetCurrentFrame(getter_AddRefs(iframe));
00910     if (!iframe) return NS_ERROR_FAILURE;
00911 
00912     nsCOMPtr<nsIImage> img(do_GetInterface(iframe));
00913     if (!img) return NS_ERROR_FAILURE;
00914 
00915     // For Bug 87819
00916     // iframe may want image to start at different position, so adjust
00917     nsRect iframeRect;
00918     iframe->GetRect(iframeRect);
00919   
00920     // offsets are always in appunits, they have
00921     // nothing to do with the current transform
00922     float app2dev;
00923     app2dev = mDeviceContext->AppUnitsToDevUnits();
00924     nsPoint s;
00925     s.x = NSToIntRound(app2dev*aXOffset);
00926     s.y = NSToIntRound(app2dev*aYOffset);
00927     
00928     PRInt32 padx = width - iframeRect.width;
00929     PRInt32 pady = height - iframeRect.height;
00930 
00931     return img->DrawTile(*this, mDrawingSurface, s.x - iframeRect.x, s.y - iframeRect.y,
00932                          padx, pady, *aTargetRect);
00933 }
00934 
00935 //
00936 // text junk
00937 //
00938 
00939 NS_IMETHODIMP
00940 nsCairoRenderingContext::SetFont(const nsFont& aFont, nsIAtom* aLangGroup)
00941 {
00942     nsCOMPtr<nsIFontMetrics> newMetrics;
00943     nsresult rv = mDeviceContext->GetMetricsFor(aFont, aLangGroup, *getter_AddRefs(newMetrics));
00944     mFontMetrics = NS_REINTERPRET_CAST(nsICairoFontMetrics*, newMetrics.get());
00945     return NS_OK;
00946 }
00947 
00948 NS_IMETHODIMP
00949 nsCairoRenderingContext::SetFont(nsIFontMetrics *aFontMetrics)
00950 {
00951     mFontMetrics = NS_STATIC_CAST(nsICairoFontMetrics*, aFontMetrics);
00952     return NS_OK;
00953 }
00954 
00955 NS_IMETHODIMP
00956 nsCairoRenderingContext::GetFontMetrics(nsIFontMetrics *&aFontMetrics)
00957 {
00958     aFontMetrics = mFontMetrics;
00959     NS_IF_ADDREF(aFontMetrics);
00960     return NS_OK;
00961 }
00962 
00963 NS_IMETHODIMP
00964 nsCairoRenderingContext::GetWidth(char aC, nscoord &aWidth)
00965 {
00966     if (aC == ' ' && mFontMetrics)
00967         return mFontMetrics->GetSpaceWidth(aWidth);
00968 
00969     return GetWidth(&aC, 1, aWidth);
00970 }
00971 
00972 NS_IMETHODIMP
00973 nsCairoRenderingContext::GetWidth(PRUnichar aC, nscoord &aWidth,
00974                                PRInt32 *aFontID)
00975 {
00976     return GetWidth(&aC, 1, aWidth, aFontID);
00977 }
00978 
00979 NS_IMETHODIMP
00980 nsCairoRenderingContext::GetWidth(const nsString& aString, nscoord &aWidth,
00981                                PRInt32 *aFontID)
00982 {
00983     return GetWidth(aString.get(), aString.Length(), aWidth, aFontID);
00984 }
00985 
00986 NS_IMETHODIMP
00987 nsCairoRenderingContext::GetWidth(const char* aString, nscoord& aWidth)
00988 {
00989     return GetWidth(aString, strlen(aString), aWidth);
00990 }
00991 
00992 NS_IMETHODIMP
00993 nsCairoRenderingContext::GetWidth(const char* aString, PRUint32 aLength,
00994                                   nscoord& aWidth)
00995 {
00996     if (aLength == 0) {
00997         aWidth = 0;
00998         return NS_OK;
00999     }
01000 
01001     return mFontMetrics->GetWidth(aString, aLength, aWidth);
01002 }
01003 
01004 NS_IMETHODIMP
01005 nsCairoRenderingContext::GetWidth(const PRUnichar *aString, PRUint32 aLength,
01006                                   nscoord &aWidth, PRInt32 *aFontID)
01007 {
01008     if (aLength == 0) {
01009         aWidth = 0;
01010         return NS_OK;
01011     }
01012 
01013     return mFontMetrics->GetWidth(aString, aLength, aWidth, aFontID);
01014 }
01015 
01016 NS_IMETHODIMP
01017 nsCairoRenderingContext::GetTextDimensions(const char* aString, PRUint32 aLength,
01018                                            nsTextDimensions& aDimensions)
01019 {
01020   mFontMetrics->GetMaxAscent(aDimensions.ascent);
01021   mFontMetrics->GetMaxDescent(aDimensions.descent);
01022   return GetWidth(aString, aLength, aDimensions.width);
01023 }
01024 
01025 NS_IMETHODIMP
01026 nsCairoRenderingContext::GetTextDimensions(const PRUnichar* aString,
01027                                            PRUint32 aLength,
01028                                            nsTextDimensions& aDimensions,
01029                                            PRInt32* aFontID)
01030 {
01031   mFontMetrics->GetMaxAscent(aDimensions.ascent);
01032   mFontMetrics->GetMaxDescent(aDimensions.descent);
01033   return GetWidth(aString, aLength, aDimensions.width, aFontID);
01034 }
01035 
01036 #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)
01037 NS_IMETHODIMP
01038 nsCairoRenderingContext::GetTextDimensions(const char*       aString,
01039                                            PRInt32           aLength,
01040                                            PRInt32           aAvailWidth,
01041                                            PRInt32*          aBreaks,
01042                                            PRInt32           aNumBreaks,
01043                                            nsTextDimensions& aDimensions,
01044                                            PRInt32&          aNumCharsFit,
01045                                            nsTextDimensions& aLastWordDimensions,
01046                                            PRInt32*          aFontID)
01047 {
01048     return NS_ERROR_NOT_IMPLEMENTED;
01049 }
01050 
01051 NS_IMETHODIMP
01052 nsCairoRenderingContext::GetTextDimensions(const PRUnichar*  aString,
01053                                            PRInt32           aLength,
01054                                            PRInt32           aAvailWidth,
01055                                            PRInt32*          aBreaks,
01056                                            PRInt32           aNumBreaks,
01057                                            nsTextDimensions& aDimensions,
01058                                            PRInt32&          aNumCharsFit,
01059                                            nsTextDimensions& aLastWordDimensions,
01060                                            PRInt32*          aFontID)
01061 {
01062     return NS_ERROR_NOT_IMPLEMENTED;
01063 }
01064 #endif
01065 
01066 #ifdef MOZ_MATHML
01067 NS_IMETHODIMP 
01068 nsCairoRenderingContext::GetBoundingMetrics(const char*        aString,
01069                                             PRUint32           aLength,
01070                                             nsBoundingMetrics& aBoundingMetrics)
01071 {
01072   return NS_OK;
01073 }
01074 
01075 NS_IMETHODIMP
01076 nsCairoRenderingContext::GetBoundingMetrics(const PRUnichar*   aString,
01077                                          PRUint32           aLength,
01078                                          nsBoundingMetrics& aBoundingMetrics,
01079                                          PRInt32*           aFontID)
01080 {
01081     return NS_OK;
01082 }
01083 #endif // MOZ_MATHML
01084 
01085 NS_IMETHODIMP
01086 nsCairoRenderingContext::DrawString(const char *aString, PRUint32 aLength,
01087                                     nscoord aX, nscoord aY,
01088                                     const nscoord* aSpacing)
01089 {
01090     return mFontMetrics->DrawString(aString, aLength, aX, aY, aSpacing,
01091                                     this, mDrawingSurface);
01092 
01093 #if 0
01094     NS_WARNING("DrawString 1");
01095     cairo_move_to(mCairo, double(aX), double(aY));
01096     cairo_new_path (mCairo);
01097     cairo_text_path (mCairo, (const unsigned char *) aString);
01098     cairo_fill (mCairo);
01099     cairo_move_to(mCairo, -double(aX), -double(aY));
01100 #endif
01101     return NS_OK;
01102 }
01103 
01104 NS_IMETHODIMP
01105 nsCairoRenderingContext::DrawString(const PRUnichar *aString, PRUint32 aLength,
01106                                     nscoord aX, nscoord aY,
01107                                     PRInt32 aFontID,
01108                                     const nscoord* aSpacing)
01109 {
01110     return mFontMetrics->DrawString(aString, aLength, aX, aY, aFontID,
01111                                     aSpacing, this, mDrawingSurface);
01112 }
01113 
01114 NS_IMETHODIMP
01115 nsCairoRenderingContext::DrawString(const nsString& aString,
01116                                     nscoord aX, nscoord aY,
01117                                     PRInt32 aFontID,
01118                                     const nscoord* aSpacing)
01119 {
01120     return DrawString(aString.get(), aString.Length(),
01121                       aX, aY, aFontID, aSpacing);
01122 }
01123 
01124 NS_IMETHODIMP
01125 nsCairoRenderingContext::GetClusterInfo(const PRUnichar *aText,
01126                                        PRUint32 aLength,
01127                                        PRUint8 *aClusterStarts)
01128 {
01129   return NS_ERROR_NOT_IMPLEMENTED;
01130 }
01131 
01132 PRInt32
01133 nsCairoRenderingContext::GetPosition(const PRUnichar *aText,
01134                                     PRUint32 aLength,
01135                                     nsPoint aPt)
01136 {
01137   return -1;
01138 }
01139 
01140 NS_IMETHODIMP
01141 nsCairoRenderingContext::GetRangeWidth(const PRUnichar *aText,
01142                                       PRUint32 aLength,
01143                                       PRUint32 aStart,
01144                                       PRUint32 aEnd,
01145                                       PRUint32 &aWidth)
01146 {
01147   return NS_ERROR_NOT_IMPLEMENTED;
01148 }
01149 
01150 NS_IMETHODIMP
01151 nsCairoRenderingContext::GetRangeWidth(const char *aText,
01152                                       PRUint32 aLength,
01153                                       PRUint32 aStart,
01154                                       PRUint32 aEnd,
01155                                       PRUint32 &aWidth)
01156 {
01157   return NS_ERROR_NOT_IMPLEMENTED;
01158 }
01159 
01160 NS_IMETHODIMP
01161 nsCairoRenderingContext::RenderEPS(const nsRect& aRect, FILE *aDataFile)
01162 {
01163     return NS_ERROR_NOT_IMPLEMENTED;
01164 }