Back to index

lightning-sunbird  0.9+nobinonly
nsSVGGDIPlusCanvas.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 project.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Crocodile Clips Ltd..
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <windows.h>
00040 
00041 // unknwn.h is needed to build with WIN32_LEAN_AND_MEAN
00042 #include <unknwn.h>
00043 
00044 #include <Gdiplus.h>
00045 using namespace Gdiplus;
00046 
00047 #include "nsCOMPtr.h"
00048 #include "nsSVGGDIPlusCanvas.h"
00049 #include "nsISVGGDIPlusCanvas.h"
00050 #include "nsIRenderingContext.h"
00051 #include "nsIDeviceContext.h"
00052 #include "nsTransform2D.h"
00053 #include "nsPresContext.h"
00054 #include "nsRect.h"
00055 #include "nsIRenderingContextWin.h"
00056 #include "nsIDOMSVGMatrix.h"
00057 #include "nsISVGGDIPlusSurface.h"
00058 #include "nsVoidArray.h"
00059 
00064 
00065 
00068 class nsSVGGDIPlusCanvas : public nsISVGGDIPlusCanvas
00069 {
00070 public:
00071   nsSVGGDIPlusCanvas();
00072   ~nsSVGGDIPlusCanvas();
00073   nsresult Init(nsIRenderingContext* ctx, nsPresContext* presContext,
00074                 const nsRect & dirtyRect);
00075 
00076   // nsISupports interface:
00077   NS_DECL_ISUPPORTS
00078 
00079   // nsISVGRendererCanvas interface:
00080   NS_DECL_NSISVGRENDERERCANVAS
00081 
00082   // nsISVGGDIPlusCanvas interface:
00083   NS_IMETHOD_(Graphics*) GetGraphics();
00084   NS_IMETHOD_(Region*) GetClipRegion();
00085 
00086 private:
00087   nsCOMPtr<nsIRenderingContext> mMozContext;
00088   nsCOMPtr<nsPresContext> mPresContext;
00089   Graphics *mGraphics;
00090   nsVoidArray mClipStack;
00091   nsVoidArray mSurfaceStack;
00092   Region mClipRegion;
00093   PRUint16 mRenderMode;
00094 
00095 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00096   Bitmap *mOffscreenBitmap;
00097   Graphics *mOffscreenGraphics;
00098   HDC mOffscreenHDC;
00099   nsIDrawingSurface* mTempBuffer; // temp storage for during DC locking
00100 #endif
00101 };
00102 
00105 //----------------------------------------------------------------------
00106 // implementation:
00107 
00108 nsSVGGDIPlusCanvas::nsSVGGDIPlusCanvas()
00109     : mGraphics(nsnull)
00110 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00111       , mOffscreenBitmap(nsnull), mOffscreenGraphics(nsnull), mOffscreenHDC(nsnull)
00112 #endif
00113 {
00114 }
00115 
00116 nsSVGGDIPlusCanvas::~nsSVGGDIPlusCanvas()
00117 {
00118 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00119   if (mOffscreenGraphics)
00120     delete mOffscreenGraphics;
00121   if (mOffscreenBitmap)
00122     delete mOffscreenBitmap;
00123 #endif
00124   if (mGraphics)
00125     delete mGraphics;
00126   mMozContext = nsnull;
00127 }
00128 
00129 nsresult
00130 nsSVGGDIPlusCanvas::Init(nsIRenderingContext* ctx,
00131                          nsPresContext* presContext,
00132                          const nsRect & dirtyRect)
00133 {
00134   mPresContext = presContext;
00135   mMozContext = ctx;
00136   NS_ASSERTION(mMozContext, "empty rendering context");
00137 
00138   HDC hdc;
00139   // this ctx better be what we think it is...
00140   mMozContext->RetrieveCurrentNativeGraphicData((PRUint32 *)(&hdc));
00141     
00142   mGraphics = new Graphics(hdc);
00143   if (!mGraphics) return NS_ERROR_FAILURE;
00144 
00145   // Work in pixel units instead of the default DisplayUnits.
00146   mGraphics->SetPageUnit(UnitPixel);
00147 
00148   // We'll scale our logical 'pixles' to device units according to the
00149   // resolution of the device. For display devices, the canonical
00150   // pixel scale will be 1, for printers it will be some other value:
00151   nsCOMPtr<nsIDeviceContext>  devcontext;
00152   mMozContext->GetDeviceContext(*getter_AddRefs(devcontext));
00153   float scale;
00154   devcontext->GetCanonicalPixelScale(scale);
00155 
00156   mGraphics->SetPageScale(scale);
00157 
00158   
00159   // get the translation set on the rendering context. It will be in
00160   // displayunits (i.e. pixels*scale), *not* pixels:
00161   nsTransform2D* xform;
00162   mMozContext->GetCurrentTransform(xform);
00163   float dx, dy;
00164   xform->GetTranslation(&dx, &dy);
00165 
00166 #if defined(DEBUG) && defined(SVG_DEBUG_PRINTING)
00167   printf("nsSVGGDIPlusCanvas(%p)::Init()[\n", this);
00168   printf("pagescale=%f\n", scale);
00169   printf("page unit=%d\n", mGraphics->GetPageUnit());
00170   printf("]\n");
00171 #endif
00172   
00173 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00174   mGraphics->TranslateTransform(dx+dirtyRect.x, dy+dirtyRect.y);
00175 
00176   // GDI+ internally works on 32bpp surfaces. Writing directly to
00177   // Mozilla's backbuffer can be very slow if it hasn't got the right
00178   // format (!=32bpp).  For complex SVG docs it is advantageous to
00179   // render to a 32bppPARGB bitmap first:
00180   mOffscreenBitmap = new Bitmap(dirtyRect.width, dirtyRect.height, PixelFormat32bppPARGB);
00181   if (!mOffscreenBitmap) return NS_ERROR_FAILURE;
00182   mOffscreenGraphics = new Graphics(mOffscreenBitmap);
00183   if (!mOffscreenGraphics) return NS_ERROR_FAILURE;
00184   mOffscreenGraphics->TranslateTransform((float)-dirtyRect.x, (float)-dirtyRect.y);
00185 #else
00186   mGraphics->TranslateTransform(dx/scale, dy/scale, MatrixOrderPrepend);
00187 #endif
00188 
00189   mRenderMode = SVG_RENDER_MODE_NORMAL;
00190   
00191   return NS_OK;
00192 }
00193 
00194 nsresult
00195 NS_NewSVGGDIPlusCanvas(nsISVGRendererCanvas **result,
00196                        nsIRenderingContext *ctx,
00197                        nsPresContext *presContext,
00198                        const nsRect & dirtyRect)
00199 {
00200   nsSVGGDIPlusCanvas* pg = new nsSVGGDIPlusCanvas();
00201   if (!pg) return NS_ERROR_OUT_OF_MEMORY;
00202 
00203   NS_ADDREF(pg);
00204 
00205   nsresult rv = pg->Init(ctx, presContext, dirtyRect);
00206 
00207   if (NS_FAILED(rv)) {
00208     NS_RELEASE(pg);
00209     return rv;
00210   }
00211   
00212   *result = pg;
00213   return rv;
00214 }
00215 
00216 //----------------------------------------------------------------------
00217 // nsISupports methods:
00218 
00219 NS_IMPL_ADDREF(nsSVGGDIPlusCanvas)
00220 NS_IMPL_RELEASE(nsSVGGDIPlusCanvas)
00221 
00222 NS_INTERFACE_MAP_BEGIN(nsSVGGDIPlusCanvas)
00223   NS_INTERFACE_MAP_ENTRY(nsISVGRendererCanvas)
00224   NS_INTERFACE_MAP_ENTRY(nsISVGGDIPlusCanvas)
00225   NS_INTERFACE_MAP_ENTRY(nsISupports)
00226 //  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGRendererCanvas)
00227 NS_INTERFACE_MAP_END
00228 
00229 //----------------------------------------------------------------------
00230 // nsISVGRendererCanvas methods:
00231 
00233 NS_IMETHODIMP
00234 nsSVGGDIPlusCanvas::LockRenderingContext(const nsRect & rect,
00235                                          nsIRenderingContext **_retval)
00236 {
00237 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00238   NS_ASSERTION(!mOffscreenHDC, "offscreen hdc already created! Nested rendering context locking?");
00239   mOffscreenHDC =  mOffscreenGraphics->GetHDC();
00240   
00241   nsCOMPtr<nsIRenderingContextWin> wincontext = do_QueryInterface(mMozContext);
00242   NS_ASSERTION(wincontext, "no windows rendering context");
00243   
00244   nsCOMPtr<nsIDrawingSurface> mOffscreenSurface;
00245   wincontext->CreateDrawingSurface(mOffscreenHDC, (void*&)*getter_AddRefs(mOffscreenSurface));
00246   mMozContext->GetDrawingSurface(&mTempBuffer);
00247   mMozContext->SelectOffScreenDrawingSurface(mOffscreenSurface);
00248 
00249 #else
00250   // XXX do we need to flush?
00251   Flush();
00252 #endif
00253   
00254   *_retval = mMozContext;
00255   NS_ADDREF(*_retval);
00256   return NS_OK;
00257 }
00258 
00260 NS_IMETHODIMP 
00261 nsSVGGDIPlusCanvas::UnlockRenderingContext()
00262 {
00263 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00264   NS_ASSERTION(mOffscreenHDC, "offscreen hdc already freed! Nested rendering context locking?");
00265 
00266   // restore original surface
00267   mMozContext->SelectOffScreenDrawingSurface(mTempBuffer);
00268   mTempBuffer = nsnull;
00269 
00270   mOffscreenGraphics->ReleaseHDC(mOffscreenHDC);
00271   mOffscreenHDC = nsnull;
00272 #else
00273   // nothing to do
00274 #endif
00275   return NS_OK;
00276 }
00277 
00279 NS_IMETHODIMP
00280 nsSVGGDIPlusCanvas::GetPresContext(nsPresContext **_retval)
00281 {
00282   *_retval = mPresContext;
00283   NS_IF_ADDREF(*_retval);
00284   return NS_OK;
00285 }
00286 
00288 NS_IMETHODIMP
00289 nsSVGGDIPlusCanvas::Clear(nscolor color)
00290 {
00291 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00292   mOffscreenGraphics->Clear(Color(NS_GET_R(color),
00293                                   NS_GET_G(color),
00294                                   NS_GET_B(color)));
00295 #else
00296   mGraphics->Clear(Color(NS_GET_R(color),
00297                          NS_GET_G(color),
00298                          NS_GET_B(color)));
00299 #endif
00300   
00301   return NS_OK;
00302 }
00303 
00305 NS_IMETHODIMP
00306 nsSVGGDIPlusCanvas::Flush()
00307 {
00308 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00309   mGraphics->SetCompositingMode(CompositingModeSourceCopy);
00310   mGraphics->DrawImage(mOffscreenBitmap, 0, 0,
00311                        mOffscreenBitmap->GetWidth(),
00312                        mOffscreenBitmap->GetHeight());
00313 #endif
00314 
00315   mGraphics->Flush(FlushIntentionSync);
00316   return NS_OK;
00317 }
00318 
00319 //----------------------------------------------------------------------
00320 // nsISVGGDIPlusCanvas methods:
00321 
00322 NS_IMETHODIMP_(Graphics*)
00323 nsSVGGDIPlusCanvas::GetGraphics()
00324 {
00325 #ifdef SVG_GDIPLUS_ENABLE_OFFSCREEN_BUFFER
00326   return mOffscreenGraphics;
00327 #else
00328   return mGraphics;
00329 #endif
00330 }
00331 
00332 NS_IMETHODIMP
00333 nsSVGGDIPlusCanvas::GetRenderMode(PRUint16 *aMode)
00334 {
00335   *aMode = mRenderMode;
00336   return NS_OK;
00337 }
00338 
00339 NS_IMETHODIMP
00340 nsSVGGDIPlusCanvas::SetRenderMode(PRUint16 aMode)
00341 {
00342   if (mRenderMode == SVG_RENDER_MODE_CLIP && aMode == SVG_RENDER_MODE_NORMAL)
00343     mGraphics->SetClip(&mClipRegion, CombineModeIntersect);
00344   mRenderMode = aMode;
00345   return NS_OK;
00346 }
00347 
00348 NS_IMETHODIMP_(Region*)
00349 nsSVGGDIPlusCanvas::GetClipRegion()
00350 {
00351   return &mClipRegion;
00352 }
00353 
00355 NS_IMETHODIMP
00356 nsSVGGDIPlusCanvas::PushClip()
00357 {
00358   Region *region = new Region;
00359   if (region)
00360     mGraphics->GetClip(region);
00361   // append even if we failed to allocate the region so push/pop match
00362   mClipStack.AppendElement((void *)region);
00363 
00364   mClipRegion.MakeEmpty();
00365 
00366   return NS_OK;
00367 }
00368 
00370 NS_IMETHODIMP
00371 nsSVGGDIPlusCanvas::PopClip()
00372 {
00373   PRUint32 count = mClipStack.Count();
00374   if (count == 0)
00375     return NS_OK;
00376 
00377   Region *region = (Region *)mClipStack[count-1];
00378   if (region) {
00379     mGraphics->SetClip(region);
00380     delete region;
00381   }
00382   mClipStack.RemoveElementAt(count-1);
00383 
00384   return NS_OK;
00385 }
00386 
00389 NS_IMETHODIMP
00390 nsSVGGDIPlusCanvas::SetClipRect(nsIDOMSVGMatrix *aCTM, float aX, float aY,
00391                                 float aWidth, float aHeight)
00392 {
00393   if (!aCTM)
00394     return NS_ERROR_FAILURE;
00395 
00396   float m[6];
00397   float val;
00398   aCTM->GetA(&val);
00399   m[0] = val;
00400     
00401   aCTM->GetB(&val);
00402   m[1] = val;
00403     
00404   aCTM->GetC(&val);  
00405   m[2] = val;  
00406     
00407   aCTM->GetD(&val);  
00408   m[3] = val;  
00409   
00410   aCTM->GetE(&val);
00411   m[4] = val;
00412   
00413   aCTM->GetF(&val);
00414   m[5] = val;
00415 
00416   Matrix matrix(m[0], m[1], m[2], m[3], m[4], m[5]);
00417   RectF rect(aX, aY, aWidth, aHeight);
00418   Region clip(rect);
00419   clip.Transform(&matrix);
00420   mGraphics->SetClip(&clip, CombineModeIntersect);
00421 
00422   return NS_OK;
00423 }
00424 
00426 NS_IMETHODIMP
00427 nsSVGGDIPlusCanvas::PushSurface(nsISVGRendererSurface *aSurface)
00428 {
00429   nsCOMPtr<nsISVGGDIPlusSurface> gdiplusSurface = do_QueryInterface(aSurface);
00430   if (!gdiplusSurface)
00431     return NS_ERROR_FAILURE;
00432 
00433   mSurfaceStack.AppendElement((void *)mGraphics);
00434   mGraphics = new Graphics(gdiplusSurface->GetSurface());
00435   if (!mGraphics) {
00436     PopSurface();
00437     return NS_ERROR_FAILURE;
00438   }
00439     
00440   mGraphics->Clear(Color(0,0,0,0));
00441   return NS_OK;
00442 }
00443 
00445 NS_IMETHODIMP
00446 nsSVGGDIPlusCanvas::PopSurface()
00447 {
00448   PRUint32 count = mSurfaceStack.Count();
00449   if (count != 0) {
00450     delete mGraphics;
00451     mGraphics = (Graphics *)mSurfaceStack[count - 1];
00452     mSurfaceStack.RemoveElementAt(count - 1);
00453   }
00454 
00455   return NS_OK;
00456 }
00457 
00461 NS_IMETHODIMP
00462 nsSVGGDIPlusCanvas::CompositeSurface(nsISVGRendererSurface *aSurface,
00463                                      PRUint32 aX, PRUint32 aY, float aOpacity)
00464 {
00465   nsCOMPtr<nsISVGGDIPlusSurface> gdiplusSurface = do_QueryInterface(aSurface);
00466   if (!gdiplusSurface)
00467     return NS_ERROR_FAILURE;
00468 
00469   ColorMatrix cxform;
00470   memset(&cxform, 0, sizeof(ColorMatrix));
00471   cxform.m[0][0] = cxform.m[1][1] = cxform.m[2][2] = cxform.m[4][4] = 1.0f;
00472   cxform.m[3][3] = aOpacity;
00473   ImageAttributes attrib;
00474   attrib.SetColorMatrix(&cxform);
00475 
00476   PRUint32 width, height;
00477   aSurface->GetWidth(&width);
00478   aSurface->GetHeight(&height);
00479 
00480   Rect rect(aX, aY, width, height);
00481 
00482   mGraphics->DrawImage(gdiplusSurface->GetSurface(),
00483                        rect, 0, 0, width, height, UnitPixel, &attrib);
00484 
00485   return NS_OK;
00486 }
00487 
00491 NS_IMETHODIMP
00492 nsSVGGDIPlusCanvas::CompositeSurfaceMatrix(nsISVGRendererSurface *aSurface,
00493                                            nsIDOMSVGMatrix *aCTM, float aOpacity)
00494 {
00495   float m[6];
00496   float val;
00497   aCTM->GetA(&val);
00498   m[0] = val;
00499     
00500   aCTM->GetB(&val);
00501   m[1] = val;
00502     
00503   aCTM->GetC(&val);  
00504   m[2] = val;  
00505     
00506   aCTM->GetD(&val);  
00507   m[3] = val;  
00508   
00509   aCTM->GetE(&val);
00510   m[4] = val;
00511   
00512   aCTM->GetF(&val);
00513   m[5] = val;
00514 
00515   Matrix xform(m[0], m[1], m[2], m[3], m[4], m[5]), orig;
00516 
00517   mGraphics->GetTransform(&orig);
00518   mGraphics->MultiplyTransform(&xform);
00519   CompositeSurface(aSurface, 0, 0, aOpacity);
00520   mGraphics->SetTransform(&orig);
00521   return NS_OK;
00522 }