Back to index

lightning-sunbird  0.9+nobinonly
nsSVGCairoCanvas.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 the Mozilla SVG Cairo Renderer project.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2004
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Parts of this file contain code derived from the following files(s)
00022  * of the Mozilla SVG project (these parts are Copyright (C) by their
00023  * respective copyright-holders):
00024  *    layout/svg/renderer/src/libart/nsSVGLibartCanvas.cpp
00025  *
00026  * Contributor(s):
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsCOMPtr.h"
00043 #include "nsSVGCairoCanvas.h"
00044 #include "nsISVGCairoCanvas.h"
00045 #include "nsIDOMSVGMatrix.h"
00046 #include "nsIRenderingContext.h"
00047 #include "nsIDeviceContext.h"
00048 #include "nsTransform2D.h"
00049 #include "nsPresContext.h"
00050 #include "nsRect.h"
00051 #include "nsISVGCairoSurface.h"
00052 #include "cairo.h"
00053 
00054 #ifdef MOZ_X11
00055 extern "C" {
00056 #include <cairo-xlib.h>
00057 }
00058 #endif
00059 
00060 #include "nsIImage.h"
00061 #include "nsIComponentManager.h"
00062 #include "imgIContainer.h"
00063 #include "gfxIImageFrame.h"
00064 #include "nsIInterfaceRequestor.h"
00065 #include "nsIInterfaceRequestorUtils.h"
00066 
00067 #ifdef XP_MACOSX
00068 #include "nsIDrawingSurfaceMac.h"
00069 extern "C" {
00070 #include <cairo-quartz.h>
00071 }
00072 #elif defined(MOZ_WIDGET_XLIB)
00073 #include "nsDrawingSurfaceXlib.h"
00074 #elif defined(MOZ_WIDGET_QT)
00075 #undef CursorShape
00076 #include "nsDrawingSurfaceQt.h"
00077 #elif defined(XP_WIN)
00078 #include "nsDrawingSurfaceWin.h"
00079 extern "C" {
00080 #include <cairo-win32.h>
00081 }
00082 #elif defined(XP_OS2)
00083 #define INCL_WINWINDOWMGR
00084 #define INCL_GPIBITMAPS
00085 #include <os2.h>
00086 #include "nsDrawingSurfaceOS2.h"
00087 extern "C" {
00088 #include <cairo-os2.h>
00089 }
00090 #else
00091 #include "nsRenderingContextGTK.h"
00092 #include <gdk/gdkx.h>
00093 #endif
00094 
00099 
00100 
00103 class nsSVGCairoCanvas : public nsISVGCairoCanvas
00104 {
00105 public:
00106   nsSVGCairoCanvas();
00107   ~nsSVGCairoCanvas();
00108   nsresult Init(nsIRenderingContext* ctx, nsPresContext* presContext,
00109                 const nsRect & dirtyRect);
00110     
00111   // nsISupports interface:
00112   NS_DECL_ISUPPORTS
00113     
00114   // nsISVGRendererCanvas interface:
00115   NS_DECL_NSISVGRENDERERCANVAS
00116 
00117   // nsISVGCairoCanvas interface:
00118   NS_IMETHOD_(cairo_t*) GetContext() { return mCR; }
00119   NS_IMETHOD AdjustMatrixForInitialTransform(cairo_matrix_t* aMatrix);
00120     
00121 private:
00122   nsCOMPtr<nsIRenderingContext> mMozContext;
00123   nsCOMPtr<nsPresContext> mPresContext;
00124   cairo_t *mCR;
00125   PRUint32 mWidth, mHeight;
00126   cairo_matrix_t mInitialTransform;
00127   nsVoidArray mContextStack;
00128 
00129 #ifdef XP_MACOSX
00130   nsCOMPtr<nsIDrawingSurfaceMac> mSurface;
00131   CGContextRef mQuartzRef;
00132 #endif
00133 
00134   // image fallback data
00135   nsSize mSrcSizeTwips;
00136   nsRect mDestRectScaledTwips;
00137   nsCOMPtr<imgIContainer> mContainer;
00138   nsCOMPtr<gfxIImageFrame> mBuffer;
00139   PRUint8 *mData;
00140 
00141   PRUint16 mRenderMode;
00142   PRPackedBool mOwnsCR;
00143 };
00144 
00145 
00148 //----------------------------------------------------------------------
00149 // implementation:
00150 
00151 nsSVGCairoCanvas::nsSVGCairoCanvas() : mData(nsnull)
00152 {
00153   mOwnsCR = PR_FALSE;
00154 }
00155 
00156 nsSVGCairoCanvas::~nsSVGCairoCanvas()
00157 {
00158   mMozContext = nsnull;
00159   mPresContext = nsnull;
00160 
00161   if (mOwnsCR) {
00162     cairo_destroy(mCR);
00163   }
00164 
00165   if (mData) {
00166     free(mData);
00167   }
00168 }
00169 
00170 nsresult
00171 nsSVGCairoCanvas::AdjustMatrixForInitialTransform(cairo_matrix_t* aMatrix)
00172 {
00173   if (mContextStack.Count() == 0)
00174     cairo_matrix_multiply(aMatrix, aMatrix, &mInitialTransform);
00175   return NS_OK;
00176 }
00177 
00178 
00179 nsresult
00180 nsSVGCairoCanvas::Init(nsIRenderingContext *ctx,
00181                        nsPresContext *presContext,
00182                        const nsRect &dirtyRect)
00183 {
00184   mPresContext = presContext;
00185   mMozContext = ctx;
00186   NS_ASSERTION(mMozContext, "empty rendering context");
00187 
00188   mRenderMode = SVG_RENDER_MODE_NORMAL;
00189 
00190 #if defined(MOZ_ENABLE_CAIRO_GFX)
00191   mOwnsCR = PR_FALSE;
00192   void* data;
00193   ctx->RetrieveCurrentNativeGraphicData(&data);
00194   mCR = (cairo_t*)data;
00195   cairo_get_matrix(mCR, &mInitialTranslation);
00196   return NS_OK;
00197 
00198 #else // !MOZ_ENABLE_CAIRO_GFX
00199   cairo_surface_t* cairoSurf = nsnull;
00200 #if defined(XP_MACOSX)
00201   nsIDrawingSurface *surface;
00202   ctx->GetDrawingSurface(&surface);
00203   mSurface = do_QueryInterface(surface);
00204   surface->GetDimensions(&mWidth, &mHeight);
00205   mQuartzRef = mSurface->StartQuartzDrawing();
00206 
00207   CGrafPtr port;
00208   mSurface->GetGrafPtr(&port);
00209   Rect portRect;
00210   ::GetPortBounds(port, &portRect);
00211 
00212 #ifdef DEBUG_tor
00213   fprintf(stderr, "CAIRO: DS=0x%08x port x=%d y=%d w=%d h=%d\n",
00214           mSurface.get(), portRect.right, portRect.top,
00215           portRect.right - portRect.left, portRect.bottom - portRect.top);
00216 #endif
00217 
00218   ::SyncCGContextOriginWithPort(mQuartzRef, port);
00219   cairoSurf = cairo_quartz_surface_create(mQuartzRef,
00220                                           portRect.right - portRect.left,
00221                                           portRect.bottom - portRect.top);
00222 
00223 #elif defined(MOZ_WIDGET_XLIB)
00224   nsIDrawingSurfaceXlib* surface;
00225   ctx->GetDrawingSurface((nsIDrawingSurface**)&surface);
00226 
00227   surface->GetDimensions(&mWidth, &mHeight);
00228 
00229   XlibRgbHandle* handle;
00230   surface->GetXlibRgbHandle(handle);
00231   Drawable drawable = surface->GetDrawable();
00232   cairoSurf = cairo_xlib_surface_create(xxlib_rgb_get_display(handle),
00233                                         drawable,
00234                                         xxlib_rgb_get_visual(handle),
00235                                         mWidth, mHeight);
00236 #elif defined(MOZ_WIDGET_QT)
00237   nsDrawingSurfaceQt* surface;
00238   ctx->GetDrawingSurface((nsIDrawingSurface**)&surface);
00239   surface->GetDimensions(&mWidth, &mHeight);
00240 
00241   QPaintDevice* dev = surface->GetPaintDevice();
00242 
00243   cairoSurf = cairo_xlib_surface_create(dev->x11Display(),
00244                                         dev->handle(),
00245                                         (Visual*)dev->x11Visual(),
00246                                         mWidth, mHeight);
00247 #elif defined(XP_WIN)
00248   nsDrawingSurfaceWin *surface;
00249   ctx->GetDrawingSurface((nsIDrawingSurface**)&surface);
00250 
00251   PRInt32 canRaster;
00252   surface->GetTECHNOLOGY(&canRaster);
00253   if (canRaster != DT_RASPRINTER) {
00254     HDC hdc;
00255     surface->GetDimensions(&mWidth, &mHeight);
00256     surface->GetDC(&hdc);
00257     cairoSurf = cairo_win32_surface_create(hdc);
00258   }
00259 #elif defined(XP_OS2)
00260   nsDrawingSurfaceOS2 *surface; /* to get a HPS from this */
00261   nsOffscreenSurface *surface2; /* to get a HDC from this */
00262   ctx->GetDrawingSurface((nsIDrawingSurface**)&surface);
00263   ctx->GetDrawingSurface((nsIDrawingSurface**)&surface2);
00264 
00265   HPS hps = surface->GetPS();
00266   HDC hdc = surface2->GetDC();
00267   LONG caps;
00268   if (DevQueryCaps(hdc, CAPS_TECHNOLOGY, 1L, &caps) &&
00269       caps == CAPS_TECH_RASTER_DISPLAY && hps) {
00270     /* only the display with an existing presentationn handle *
00271      * can handle content drawn by cairo                      */
00272     surface->GetDimensions(&mWidth, &mHeight);
00273     cairoSurf = cairo_os2_surface_create(hps, mWidth, mHeight);
00274     cairo_surface_mark_dirty(cairoSurf);
00275   }
00276 #elif defined(MOZ_ENABLE_GTK2) || defined(MOZ_ENABLE_GTK)
00277   nsDrawingSurfaceGTK *surface;
00278   ctx->GetDrawingSurface((nsIDrawingSurface**)&surface);
00279   if (surface) {
00280     surface->GetSize(&mWidth, &mHeight);
00281     GdkDrawable *drawable = surface->GetDrawable();
00282     GdkVisual *visual = gdk_window_get_visual(drawable);
00283     cairoSurf = cairo_xlib_surface_create(GDK_WINDOW_XDISPLAY(drawable),
00284                                           GDK_WINDOW_XWINDOW(drawable),
00285                                           GDK_VISUAL_XVISUAL(visual),
00286                                           mWidth, mHeight);
00287   }
00288 #endif
00289 
00290   // Account for the transform in the rendering context. We set dx,dy
00291   // to the translation required in the cairo context that will
00292   // ensure the cairo context's origin coincides with the top-left
00293   // of the area we need to draw.
00294   nsTransform2D* xform;
00295   mMozContext->GetCurrentTransform(xform);
00296   float dx, dy;
00297   xform->GetTranslation(&dx, &dy);
00298   
00299   if (!cairoSurf) {
00300     /* printing or some platform we don't know about yet - use an image */
00301     float scaledTwipsPerPx = presContext->ScaledPixelsToTwips();
00302     mDestRectScaledTwips = dirtyRect;
00303     mDestRectScaledTwips.ScaleRoundOut(scaledTwipsPerPx);
00304     
00305     float twipsPerPx = presContext->PixelsToTwips();
00306     nsRect r = dirtyRect;
00307     r.ScaleRoundOut(twipsPerPx);
00308     mSrcSizeTwips = r.Size();
00309   
00310     mWidth = dirtyRect.width;
00311     mHeight = dirtyRect.height;
00312 
00313     mContainer = do_CreateInstance("@mozilla.org/image/container;1");
00314     mContainer->Init(mWidth, mHeight, nsnull);
00315     
00316     mBuffer = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
00317 #if defined(XP_WIN) || defined(XP_OS2)
00318     mBuffer->Init(0, 0, mWidth, mHeight, gfxIFormats::BGR, 24);
00319 #else
00320     mBuffer->Init(0, 0, mWidth, mHeight, gfxIFormats::RGB_A8, 24);
00321 #endif
00322     mContainer->AppendFrame(mBuffer);
00323 
00324     mData = (PRUint8 *)calloc(4 * mWidth * mHeight, 1);
00325     if (!mData)
00326       return NS_ERROR_FAILURE;
00327 
00328     cairoSurf = cairo_image_surface_create_for_data(mData, CAIRO_FORMAT_ARGB32,
00329                                                     mWidth, mHeight, 4 * mWidth);
00330     
00331     // Ensure that dirtyRect.TopLeft() is translated to (0,0) in the surface                            
00332     dx = -dirtyRect.x;
00333     dy = -dirtyRect.y;
00334   }
00335 
00336   mOwnsCR = PR_TRUE;
00337   mCR = cairo_create(cairoSurf);
00338   // Destroy our reference to the surface; the cairo_t will continue to hold a reference to it
00339   cairo_surface_destroy(cairoSurf);
00340 
00341   cairo_translate(mCR, dx, dy);
00342   cairo_get_matrix(mCR, &mInitialTransform);
00343 
00344 #if defined(DEBUG_tor) || defined(DEBUG_roc)
00345   fprintf(stderr, "cairo translate: %f %f\n", dx, dy);
00346 
00347   fprintf(stderr, "cairo dirty: %d %d %d %d\n",
00348                   dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
00349 #endif
00350 
00351   // clip to dirtyRect
00352   cairo_new_path(mCR);
00353   cairo_rectangle(mCR,
00354                   dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
00355   cairo_clip(mCR);
00356   cairo_new_path(mCR);
00357 
00358   return NS_OK;
00359 #endif // !MOZ_ENABLE_CAIRO_GFX
00360 }
00361 
00362 nsresult
00363 NS_NewSVGCairoCanvas(nsISVGRendererCanvas **result,
00364                      nsIRenderingContext *ctx,
00365                      nsPresContext *presContext,
00366                      const nsRect & dirtyRect)
00367 {
00368   nsSVGCairoCanvas* pg = new nsSVGCairoCanvas();
00369   if (!pg) return NS_ERROR_OUT_OF_MEMORY;
00370 
00371   NS_ADDREF(pg);
00372 
00373   nsresult rv = pg->Init(ctx, presContext, dirtyRect);
00374 
00375   if (NS_FAILED(rv)) {
00376     NS_RELEASE(pg);
00377     return rv;
00378   }
00379   
00380   *result = pg;
00381   return rv;
00382 }
00383 
00384 //----------------------------------------------------------------------
00385 // nsISupports methods:
00386 
00387 NS_IMPL_ADDREF(nsSVGCairoCanvas)
00388 NS_IMPL_RELEASE(nsSVGCairoCanvas)
00389 
00390 NS_INTERFACE_MAP_BEGIN(nsSVGCairoCanvas)
00391 NS_INTERFACE_MAP_ENTRY(nsISVGRendererCanvas)
00392 NS_INTERFACE_MAP_ENTRY(nsISVGCairoCanvas)
00393 NS_INTERFACE_MAP_ENTRY(nsISupports)
00394 NS_INTERFACE_MAP_END
00395 
00396 //----------------------------------------------------------------------
00397 // nsISVGRendererCanvas methods:
00398 
00400 NS_IMETHODIMP
00401 nsSVGCairoCanvas::LockRenderingContext(const nsRect & rect,
00402                                        nsIRenderingContext **_retval)
00403 {
00404   // XXX do we need to flush?
00405   Flush();
00406   
00407   *_retval = mMozContext;
00408   NS_ADDREF(*_retval);
00409   return NS_OK;
00410 }
00411 
00413 NS_IMETHODIMP 
00414 nsSVGCairoCanvas::UnlockRenderingContext()
00415 {
00416   return NS_OK;
00417 }
00418 
00420 NS_IMETHODIMP
00421 nsSVGCairoCanvas::GetPresContext(nsPresContext **_retval)
00422 {
00423   *_retval = mPresContext;
00424   NS_IF_ADDREF(*_retval);
00425   return NS_OK;
00426 }
00427 
00429 NS_IMETHODIMP
00430 nsSVGCairoCanvas::Clear(nscolor color)
00431 {
00432   cairo_set_source_rgba(mCR,
00433                         NS_GET_R(color)/255.0,
00434                         NS_GET_G(color)/255.0,
00435                         NS_GET_B(color)/255.0,
00436                         NS_GET_A(color)/255.0);
00437   cairo_rectangle(mCR, 0, 0, mWidth, mHeight);
00438   cairo_fill(mCR);
00439 
00440   return NS_OK;
00441 }
00442 
00443 // XXX this is copied from nsCanvasRenderingContext2D, which sucks,
00444 // but all this is going to go away in 1.9 so making it shareable is
00445 // a bit of unnecessary pain
00446 static nsresult CopyCairoImageToIImage(PRUint8* aData, PRInt32 aWidth, PRInt32 aHeight,
00447     gfxIImageFrame* aImage)
00448 {
00449   PRUint8 *alphaBits, *rgbBits;
00450   PRUint32 alphaLen, rgbLen;
00451   PRUint32 alphaStride, rgbStride;
00452 
00453   nsresult rv = aImage->LockImageData();
00454   if (NS_FAILED(rv)) {
00455     return rv;
00456   }
00457 
00458 #ifndef XP_WIN
00459   rv = aImage->LockAlphaData();
00460   if (NS_FAILED(rv)) {
00461     aImage->UnlockImageData();
00462     return rv;
00463   }
00464 #endif
00465 
00466   rv = aImage->GetImageBytesPerRow(&rgbStride);
00467   rv |= aImage->GetImageData(&rgbBits, &rgbLen);
00468 
00469 #ifndef XP_WIN
00470   rv |= aImage->GetAlphaBytesPerRow(&alphaStride);
00471   rv |= aImage->GetAlphaData(&alphaBits, &alphaLen);
00472 #endif
00473 
00474   if (NS_FAILED(rv)) {
00475     aImage->UnlockImageData();
00476 #ifndef XP_WIN
00477     aImage->UnlockAlphaData();
00478 #endif
00479     return rv;
00480   }
00481 
00482   nsCOMPtr<nsIImage> img(do_GetInterface(aImage));
00483   PRBool topToBottom = img->GetIsRowOrderTopToBottom();
00484 
00485   for (PRUint32 j = 0; j < (PRUint32) aHeight; j++) {
00486     PRUint8 *inrow = (PRUint8*)(aData + (aWidth * 4 * j));
00487 
00488     PRUint32 rowIndex;
00489     if (topToBottom)
00490       rowIndex = j;
00491     else
00492       rowIndex = aHeight - j - 1;
00493 
00494     PRUint8 *outrowrgb = rgbBits + (rgbStride * rowIndex);
00495 #ifndef XP_WIN
00496     PRUint8 *outrowalpha = alphaBits + (alphaStride * rowIndex);
00497 #endif
00498 
00499     for (PRUint32 i = 0; i < (PRUint32) aWidth; i++) {
00500 #ifdef IS_LITTLE_ENDIAN
00501       PRUint8 b = *inrow++;
00502       PRUint8 g = *inrow++;
00503       PRUint8 r = *inrow++;
00504       PRUint8 a = *inrow++;
00505 #else
00506       PRUint8 a = *inrow++;
00507       PRUint8 r = *inrow++;
00508       PRUint8 g = *inrow++;
00509       PRUint8 b = *inrow++;
00510 #endif
00511       // now recover the real bgra from the cairo
00512       // premultiplied values
00513       if (a == 0) {
00514         // can't do much for us if we're at 0
00515         b = g = r = 0;
00516       } else {
00517         // the (a/2) factor is a bias similar to one cairo applies
00518         // when premultiplying
00519         b = (b * 255 + a / 2) / a;
00520         g = (g * 255 + a / 2) / a;
00521         r = (r * 255 + a / 2) / a;
00522       }
00523 
00524 #ifndef XP_WIN
00525       *outrowalpha++ = a;
00526 #endif
00527 
00528 #ifdef XP_MACOSX
00529       // On the mac, RGB_A8 is really RGBX_A8
00530       *outrowrgb++ = 0;
00531 #endif
00532 
00533 #if defined(XP_WIN) || defined(XP_OS2)
00534       // On windows, RGB_A8 is really BGR_A8.
00535       // in fact, BGR_A8 is also BGR_A8.
00536       // Preblend with white since win32 printing can't deal with RGBA
00537       MOZ_BLEND(*outrowrgb++, 255, b, (unsigned)a);
00538       MOZ_BLEND(*outrowrgb++, 255, g, (unsigned)a);
00539       MOZ_BLEND(*outrowrgb++, 255, r, (unsigned)a);
00540 #else
00541       *outrowrgb++ = r;
00542       *outrowrgb++ = g;
00543       *outrowrgb++ = b;
00544 #endif
00545     }
00546   }
00547 
00548 #ifndef XP_WIN
00549   rv = aImage->UnlockAlphaData();
00550 #else
00551   rv = NS_OK;
00552 #endif
00553 
00554   rv |= aImage->UnlockImageData();
00555   if (NS_FAILED(rv))
00556     return rv;
00557 
00558   nsRect r(0, 0, aWidth, aHeight);
00559   img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
00560   return NS_OK;
00561 }
00562 
00564 NS_IMETHODIMP
00565 nsSVGCairoCanvas::Flush()
00566 {
00567 #ifdef XP_MACOSX
00568   if (mSurface)
00569     mSurface->EndQuartzDrawing(mQuartzRef);
00570 #endif
00571 
00572   if (mData) {
00573     // ensure that everything's flushed
00574     cairo_destroy(mCR);
00575     mCR = nsnull;
00576     mOwnsCR = PR_FALSE;
00577     
00578     nsCOMPtr<nsIDeviceContext> ctx;
00579     mMozContext->GetDeviceContext(*getter_AddRefs(ctx));
00580 
00581     nsCOMPtr<nsIInterfaceRequestor> ireq(do_QueryInterface(mBuffer));
00582     if (ireq) {
00583       nsCOMPtr<gfxIImageFrame> img(do_GetInterface(ireq));
00584       CopyCairoImageToIImage(mData, mWidth, mHeight, img);
00585     }
00586     mContainer->DecodingComplete();
00587 
00588     nsRect src(0, 0, mSrcSizeTwips.width, mSrcSizeTwips.height);
00589     mMozContext->DrawImage(mContainer, src, mDestRectScaledTwips);
00590   }
00591 
00592   return NS_OK;
00593 }
00594 
00595 NS_IMETHODIMP
00596 nsSVGCairoCanvas::GetRenderMode(PRUint16 *aMode)
00597 {
00598   *aMode = mRenderMode;
00599   return NS_OK;
00600 }
00601 
00602 NS_IMETHODIMP
00603 nsSVGCairoCanvas::SetRenderMode(PRUint16 mode)
00604 {
00605   if (mRenderMode == SVG_RENDER_MODE_CLIP && mode == SVG_RENDER_MODE_NORMAL)
00606     cairo_clip(mCR);
00607   mRenderMode = mode;
00608   return NS_OK;
00609 }
00610 
00612 NS_IMETHODIMP
00613 nsSVGCairoCanvas::PushClip()
00614 {
00615   cairo_save(mCR);
00616   return NS_OK;
00617 }
00618 
00620 NS_IMETHODIMP
00621 nsSVGCairoCanvas::PopClip()
00622 {
00623   cairo_restore(mCR);
00624   return NS_OK;
00625 }
00626 
00629 NS_IMETHODIMP
00630 nsSVGCairoCanvas::SetClipRect(nsIDOMSVGMatrix *aCTM, float aX, float aY,
00631                               float aWidth, float aHeight)
00632 {
00633   if (!aCTM)
00634     return NS_ERROR_FAILURE;
00635 
00636   float m[6];
00637   float val;
00638   aCTM->GetA(&val);
00639   m[0] = val;
00640     
00641   aCTM->GetB(&val);
00642   m[1] = val;
00643     
00644   aCTM->GetC(&val);  
00645   m[2] = val;  
00646     
00647   aCTM->GetD(&val);  
00648   m[3] = val;  
00649   
00650   aCTM->GetE(&val);
00651   m[4] = val;
00652   
00653   aCTM->GetF(&val);
00654   m[5] = val;
00655 
00656   cairo_matrix_t oldMatrix;
00657   cairo_get_matrix(mCR, &oldMatrix);
00658   cairo_matrix_t matrix = {m[0], m[1], m[2], m[3], m[4], m[5]};
00659   cairo_matrix_t inverse = matrix;
00660   if (cairo_matrix_invert(&inverse))
00661     return NS_ERROR_FAILURE;
00662   cairo_transform(mCR, &matrix);
00663 
00664   cairo_new_path(mCR);
00665   cairo_rectangle(mCR, aX, aY, aWidth, aHeight);
00666   cairo_clip(mCR);
00667   cairo_new_path(mCR);
00668 
00669   cairo_set_matrix(mCR, &oldMatrix);
00670 
00671   return NS_OK;
00672 }
00673 
00675 NS_IMETHODIMP
00676 nsSVGCairoCanvas::PushSurface(nsISVGRendererSurface *aSurface)
00677 {
00678   nsCOMPtr<nsISVGCairoSurface> cairoSurface = do_QueryInterface(aSurface);
00679   if (!cairoSurface)
00680     return NS_ERROR_FAILURE;
00681 
00682   cairo_t* oldCR = mCR;
00683   mContextStack.AppendElement(NS_STATIC_CAST(void*, oldCR));
00684 
00685   mCR = cairo_create(cairoSurface->GetSurface());
00686   // XXX Copy state over from oldCR?
00687 
00688   return NS_OK;
00689 }
00690 
00692 NS_IMETHODIMP
00693 nsSVGCairoCanvas::PopSurface()
00694 {
00695   PRUint32 count = mContextStack.Count();
00696   if (count != 0) {
00697     cairo_destroy(mCR);
00698     mCR = NS_STATIC_CAST(cairo_t*, mContextStack[count - 1]);
00699     mContextStack.RemoveElementAt(count - 1);
00700   }
00701 
00702   return NS_OK;
00703 }
00704 
00708 NS_IMETHODIMP
00709 nsSVGCairoCanvas::CompositeSurface(nsISVGRendererSurface *aSurface,
00710                                    PRUint32 aX, PRUint32 aY, float aOpacity)
00711 {
00712   nsCOMPtr<nsISVGCairoSurface> cairoSurface = do_QueryInterface(aSurface);
00713   if (!cairoSurface)
00714     return NS_ERROR_FAILURE;
00715 
00716   cairo_save(mCR);
00717   cairo_translate(mCR, aX, aY);
00718 
00719   PRUint32 width, height;
00720   aSurface->GetWidth(&width);
00721   aSurface->GetHeight(&height);
00722 
00723   cairo_set_source_surface(mCR, cairoSurface->GetSurface(), 0.0, 0.0);
00724   cairo_paint_with_alpha(mCR, aOpacity);
00725   cairo_restore(mCR);
00726 
00727   return NS_OK;
00728 }
00729 
00733 NS_IMETHODIMP
00734 nsSVGCairoCanvas::CompositeSurfaceMatrix(nsISVGRendererSurface *aSurface,
00735                                          nsIDOMSVGMatrix *aCTM, float aOpacity)
00736 {
00737   nsCOMPtr<nsISVGCairoSurface> cairoSurface = do_QueryInterface(aSurface);
00738   if (!cairoSurface)
00739     return NS_ERROR_FAILURE;
00740 
00741   cairo_save(mCR);
00742 
00743   float m[6];
00744   float val;
00745   aCTM->GetA(&val);
00746   m[0] = val;
00747     
00748   aCTM->GetB(&val);
00749   m[1] = val;
00750     
00751   aCTM->GetC(&val);  
00752   m[2] = val;  
00753     
00754   aCTM->GetD(&val);  
00755   m[3] = val;  
00756   
00757   aCTM->GetE(&val);
00758   m[4] = val;
00759   
00760   aCTM->GetF(&val);
00761   m[5] = val;
00762 
00763   cairo_matrix_t matrix = {m[0], m[1], m[2], m[3], m[4], m[5]};
00764   cairo_transform(mCR, &matrix);
00765 
00766   PRUint32 width, height;
00767   aSurface->GetWidth(&width);
00768   aSurface->GetHeight(&height);
00769 
00770   cairo_set_source_surface(mCR, cairoSurface->GetSurface(), 0.0, 0.0);
00771   cairo_paint_with_alpha(mCR, aOpacity);
00772   cairo_restore(mCR);
00773 
00774   return NS_OK;
00775 }
00776