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