Back to index

lightning-sunbird  0.9+nobinonly
nsDrawingSurfaceMac.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 mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <MacMemory.h>
00039 
00040 #include "nsDrawingSurfaceMac.h"
00041 #include "nsGraphicState.h"
00042 #include "nsRegionPool.h"
00043 
00044 #ifdef MOZ_WIDGET_COCOA
00045 // Helper functions to manipulate CGContextRef from a Cocoa NSQuickDrawView.
00046 // Implemented in nsCocoaUtils.mm.
00047 extern CGContextRef Cocoa_LockFocus(void* view);
00048 extern void Cocoa_UnlockFocus(void* view);
00049 #endif
00050 
00051 static NS_DEFINE_IID(kIDrawingSurfaceIID, NS_IDRAWING_SURFACE_IID);
00052 static NS_DEFINE_IID(kIDrawingSurfaceMacIID, NS_IDRAWING_SURFACE_MAC_IID);
00053 
00054 
00060 nsDrawingSurfaceMac::nsDrawingSurfaceMac()
00061 {
00062   mPort = NULL;
00063   mGS = sGraphicStatePool.GetNewGS();     //new nsGraphicState();
00064   mWidth = mHeight = 0;
00065   mLockOffset = mLockHeight = 0;
00066   mLockFlags = 0;
00067   mIsOffscreen = PR_FALSE;
00068   mIsLocked = PR_FALSE;
00069 #ifdef MOZ_WIDGET_COCOA
00070   mWidgetView = nsnull;
00071 #endif
00072 }
00073 
00079 nsDrawingSurfaceMac::~nsDrawingSurfaceMac()
00080 {
00081        if(mIsOffscreen && mPort){
00082        GWorldPtr offscreenGWorld = (GWorldPtr)mPort;
00083               ::UnlockPixels(::GetGWorldPixMap(offscreenGWorld));
00084               ::DisposeGWorld(offscreenGWorld);
00085               
00086               nsGraphicsUtils::SetPortToKnownGoodPort();
00087        }
00088 
00089        if (mGS){
00090               sGraphicStatePool.ReleaseGS(mGS); //delete mGS;
00091        }
00092 }
00093 
00099 NS_IMETHODIMP nsDrawingSurfaceMac::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00100 {
00101   if (nsnull == aInstancePtr)
00102     return NS_ERROR_NULL_POINTER;
00103 
00104   if (aIID.Equals(kIDrawingSurfaceIID)){
00105     nsIDrawingSurface* tmp = this;
00106     *aInstancePtr = (void*) tmp;
00107     NS_ADDREF_THIS();
00108     return NS_OK;
00109   }
00110 
00111   if (aIID.Equals(kIDrawingSurfaceMacIID)){
00112     nsIDrawingSurfaceMac* tmp = this;
00113     *aInstancePtr = (void*) tmp;
00114     NS_ADDREF_THIS();
00115     return NS_OK;
00116   }
00117 
00118   static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
00119 
00120   if (aIID.Equals(kISupportsIID)){
00121     nsIDrawingSurface* tmp = this;
00122     nsISupports* tmp2 = tmp;
00123     *aInstancePtr = (void*) tmp2;
00124     NS_ADDREF_THIS();
00125     return NS_OK;
00126   }
00127 
00128   return NS_NOINTERFACE;
00129 }
00130 
00131 NS_IMPL_ADDREF(nsDrawingSurfaceMac)
00132 NS_IMPL_RELEASE(nsDrawingSurfaceMac)
00133 
00134 #pragma mark-
00135 
00141 NS_IMETHODIMP nsDrawingSurfaceMac::Lock(PRInt32 aX, PRInt32 aY,
00142                                           PRUint32 aWidth, PRUint32 aHeight,
00143                                           void **aBits, PRInt32 *aStride,
00144                                           PRInt32 *aWidthBytes, PRUint32 aFlags)
00145 {
00146 
00147   if (!mIsLocked && mIsOffscreen && mPort)
00148   {
00149     // get the offscreen gworld for our use
00150     GWorldPtr     offscreenGWorld = (GWorldPtr)mPort;
00151 
00152     // calculate the pixel data size
00153     PixMapHandle  thePixMap = ::GetGWorldPixMap(offscreenGWorld);
00154     Ptr           baseaddr  = ::GetPixBaseAddr(thePixMap);
00155     PRInt32       cmpSize   = ((**thePixMap).pixelSize >> 3);
00156     PRInt32       rowBytes  = (**thePixMap).rowBytes & 0x3FFF;
00157 
00158     *aBits = baseaddr + (aX * cmpSize) + aY * rowBytes;
00159     *aStride = rowBytes;
00160     *aWidthBytes = aWidth * cmpSize;
00161 
00162     mIsLocked = PR_TRUE;
00163   }
00164   else
00165   {
00166     NS_ASSERTION(0, "nested lock attempt");
00167     return NS_ERROR_FAILURE;
00168   }
00169 
00170   return NS_OK;
00171 }
00172 
00178 NS_IMETHODIMP nsDrawingSurfaceMac::Unlock(void)
00179 {
00180        mIsLocked = PR_FALSE;
00181   return NS_OK;
00182 }
00183 
00189 NS_IMETHODIMP nsDrawingSurfaceMac::GetDimensions(PRUint32 *aWidth, PRUint32 *aHeight)
00190 {
00191   *aWidth = mWidth;
00192   *aHeight = mHeight;
00193   return NS_OK;
00194 }
00195 
00201 NS_IMETHODIMP nsDrawingSurfaceMac::IsPixelAddressable(PRBool *aAddressable)
00202 {
00203   NS_ASSERTION(0, "Not implemented!");
00204   return NS_ERROR_NOT_IMPLEMENTED;
00205 }
00206 
00212 NS_IMETHODIMP nsDrawingSurfaceMac::GetPixelFormat(nsPixelFormat *aFormat)
00213 {
00214   //*aFormat = mPixFormat;
00215   NS_ASSERTION(0, "Not implemented!");
00216   return NS_ERROR_NOT_IMPLEMENTED;
00217 }
00218 
00219 #pragma mark -
00220 
00226 NS_IMETHODIMP nsDrawingSurfaceMac::Init(nsIDrawingSurface*     aDS)
00227 {
00228        nsDrawingSurfaceMac* surface = static_cast<nsDrawingSurfaceMac*>(aDS);
00229        surface->GetGrafPtr(&mPort);
00230        mGS->Init(surface);
00231        
00232   return NS_OK;
00233 }
00234 
00240 NS_IMETHODIMP nsDrawingSurfaceMac::Init(CGrafPtr aPort)
00241 {
00242        // set our grafPtr to the passed in port
00243   mPort = aPort;
00244        mGS->Init(aPort);
00245   return NS_OK;
00246 }
00247 
00253 NS_IMETHODIMP nsDrawingSurfaceMac::Init(nsIWidget *aTheWidget)
00254 {
00255        // get our native graphics port from the widget
00256        mPort = reinterpret_cast<CGrafPtr>(aTheWidget->GetNativeData(NS_NATIVE_GRAPHIC));
00257        mGS->Init(aTheWidget);
00258 #ifdef MOZ_WIDGET_COCOA
00259   mWidgetView = aTheWidget->GetNativeData(NS_NATIVE_WIDGET);
00260 #endif
00261   return NS_OK;
00262 }
00263 
00270 NS_IMETHODIMP nsDrawingSurfaceMac::Init(PRUint32 aDepth, PRUint32 aWidth, PRUint32 aHeight, PRUint32 aFlags)
00271 {
00272   PRUint32    depth;
00273   Rect               macRect;
00274   GWorldPtr offscreenGWorld = nsnull;
00275   Boolean   tryTempMemFirst = ((aFlags & NS_CREATEDRAWINGSURFACE_SHORTLIVED) != 0);
00276   
00277   depth = aDepth;
00278   mWidth = aWidth;
00279   mHeight = aHeight;
00280   
00281 
00282        // calculate the rectangle
00283   if (aWidth != 0){
00284        ::SetRect(&macRect, 0, 0, aWidth, aHeight);
00285   }else{
00286        ::SetRect(&macRect, 0, 0, 2, 2);
00287        }
00288 
00289        // create offscreen, first with normal memory, if that fails use temp memory, if that fails, return
00290 
00291        // Quick and dirty check to make sure there is some memory available.
00292        // GWorld allocations in temp mem can still fail if the heap is totally
00293        // full, because some stuff is allocated in the heap
00294        const long kReserveHeapFreeSpace = (1024 * 1024);
00295        const long kReserveHeapContigSpace = (512 * 1024);
00296 
00297   long   totalSpace, contiguousSpace;
00298   
00299   if (tryTempMemFirst)
00300   {
00301          ::NewGWorld(&offscreenGWorld, depth, &macRect, nsnull, nsnull, useTempMem);
00302     if (!offscreenGWorld)
00303     {
00304       // only try the heap if there is enough space
00305        ::PurgeSpace(&totalSpace, &contiguousSpace);            // this does not purge memory, just measure it
00306 
00307        if (totalSpace > kReserveHeapFreeSpace && contiguousSpace > kReserveHeapContigSpace)
00308               ::NewGWorld(&offscreenGWorld, depth, &macRect, nsnull, nsnull, 0);
00309     }
00310   }
00311   else    // heap first
00312   {
00313       // only try the heap if there is enough space
00314        ::PurgeSpace(&totalSpace, &contiguousSpace);            // this does not purge memory, just measure it
00315 
00316        if (totalSpace > kReserveHeapFreeSpace && contiguousSpace > kReserveHeapContigSpace)
00317               ::NewGWorld(&offscreenGWorld, depth, &macRect, nsnull, nsnull, 0);
00318   
00319       if (!offscreenGWorld)
00320              ::NewGWorld(&offscreenGWorld, depth, &macRect, nsnull, nsnull, useTempMem);
00321   }
00322   
00323   if (!offscreenGWorld)
00324     return NS_ERROR_OUT_OF_MEMORY;  
00325   
00326        // keep the pixels locked... that's how it works on Windows and  we are forced to do
00327        // the same because the API doesn't give us any hook to do it at drawing time.
00328   ::LockPixels(::GetGWorldPixMap(offscreenGWorld));
00329 
00330        // erase the offscreen area
00331        {
00332          StGWorldPortSetter  setter(offscreenGWorld);
00333          ::EraseRect(&macRect);
00334   }
00335 
00336        Init(offscreenGWorld);
00337        mIsOffscreen = PR_TRUE;
00338   return NS_OK;
00339 }
00340 
00341 
00342 // Takes a QD Rect and adds it to the path of the CG Context.  This is used
00343 // by QDRegionToRects in order to create a clipping path from a region.
00344 static OSStatus
00345 CreatePathFromRectsProc(UInt16 aMessage, RgnHandle aRegion, const Rect* aRect,
00346                         void* aData)
00347 {
00348   CGContextRef context = NS_STATIC_CAST(CGContextRef, aData);
00349 
00350   if (aMessage == kQDRegionToRectsMsgParse)
00351   {
00352     CGRect rect = ::CGRectMake(aRect->left, aRect->top,
00353                                aRect->right - aRect->left,
00354                                aRect->bottom - aRect->top);
00355     ::CGContextAddRect(context, rect);
00356   }
00357 
00358   return noErr;
00359 }
00360 
00361 NS_IMETHODIMP_(CGContextRef)
00362 nsDrawingSurfaceMac::StartQuartzDrawing()
00363 {
00364   CGContextRef context;
00365 #ifdef MOZ_WIDGET_COCOA
00366   // In Cocoa, we get the context directly from the NSQuickDrawView.
00367   if (mWidgetView) {
00368     context = Cocoa_LockFocus(mWidgetView);
00369   } else
00370 #endif
00371   {
00372     // Convert GrafPort to a CGContext
00373     ::QDBeginCGContext(mPort, &context);
00374 
00375     // Translate to QuickDraw coordinate system
00376     Rect portRect;
00377     ::GetPortBounds(mPort, &portRect);
00378     ::CGContextTranslateCTM(context, 0, (float)(portRect.bottom - portRect.top));
00379     ::CGContextScaleCTM(context, 1, -1);
00380   }
00381 
00382   if (::IsPortClipRegionEmpty(mPort)) {
00383     // If port clip region is empty, then we need to create 0 by 0 path.
00384     CGRect rect = ::CGRectMake(0, 0, 0, 0);
00385     ::CGContextClipToRect(context, rect);
00386   } else {
00387     // Construct a CG path from the QD region and clip to the path.
00388     StRegionFromPool currentClipRgn;
00389     ::GetPortClipRegion(mPort, currentClipRgn);
00390     ::QDRegionToRects(currentClipRgn, kQDParseRegionFromTopLeft,
00391                       CreatePathFromRectsProc, context);
00392     ::CGContextClip(context);
00393   }
00394 
00395   return context;
00396 }
00397 
00398 NS_IMETHODIMP_(void)
00399 nsDrawingSurfaceMac::EndQuartzDrawing(CGContextRef aContext)
00400 {
00401   // Synchronize the context to QD port before closing it.
00402   ::CGContextSynchronize(aContext);
00403 
00404 #ifdef MOZ_WIDGET_COCOA
00405   if (mWidgetView)
00406     Cocoa_UnlockFocus(mWidgetView);
00407   else
00408 #endif
00409     ::QDEndCGContext(mPort, &aContext);
00410 }