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