1///////////////////////////////////////////////////////////////////////////// 2// Name: src/cocoa/dcmemory.mm 3// Purpose: wxMemoryDC class 4// Author: David Elliott 5// Modified by: 6// Created: 2003/03/16 7// RCS-ID: $Id: dcmemory.mm 47860 2007-08-02 16:28:31Z DE $ 8// Copyright: (c) 2002 David Elliott 9// Licence: wxWidgets licence 10///////////////////////////////////////////////////////////////////////////// 11 12#include "wx/wxprec.h" 13 14#ifndef WX_PRECOMP 15 #include "wx/log.h" 16 #include "wx/dcmemory.h" 17#endif //WX_PRECOMP 18 19#include "wx/cocoa/autorelease.h" 20 21#import <AppKit/NSImage.h> 22#import <AppKit/NSAffineTransform.h> 23#import <AppKit/NSGraphicsContext.h> 24#import <AppKit/NSColor.h> 25#import <AppKit/NSBezierPath.h> 26 27//----------------------------------------------------------------------------- 28// wxMemoryDC 29//----------------------------------------------------------------------------- 30 31IMPLEMENT_DYNAMIC_CLASS(wxMemoryDC,wxDC) 32 33void wxMemoryDC::Init() 34{ 35 m_cocoaNSImage = NULL; 36 m_ok = false; 37} 38 39wxMemoryDC::wxMemoryDC( wxDC *WXUNUSED(dc) ) 40{ 41 Init(); 42} 43 44wxMemoryDC::~wxMemoryDC(void) 45{ 46 CocoaUnwindStackAndLoseFocus(); 47 [m_cocoaNSImage release]; 48} 49 50bool wxMemoryDC::CocoaLockFocus() 51{ 52 if(m_cocoaNSImage) 53 { 54 [m_cocoaNSImage lockFocus]; 55 sm_cocoaDCStack.Insert(this); 56 NSAffineTransform *newTransform = CocoaGetWxToBoundsTransform([m_cocoaNSImage isFlipped], [m_cocoaNSImage size].height); 57 [newTransform retain]; 58 [m_cocoaWxToBoundsTransform release]; 59 m_cocoaWxToBoundsTransform = newTransform; 60 CocoaApplyTransformations(); 61 return true; 62 } 63 return false; 64} 65 66bool wxMemoryDC::CocoaUnlockFocus() 67{ 68 [m_cocoaNSImage unlockFocus]; 69 return true; 70} 71 72// NOTE: The AppKit is unable to draw onto an NSBitmapImageRep so we must 73// instead copy the data to an offscreen window, then copy it back 74void wxMemoryDC::DoSelect( const wxBitmap& bitmap ) 75{ 76 wxAutoNSAutoreleasePool pool; 77 if(m_selectedBitmap.Ok()) 78 { 79 CocoaTakeFocus(); 80 wxASSERT(m_cocoaNSImage); 81 // Replace the bitmap's native data with a newly created one based on the 82 // NSImage that has been (potentially) drawn upon. Note that this may and 83 // probably will in many cases change the bitmap's format. 84 // There is nothing we can do about this using pure Cocoa code. Even using 85 // CGBitmapContext is not an option because it only supports a limited 86 // number of bitmap formats. Specifically, 24-bpp is not supported. 87 m_selectedBitmap.SetNSBitmapImageRep( 88 [[NSBitmapImageRep alloc] 89 initWithFocusedViewRect:NSMakeRect(0.0,0.0, 90 m_selectedBitmap.GetWidth(), 91 m_selectedBitmap.GetHeight())]); 92 } 93 CocoaUnwindStackAndLoseFocus(); 94 [m_cocoaNSImage release]; 95 m_cocoaNSImage = nil; 96 m_selectedBitmap = bitmap; 97 if(m_selectedBitmap.Ok()) 98 { 99 // Create an offscreen window of the same size 100 m_cocoaNSImage = [[NSImage alloc] 101 initWithSize:NSMakeSize(m_selectedBitmap.GetWidth(), 102 m_selectedBitmap.GetHeight())]; 103 104 // Now copy the data 105 // Pass false to GetNSImage so the mask is not applied as an alpha channel. 106 // Cocoa uses premultiplied alpha so applying the mask would cause all 107 // color information masked out to be turned black which is undesirable. 108 // FIXME: Currently, the mask will not be updated if any drawing occurs. 109 // My only suggestion is for wxCocoa users to eschew the mask in favor 110 // of an alpha channel or to recreate the mask after drawing. 111 // The only way to fix this is to draw twice, once as normal and again 112 // onto the mask to update it. That would require overriding every 113 // single drawing primitive (e.g. DoDrawLine, DoDrawRectangle, etc.) 114 // and would be a major undertaking. 115 NSImage *nsimage = [m_selectedBitmap.GetNSImage(false) retain]; 116 [m_cocoaNSImage lockFocus]; 117 [nsimage drawAtPoint: NSMakePoint(0,0) 118 fromRect: NSMakeRect(0.0,0.0,m_selectedBitmap.GetWidth(),m_selectedBitmap.GetHeight()) 119 operation: NSCompositeCopy 120 fraction: 1.0]; 121 [m_cocoaNSImage unlockFocus]; 122 123 [nsimage release]; 124 } 125} 126 127void wxMemoryDC::DoGetSize( int *width, int *height ) const 128{ 129 if(width) 130 *width = m_selectedBitmap.GetWidth(); 131 if(height) 132 *height = m_selectedBitmap.GetHeight(); 133} 134 135bool wxMemoryDC::CocoaDoBlitOnFocusedDC(wxCoord xdest, wxCoord ydest, 136 wxCoord width, wxCoord height, wxCoord xsrc, wxCoord ysrc, 137 int logicalFunc, bool useMask, wxCoord xsrcMask, wxCoord ysrcMask) 138{ 139 if(!m_selectedBitmap.Ok()) 140 return false; 141 142 NSAffineTransform *transform = [NSAffineTransform transform]; 143 [transform translateXBy:xdest yBy:ydest]; 144 145 NSAffineTransform *flipTransform = [NSAffineTransform transform]; 146 /* x' = 1x + 0y + 0 147 y' = 0x + -1y + window's height 148 */ 149 NSAffineTransformStruct matrix = { 150 1, 0 151 , 0, -1 152 , 0, height 153 }; 154 [flipTransform setTransformStruct: matrix]; 155 156 NSGraphicsContext *context = [NSGraphicsContext currentContext]; 157 [context saveGraphicsState]; 158 [transform concat]; 159 [flipTransform concat]; 160 161 NSImage *sourceImage; 162 if(useMask) 163 { 164 sourceImage = [m_cocoaNSImage copy]; 165 // Apply the mask to the copied image 166 NSBitmapImageRep *maskRep = m_selectedBitmap.GetMask()->GetNSBitmapImageRep(); 167 NSImage *maskImage = [[NSImage alloc] initWithSize:[maskRep size]]; 168 [maskImage addRepresentation:maskRep]; 169 [sourceImage lockFocus]; 170 [maskImage compositeToPoint:NSZeroPoint operation:NSCompositeDestinationIn]; 171 [sourceImage unlockFocus]; 172 [maskImage release]; 173 } 174 else 175 { // retain the m_cocoaNSImage so it has the same ownership as the copy done in the other case. 176 sourceImage = [m_cocoaNSImage retain]; 177 } 178 NSCompositingOperation drawingOp; 179 switch(logicalFunc) 180 { 181 case wxCOPY: 182 // Even if not using the mask, the image might have an alpha channel 183 // so always use NSCompositeSourceOver. If the image is fully opaque 184 // it works out the same as NSCompositeCopy. 185 drawingOp = NSCompositeSourceOver; 186 break; 187 // FIXME: implement more raster ops 188 default: 189 wxLogDebug(wxT("wxCocoa does not support blitting with raster operation %d."), logicalFunc); 190 // Just use the default operation. 191 drawingOp = NSCompositeCopy; 192 } 193 194 wxLogTrace(wxTRACE_COCOA,wxT("[m_cocoaNSImage isFlipped]=%d"), [m_cocoaNSImage isFlipped]); 195 [sourceImage drawAtPoint: NSMakePoint(0,0) 196 fromRect: NSMakeRect(xsrc, 197 m_selectedBitmap.GetHeight()-height-ysrc, 198 width, height) 199 operation: drawingOp 200 fraction: 1.0]; 201 202 [sourceImage release]; // It was either retained, copied, or allocated. 203 [context restoreGraphicsState]; 204 return false; 205} 206 207bool wxMemoryDC::CocoaGetBounds(void *rectData) 208{ 209 if(!rectData) 210 return false; 211 if(!m_cocoaNSImage) 212 return false; 213 NSRect *pRect = (NSRect*)rectData; 214 pRect->origin.x = 0.0; 215 pRect->origin.y = 0.0; 216 pRect->size = [m_cocoaNSImage size]; 217 return true; 218} 219 220