1 unit CocoaGDIObjects;
2 //todo: Remove MacOSAll unit to prevent Carbon framework linking.
3 //todo: Remove HIShape usage used in TCocoaRegion.
4 
5 interface
6 
7 {$mode objfpc}{$H+}
8 {$modeswitch objectivec1}
9 
10 uses
11   MacOSAll, // for CGContextRef
12   LCLtype, LCLProc, Graphics, Controls, fpcanvas,
13   CocoaAll, CocoaUtils,
14   cocoa_extra,
15   {$ifndef CocoaUseHITheme}
16   customdrawndrawers, customdrawn_mac,
17   {$endif}
18   SysUtils, Classes, Contnrs, Types, Math;
19 
20 type
21   TCocoaBitmapAlignment = (
22     cbaByte,  // each line starts at byte boundary.
23     cbaWord,  // each line starts at word (16bit) boundary
24     cbaDWord, // each line starts at double word (32bit) boundary
25     cbaQWord, // each line starts at quad word (64bit) boundary
26     cbaDQWord // each line starts at double quad word (128bit) boundary
27   );
28 
29   TCocoaBitmapType = (
30     cbtMono,  // mask or mono bitmap
31     cbtGray,  // grayscale bitmap
32     cbtRGB,   // color bitmap 8-8-8 R-G-B
33     cbtARGB,  // color bitmap with alpha channel first 8-8-8-8 A-R-G-B
34     cbtRGBA,  // color bitmap with alpha channel last 8-8-8-8 R-G-B-A
35     cbtABGR,  // color bitmap with alpha channel first 8-8-8-8 A-B-G-R
36     cbtBGRA   // color bitmap with alpha channel last 8-8-8-8 B-G-R-A
37   );
38 
39 const
40   cbtMask = cbtMono;
41 
42 type
43   TCocoaBitmap = class;
44   TCocoaContext = class;
45 
46   { TCocoaGDIObject }
47 
48   TCocoaGDIObject = class(TObject)
49   strict private
50     FRefCount: Integer;
51     FGlobal: Boolean;
52   public
53     constructor Create(AGlobal: Boolean); virtual;
54     destructor Destroy; override;
55 
UpdateRefsnull56     class function UpdateRefs(ATarget: TCocoaGDIObject; ASource: TCocoaGDIObject): Boolean; static;
57     procedure AddRef;
58     procedure Release;
59     property Global: Boolean read FGlobal write FGlobal;
60     property RefCount: Integer read FRefCount;
61   end;
62 
63   TCocoaRegionType = (
64     crt_Error,
65     crt_Empty,
66     crt_Rectangle,
67     crt_Complex);
68 
69   TCocoaCombine = (
70     cc_And,
71     cc_Xor,
72     cc_Or,
73     cc_Diff,
74     cc_Copy);
75 
76   { TCocoaRegion }
77 
78   //todo: Remove HIShape usage. HIShape is legacy
79   TCocoaRegion = class(TCocoaGDIObject)
80   strict private
81     FShape: HIShapeRef;
82   public
83     constructor CreateDefault(AGlobal: Boolean = False);
84     constructor Create(const X1, Y1, X2, Y2: Integer);
85     constructor Create(Points: PPoint; NumPts: Integer; isAlter: Boolean);
86     destructor Destroy; override;
87 
88     procedure Apply(ADC: TCocoaContext);
GetBoundsnull89     function GetBounds: TRect;
GetTypenull90     function GetType: TCocoaRegionType;
ContainsPointnull91     function ContainsPoint(const P: TPoint): Boolean;
92     procedure SetShape(AShape: HIShapeRef);
93     procedure Clear;
CombineWithnull94     function CombineWith(ARegion: TCocoaRegion; CombineMode: TCocoaCombine): TCocoaRegionType;
95     procedure Offset(dx, dy: Integer);
GetShapeCopynull96     function GetShapeCopy: HIShapeRef;
97     procedure MakeMutable;
98   public
99     property Shape: HIShapeRef read FShape write SetShape;
100   end;
101 
102   { TCocoaColorObject }
103 
104   TCocoaColorObject = class(TCocoaGDIObject)
105   strict private
106     FR, FG, FB: Byte;
107     FA: Boolean; // alpha: True - solid, False - clear
GetColorRefnull108     function GetColorRef: TColorRef;
109   public
110     constructor Create(const AColor: TColor; ASolid, AGlobal: Boolean); reintroduce;
111     procedure SetColor(const AColor: TColor; ASolid: Boolean);
112     procedure GetRGBA(AROP2: Integer; out AR, AG, AB, AA: CGFloat);
ObtainNSColornull113     function ObtainNSColor: NSColor;
114 
115     property Red: Byte read FR write FR;
116     property Green: Byte read FG write FG;
117     property Blue: Byte read FB write FB;
118     property Solid: Boolean read FA write FA;
119     property ColorRef: TColorRef read GetColorRef;
120   end;
121 
122   TCocoaPatternColorMode = (cpmBitmap, cpmBrushColor, cpmContextColor);
123 
124   { TCocoaBrush }
125 
126   TCocoaBrush = class(TCocoaColorObject)
127   strict private
128     FCGPattern: CGPatternRef;
129     FPatternColorMode: TCocoaPatternColorMode;
130     FBitmap: TCocoaBitmap;
131     FColor: NSColor;
132     FFgColor: TColorRef;
133   private
134     FImage: CGImageRef;
135     procedure DrawPattern(c: CGContextRef);
136   strict protected
137     procedure Clear;
138 
139     procedure SetHatchStyle(AHatch: PtrInt);
140     procedure SetBitmap(ABitmap: TCocoaBitmap);
141     procedure SetImage(AImage: NSImage);
142     procedure SetColor(AColor: NSColor); overload;
143   public
144     constructor CreateDefault(const AGlobal: Boolean = False);
145     constructor Create(const ALogBrush: TLogBrush; const AGlobal: Boolean = False);
146     constructor Create(const AColor: NSColor; const AGlobal: Boolean = False);
147     constructor Create(const AColor: TColor; AStyle: TFPBrushStyle; APattern: TBrushPattern;
148       AGlobal: Boolean = False);
149     destructor Destroy; override;
150     procedure Apply(ADC: TCocoaContext; UseROP2: Boolean = True);
151     procedure ApplyAsPenColor(ADC: TCocoaContext; UseROP2: Boolean = True);
152 
153     // for brushes created by NCColor
154     property Color: NSColor read FColor write SetColor;
155   end;
156 
157 type
158   TCocoaStatDashes = record
159     Len  : integer;
160     Dash : array [0..5] of CGFloat;
161   end;
162   PCocoaStatDashes = ^TCocoaStatDashes;
163 
164 const
165   CocoaPenDash : array [Boolean] of
166     array [PS_DASH..PS_DASHDOTDOT] of TCocoaStatDashes = (
167     // cosmetic = false (geometry)
168     (
169       (len: 2; dash: (2,2,0,0,0,0)), // PS_DASH        = 1;      { ------- }
170       (len: 2; dash: (0,2,0,0,0,0)), // PS_DOT         = 2;      { ....... }
171       (len: 4; dash: (2,2,0,2,0,0)), // PS_DASHDOT     = 3;      { _._._._ }
172       (len: 6; dash: (2,2,0,2,0,2))  // PS_DASHDOTDOT  = 4;      { _.._.._ }
173     ),
174     // cosmetic = true (windows like cosmetic)
175     (
176       (len: 2; dash: (18,6,0,0,0,0)), // PS_DASH        = 1;      { ------- }
177       (len: 2; dash: (3,3,0,0,0,0)),  // PS_DOT         = 2;      { ....... }
178       (len: 4; dash: (9,6,3,6,0,0)),  // PS_DASHDOT     = 3;      { _._._._ }
179       (len: 6; dash: (9,3,3,3,3,3))   // PS_DASHDOTDOT  = 4;      { _.._.._ }
180     )
181   );
182 
183 type
184   TCocoaDashes = array of CGFloat;
185 
186   { TCocoaPen }
187 
188   TCocoaPen = class(TCocoaColorObject)
189   strict private
190     FWidth: Integer;
191     FStyle: LongWord;
192     FIsExtPen: Boolean;
193     FIsGeometric: Boolean;
194     FEndCap: CGLineCap;
195     FJoinStyle: CGLineJoin;
196    public
197     Dashes: TCocoaDashes;
198     constructor CreateDefault(const AGlobal: Boolean = False);
199     constructor Create(const ALogPen: TLogPen; const AGlobal: Boolean = False);
200     constructor Create(dwPenStyle, dwWidth: DWord; const lplb: TLogBrush; dwStyleCount: DWord; lpStyle: PDWord);
201     constructor Create(const ABrush: TCocoaBrush; const AGlobal: Boolean = False);
202     constructor Create(const AColor: TColor; AGlobal: Boolean);
203     constructor Create(const AColor: TColor; AStyle: TFPPenStyle; ACosmetic: Boolean;
204       AWidth: Integer; AMode: TFPPenMode; AEndCap: TFPPenEndCap;
205       AJoinStyle: TFPPenJoinStyle; AGlobal: Boolean = False);
206     procedure Apply(ADC: TCocoaContext; UseROP2: Boolean = True);
207 
208     property Width: Integer read FWidth;
209     property Style: LongWord read FStyle;
210     property IsExtPen: Boolean read FIsExtPen;
211     property IsGeometric: Boolean read FIsGeometric;
212     property JoinStyle: CGLineJoin read FJoinStyle;
213     property CapStyle: CGLineCap read FEndCap;
214   end;
215 
216   { TCocoaFont }
217 
218   TCocoaFontStyle = set of (cfs_Bold, cfs_Italic, cfs_Underline, cfs_Strikeout);
219 
220   TCocoaFont = class(TCocoaGDIObject)
221   strict private
222     FFont: NSFont;
223     FName: AnsiString;
224     FSize: Integer;
225     FStyle: TCocoaFontStyle;
226     FAntialiased: Boolean;
227     FIsSystemFont: Boolean;
228     FRotationDeg: Single;
229   public
230     constructor CreateDefault(AGlobal: Boolean = False);
231     constructor Create(const ALogFont: TLogFont; AFontName: String; AGlobal: Boolean = False); reintroduce; overload;
232     constructor Create(const AFont: NSFont; AGlobal: Boolean = False); overload;
233     destructor Destroy; override;
CocoaFontWeightToWin32FontWeightnull234     class function CocoaFontWeightToWin32FontWeight(const CocoaFontWeight: Integer): Integer; static;
235     procedure SetHandle(ANewHandle: NSFont);
236     property Antialiased: Boolean read FAntialiased;
237     property Font: NSFont read FFont;
238     property Name: String read FName;
239     property Size: Integer read FSize;
240     property Style: TCocoaFontStyle read FStyle;
241     property RotationDeg: Single read FRotationDeg;
242   end;
243 
244   { TCocoaBitmap }
245 
246   TCocoaBitmap = class(TCocoaGDIObject)
247   strict private
248     FData: Pointer;
249     FOriginalData: PByte; // Exists and is set in case the data needed pre-multiplication
250     FAlignment: TCocoaBitmapAlignment;
251     FFreeData: Boolean;
252     FModified_SinceLastRecreate: Boolean;
253     FDataSize: Integer;
254     FBytesPerRow: Integer;
255     FDepth: Byte;
256     FBitsPerPixel: Byte;
257     FWidth: Integer;
258     FHeight: Integer;
259     FType: TCocoaBitmapType;
260     // Cocoa information
261     FBitsPerSample: NSInteger;  // How many bits in each color component
262     FSamplesPerPixel: NSInteger;// How many color components
263     FImage: NSImage;
264     FImagerep: NSBitmapImageRep;
GetColorSpacenull265     function GetColorSpace: NSString;
DebugShowDatanull266     function DebugShowData(): string;
267   public
268     constructor Create(ABitmap: TCocoaBitmap);
269     constructor Create(AWidth, AHeight, ADepth, ABitsPerPixel: Integer;
270       AAlignment: TCocoaBitmapAlignment; AType: TCocoaBitmapType;
271       AData: Pointer; ACopyData: Boolean = True);
272     constructor CreateDefault;
273     destructor Destroy; override;
274     procedure SetInfo(AWidth, AHeight, ADepth, ABitsPerPixel: Integer;
275       AAlignment: TCocoaBitmapAlignment; AType: TCocoaBitmapType);
276 
277     procedure CreateHandle();
278     procedure FreeHandle();
279     procedure ReCreateHandle();
280     procedure ReCreateHandle_IfModified();
281     procedure SetModified();
CreateSubImagenull282     function CreateSubImage(const ARect: TRect): CGImageRef;
CreateMaskImagenull283     function CreateMaskImage(const ARect: TRect): CGImageRef;
284     procedure PreMultiplyAlpha();
GetNonPreMultipliedDatanull285     function GetNonPreMultipliedData(): PByte;
286   public
287     property BitmapType: TCocoaBitmapType read FType;
288     property BitsPerPixel: Byte read FBitsPerPixel;
289     property BitsPerSample: NSInteger read FBitsPerSample;
290     property BytesPerRow: Integer read FBytesPerRow;
291     property Image: NSImage read FImage;
292     property ImageRep: NSBitmapImageRep read FImageRep;
293     property ColorSpace: NSString read GetColorSpace;
294     property Data: Pointer read FData;
295     property DataSize: Integer read FDataSize;
296     property Depth: Byte read FDepth;
297     property Width: Integer read FWidth;
298     property Height: Integer read FHeight;
299   end;
300 
301   { TCocoaCursor }
302 
303   TCocoaCursor = class(TObject)
304   strict private
305     FStandard: Boolean;
306     FBitmap: TCocoaBitmap;
307     FCursor: NSCursor;
308   public
309     constructor CreateStandard(const ACursor: NSCursor);
310     constructor CreateFromBitmap(const ABitmap: TCocoaBitmap; const hotSpot: NSPoint);
311     constructor CreateFromCustomCursor(const ACursor: NSCursor);
312     destructor Destroy; override;
Installnull313     function Install: TCocoaCursor;
314     procedure SetCursor;
315     class procedure SetDefaultCursor;
316     property Cursor: NSCursor read FCursor;
317     property Standard: Boolean read FStandard;
318   end;
319 
320 
321   // device context data for SaveDC/RestoreDC
322   TCocoaDCData = class
323   public
324     CurrentFont: TCocoaFont;
325     CurrentBrush: TCocoaBrush;
326     CurrentPen: TCocoaPen;
327     CurrentRegion: TCocoaRegion;
328 
329     BkColor: TColor;
330     BkMode: Integer;
331     BkBrush: TCocoaBrush;
332 
333     TextColor: TColor;
334 
335     ROP2: Integer;
336     PenPos: TPoint;
337     WindowOfs: TPoint;
338     ViewportOfs: TPoint;
339 
340     isClipped: Boolean;
341     ClipShape: HIShapeRef;
342 
343     destructor Destroy; override;
344   end;
345 
346   TGlyphArray = array of NSGlyph;
347 
348   { TCocoaTextLayout }
349 
350   TCocoaTextLayout = class
351   strict private
352     FBackgroundColor: TColor;
353     FForegroundColor: TColor;
354     FLayout: NSLayoutManager;
355     FTextStorage: NSTextStorage;
356     FTextContainer: NSTextContainer;
357     FText: String;
358     FFont: TCocoaFont;
359     // surrogate pairs (for UTF16)
360     FSurr: array of NSRange;
361     FSurrCount: Integer;
362     procedure SetBackgoundColor(AValue: TColor);
363     procedure SetForegoundColor(AValue: TColor);
364     procedure SetFont(AFont: TCocoaFont);
365     procedure UpdateFont;
366     procedure UpdateColor;
GetTextRangenull367     function GetTextRange: NSRange;
368 
369     procedure EvalSurrogate(s: NSString);
370   public
371     constructor Create;
372     destructor Destroy; override;
373     procedure SetText(UTF8Text: PChar; ByteSize: Integer);
GetSizenull374     function GetSize: TSize;
GetGlyphsnull375     function GetGlyphs: TGlyphArray;
376     procedure Draw(ctx: NSGraphicsContext; X, Y: Integer; FillBackground: Boolean; DX: PInteger);
377 
378     property Font: TCocoaFont read FFont write SetFont;
379     property BackgroundColor: TColor read FBackgroundColor write SetBackgoundColor;
380     property ForegroundColor: TColor read FForegroundColor write SetForegoundColor;
381     property Layout: NSLayoutManager read FLayout;
382   end;
383 
384   { TCocoaContext }
385 
386   TCocoaBitmapContext = class;
387   TCocoaContext = class(TObject)
388   private
389     FBkBrush: TCocoaBrush;
390     FBkColor: TColor;
391     FBkMode: Integer;
392     FROP2: Integer;
393     FText   : TCocoaTextLayout;
394     FBrush  : TCocoaBrush;
395     FPen    : TCocoaPen;
396     FRegion : TCocoaRegion;
397     // In Cocoa there is no way to enlarge a clip region :(
398     // see http://stackoverflow.com/questions/18648608/how-can-i-reset-or-clear-the-clipping-mask-associated-with-a-cgcontext
399     // So before every single clip operation we need to save the DC state
400     // And before every single clip operator or savedc/restoredc
401     // we need to restore the dc to clear the clipping region
402     //
403     // Also, because of bug 28015 FClipped cannot use ctx.Restore(Save)GraphicsState;
404     // it will use CGContextRestore(Save)GState(CGContext()); to save/restore DC instead
405     FClipped: Boolean;
406     FClipRegion: TCocoaRegion;
407     FSavedDCList: TFPObjectList;
408     FPenPos: TPoint;
409     FSize: TSize;
410     FViewPortOfs: TPoint;
411     FWindowOfs: TPoint;
412     boxview : NSBox; // the view is used to draw Frame3d
GetFontnull413     function GetFont: TCocoaFont;
GetTextColornull414     function GetTextColor: TColor;
415     procedure SetBkColor(AValue: TColor);
416     procedure SetBkMode(AValue: Integer);
417     procedure SetBrush(const AValue: TCocoaBrush);
418     procedure SetFont(const AValue: TCocoaFont);
419     procedure SetPen(const AValue: TCocoaPen);
420     procedure SetRegion(const AValue: TCocoaRegion);
421     procedure SetROP2(AValue: Integer);
422     procedure SetTextColor(AValue: TColor);
423 
424     procedure UpdateContextOfs(const AWindowOfs, AViewOfs: TPoint);
425     procedure SetViewPortOfs(AValue: TPoint);
426     procedure SetWindowOfs(AValue: TPoint);
427   protected
SaveDCDatanull428     function SaveDCData: TCocoaDCData; virtual;
429     procedure RestoreDCData(const AData: TCocoaDCData); virtual;
430     procedure SetCGFillping(Ctx: CGContextRef; Width, Height: CGFloat);
431     procedure RestoreCGFillping(Ctx: CGContextRef; Width, Height: CGFloat);
432     procedure ApplyTransform(Trans: CGAffineTransform);
433     procedure ClearClipping;
434     procedure AttachedBitmap_SetModified(); virtual;
435   public
436     ctx: NSGraphicsContext;
437     isControlDC: Boolean; // control DCs should never be freed by ReleaseDC as the control will free it by itself
438     isDesignDC: Boolean;  // this is a special Designer Overlay DC
439     constructor Create(AGraphicsContext: NSGraphicsContext); virtual;
440     destructor Destroy; override;
441 
SaveDCnull442     function SaveDC: Integer;
RestoreDCnull443     function RestoreDC(ASavedDC: Integer): Boolean;
444 
InitDrawnull445     function InitDraw(width, height: Integer): Boolean;
446 
447     // drawing functions
448     procedure DrawFocusRect(ARect: TRect);
449     procedure InvertRectangle(X1, Y1, X2, Y2: Integer);
450     procedure MoveTo(X, Y: Integer);
451     procedure LineTo(X, Y: Integer);
GetPixelnull452     function GetPixel(X,Y:integer): TColor; virtual;
453     procedure SetPixel(X,Y:integer; AColor:TColor); virtual;
454     procedure Polygon(const Points: array of TPoint; NumPts: Integer; Winding: boolean);
455     procedure Polyline(const Points: array of TPoint; NumPts: Integer);
456     // draws a rectangle by given LCL coordinates.
457     // always outlines rectangle
458     // if FillRect is set to true, then fills with either Context brush
459     // OR with "UseBrush" brush, if provided
460     // if FillRect is set to false, draws outlines only.
461     //   if "UseBrush" is not provided, uses the current pen
462     //   if "useBrush" is provided, uses the color from the defined brush
463     procedure Rectangle(X1, Y1, X2, Y2: Integer; FillRect: Boolean; UseBrush: TCocoaBrush);
464     procedure BackgroundFill(dirtyRect:NSRect);
465     procedure Ellipse(X1, Y1, X2, Y2: Integer);
466     procedure TextOut(X, Y: Integer; Options: Longint; Rect: PRect; UTF8Chars: PChar; Count: Integer; CharsDelta: PInteger);
467     procedure Frame(const R: TRect);
468     procedure Frame3d(var ARect: TRect; const FrameWidth: integer; const Style: TBevelCut);
469     procedure FrameRect(const ARect: TRect; const ABrush: TCocoaBrush);
470     procedure DrawBitmap(X, Y: Integer; ABitmap: TCocoaBitmap);
DrawImageRepnull471     function DrawImageRep(dstRect: NSRect; const srcRect: NSRect; ImageRep: NSBitmapImageRep): Boolean;
StretchDrawnull472     function StretchDraw(X, Y, Width, Height: Integer; SrcDC: TCocoaBitmapContext;
473       XSrc, YSrc, SrcWidth, SrcHeight: Integer; Msk: TCocoaBitmap; XMsk,
474       YMsk: Integer; Rop: DWORD): Boolean;
475 
GetTextExtentPointnull476     function GetTextExtentPoint(AStr: PChar; ACount: Integer; var Size: TSize): Boolean;
GetTextMetricsnull477     function GetTextMetrics(var TM: TTextMetric): Boolean;
478 
CGContextnull479     function CGContext: CGContextRef; virtual;
480     procedure SetAntialiasing(AValue: Boolean);
481 
GetLogicalOffsetnull482     function GetLogicalOffset: TPoint;
GetClipRectnull483     function GetClipRect: TRect;
SetClipRegionnull484     function SetClipRegion(AClipRegion: TCocoaRegion; Mode: TCocoaCombine): TCocoaRegionType;
CopyClipRegionnull485     function CopyClipRegion(ADstRegion: TCocoaRegion): TCocoaRegionType;
486 
487     property Clipped: Boolean read FClipped;
488     property PenPos: TPoint read FPenPos write FPenPos;
489     property ROP2: Integer read FROP2 write SetROP2;
490     property Size: TSize read FSize;
491     property WindowOfs: TPoint read FWindowOfs write SetWindowOfs;
492     property ViewPortOfs: TPoint read FViewPortOfs write SetViewPortOfs;
493 
494     property BkColor: TColor read FBkColor write SetBkColor;
495     property BkMode: Integer read FBkMode write SetBkMode;
496     property BkBrush: TCocoaBrush read FBkBrush;
497 
498     property TextColor: TColor read GetTextColor write SetTextColor;
499 
500     // selected GDI objects
501     property Brush: TCocoaBrush read FBrush write SetBrush;
502     property Pen: TCocoaPen read FPen write SetPen;
503     property Font: TCocoaFont read GetFont write SetFont;
504     property Region: TCocoaRegion read FRegion write SetRegion;
505   end;
506 
507   { TCocoaBitmapContext }
508 
509   TCocoaBitmapContext = class(TCocoaContext)
510   private
511     FBitmap : TCocoaBitmap;
512     procedure SetBitmap(const AValue: TCocoaBitmap);
513   protected
514     procedure AttachedBitmap_SetModified(); override;
515   public
516     constructor Create; reintroduce;
517     destructor Destroy; override;
GetPixelnull518     function GetPixel(X,Y:integer): TColor; override;
519     property Bitmap: TCocoaBitmap read FBitmap write SetBitmap;
520   end;
521 
522 var
523   DefaultBrush: TCocoaBrush;
524   DefaultPen: TCocoaPen;
525   DefaultFont: TCocoaFont;
526   DefaultBitmap: TCocoaBitmap;
527   DefaultContext: TCocoaBitmapContext;
528   ScreenContext: TCocoaContext;
529 
CheckDCnull530 function CheckDC(dc: HDC): TCocoaContext;
CheckDCnull531 function CheckDC(dc: HDC; Str: string): Boolean;
CheckGDIOBJnull532 function CheckGDIOBJ(obj: HGDIOBJ): TCocoaGDIObject;
CheckBitmapnull533 function CheckBitmap(ABitmap: HBITMAP; AStr: string): Boolean;
534 
535 type
536 
537   { LCLNSGraphicsContext }
538 
539   LCLNSGraphicsContext = objccategory (NSGraphicsContext)
lclCGContextnull540     function lclCGContext: CGContextRef; message 'lclCGContext';
541   end;
542 
543 implementation
544 
545 uses
546   CocoaInt;
547 
548 
549 { LCLNSGraphicsContext }
550 
LCLNSGraphicsContext.lclCGcontextnull551 function LCLNSGraphicsContext.lclCGcontext: CGContextRef;
552 begin
553   if NSAppKitVersionNumber >= NSAppKitVersionNumber10_10 then
554     Result := CGContext
555   else
556     Result := CGContextRef(graphicsPort);
557 end;
558 
559 //todo: a better check!
560 
CheckDCnull561 function CheckDC(dc: HDC): TCocoaContext;
562 begin
563   //Result := TCocoaContext(dc);
564   if TObject(dc) is TCocoaContext then
565     Result := TCocoaContext(dc)
566   else
567     Result := nil;
568 end;
569 
CheckDCnull570 function CheckDC(dc: HDC; Str: string): Boolean;
571 begin
572   //Result := dc<>0;
573   Result := (dc <> 0) and (TObject(dc) is TCocoaContext);
574 end;
575 
CheckGDIOBJnull576 function CheckGDIOBJ(obj: HGDIOBJ): TCocoaGDIObject;
577 begin
578   //Result := TObject(obj) as TCocoaGDIObject;
579   if TObject(obj) is TCocoaGDIObject then
580     Result := TCocoaGDIObject(obj)
581   else
582     Result := nil;
583 end;
584 
CheckBitmapnull585 function CheckBitmap(ABitmap: HBITMAP; AStr: string): Boolean;
586 begin
587   Result := ABitmap <> 0;
588 end;
589 
590 procedure GetWindowViewTranslate(const AWindowOfs, AViewOfs: TPoint; out dx, dy: Integer); inline;
591 begin
592   dx := AViewOfs.x - AWindowOfs.x;
593   dy := AViewOfs.y - AWindowOfs.y;
594 end;
595 
isSamePointnull596 function isSamePoint(const p1, p2: TPoint): Boolean; inline;
597 begin
598   Result:=(p1.x=p2.x) and (p1.y=p2.y);
599 end;
600 
601 { TCocoaFont }
602 
603 constructor TCocoaFont.CreateDefault(AGlobal: Boolean = False);
604 var Pool: NSAutoreleasePool;
605 begin
606   Pool := NSAutoreleasePool.alloc.init;
607   FIsSystemFont := True;
608   Create(NSFont.systemFontOfSize(0), AGlobal);
609   Pool.release;
610 end;
611 
612 constructor TCocoaFont.Create(const ALogFont: TLogFont; AFontName: String; AGlobal: Boolean);
613 var
614   FontName: NSString;
615   Descriptor: NSFontDescriptor;
616   Attributes: NSDictionary;
617   Pool: NSAutoreleasePool;
618   Win32Weight, LoopCount: Integer;
619   CocoaWeight: NSInteger;
620   FTmpFont: NSFont;
621   IsDefault: Boolean;
622 begin
623   inherited Create(AGlobal);
624 
625   Pool := NSAutoreleasePool.alloc.init;
626   try
627     FName := AFontName;
628 
629     // If we are using a "systemFont" font we need this complex shuffling,
630     // because otherwise the result is wrong in Mac OS X 10.11, see bug 30300
631     // Code used for 10.10 or inferior:
632     // FName := NSStringToString(NSFont.systemFontOfSize(0).familyName);
633     //
634     // There's a differnet issue with not using systemFont.
635     // NSComboBox, if assigned a manually created font have an odd ascending-offset
636     // (easily seen in Xcode interface builder as well). systemFonts()
637     // don't have such issue at all. see bug 33626
638     // the fix below (detecting "default" font and use systemFont()) is a potential
639     // regression for bug 30300.
640     //
641     // There might font properties (i.e. Transform Matrix) to adjust the position of
642     // the font. But at this time, it's safer to use systemFont() method
643     IsDefault := IsFontNameDefault(FName);
644     {if IsDefault then
645     begin
646       FTmpFont := NSFont.fontWithName_size(NSFont.systemFontOfSize(0).fontDescriptor.postscriptName, 0);
647       FName := NSStringToString(FTmpFont.familyName);
648     end;}
649 
650     if ALogFont.lfHeight = 0 then
651       FSize := Round(NSFont.systemFontSize)
652     else
653       FSize := Abs(ALogFont.lfHeight); // To-Do: emulate WinAPI difference between negative and absolute height values
654 
655     // create font attributes
656     Win32Weight := ALogFont.lfWeight;
657     FStyle := [];
658     if ALogFont.lfItalic > 0 then
659       include(FStyle, cfs_Italic);
660     if Win32Weight > FW_NORMAL then
661       include(FStyle, cfs_Bold);
662     if ALogFont.lfUnderline > 0 then
663       include(FStyle, cfs_Underline);
664     if ALogFont.lfStrikeOut > 0 then
665       include(FStyle, cfs_StrikeOut);
666 
667     // If this is not a "systemFont" Create the font ourselves
668     if IsDefault then
669     begin
670       FFont := NSFont.systemFontOfSize( FSize );
671     end else begin
672       FontName := NSStringUTF8(FName);
673       FFont := NSFont.fontWithName_size(FontName, FSize);
674       FontName.release;
675     end;
676 
677     if FFont = nil then
678     begin
679       // fallback to system font if not found (at least we can try to apply some of the other traits)
680       FName := NSStringToString(NSFont.systemFontOfSize(0).familyName);
681       FontName := NSStringUTF8(FName);
682       Attributes := NSDictionary.dictionaryWithObjectsAndKeys(
683                  FontName, NSFontFamilyAttribute,
684                  NSNumber.numberWithFloat(FSize), NSFontSizeAttribute,
685                  nil);
686       FontName.release;
687       Descriptor := NSFontDescriptor.fontDescriptorWithFontAttributes(Attributes);
688       FFont := NSFont.fontWithDescriptor_textTransform(Descriptor, nil);
689       if FFont = nil then
690       begin
691         exit;
692       end;
693     end;
694     // we could use NSFontTraitsAttribute to request the desired font style (Bold/Italic)
695     // but in this case we may get NIL as result. This way is safer.
696     if cfs_Italic in Style then
697       FFont := NSFontManager.sharedFontManager.convertFont_toHaveTrait(FFont, NSItalicFontMask)
698     else
699       FFont := NSFontManager.sharedFontManager.convertFont_toNotHaveTrait(FFont, NSItalicFontMask);
700     if cfs_Bold in Style then
701       FFont := NSFontManager.sharedFontManager.convertFont_toHaveTrait(FFont, NSBoldFontMask)
702     else
703       FFont := NSFontManager.sharedFontManager.convertFont_toNotHaveTrait(FFont, NSBoldFontMask);
704     case ALogFont.lfPitchAndFamily and $F of
705       FIXED_PITCH, MONO_FONT:
706         FFont := NSFontManager.sharedFontManager.convertFont_toHaveTrait(FFont, NSFixedPitchFontMask);
707       VARIABLE_PITCH:
708         FFont := NSFontManager.sharedFontManager.convertFont_toNotHaveTrait(FFont, NSFixedPitchFontMask);
709     end;
710     if Win32Weight <> FW_DONTCARE then
711     begin
712       // currently if we request the desired weight by Attributes we may get a nil font
713       // so we need to get font weight and to convert it to lighter/heavier
714       LoopCount := 0;
715       repeat
716         // protection from endless loop
717         if LoopCount > 12 then
718           Break;
719         CocoaWeight := CocoaFontWeightToWin32FontWeight(NSFontManager.sharedFontManager.weightOfFont(FFont));
720         if CocoaWeight < Win32Weight then
721           FFont := NSFontManager.sharedFontManager.convertWeight_ofFont(True, FFont)
722         else
723         if CocoaWeight > Win32Weight then
724           FFont := NSFontManager.sharedFontManager.convertWeight_ofFont(False, FFont);
725         inc(LoopCount);
726       until CocoaWeight = Win32Weight;
727     end;
728     FFont.retain;
729     FAntialiased := ALogFont.lfQuality <> NONANTIALIASED_QUALITY;
730 
731     FRotationDeg := ALogFont.lfEscapement / 10;
732   finally
733     Pool.release;
734   end;
735 end;
736 
737 constructor TCocoaFont.Create(const AFont: NSFont; AGlobal: Boolean = False);
738 begin
739   inherited Create(AGlobal);
740   SetHandle(AFont);
741 end;
742 
743 destructor TCocoaFont.Destroy;
744 begin
745   if Assigned(FFont) then
746     FFont.release;
747   inherited;
748 end;
749 
750 class function TCocoaFont.CocoaFontWeightToWin32FontWeight(const CocoaFontWeight: Integer): Integer; static;
751 begin
752   case CocoaFontWeight of
753     0, 1: Result := FW_THIN;
754     2: Result := FW_ULTRALIGHT;
755     3: Result := FW_EXTRALIGHT;
756     4: Result := FW_LIGHT;
757     5: Result := FW_NORMAL;
758     6: Result := FW_MEDIUM;
759     7, 8: Result := FW_SEMIBOLD;
760     9: Result := FW_BOLD;
761     10: Result := FW_EXTRABOLD;
762   else
763     Result := FW_HEAVY;
764   end;
765 end;
766 
767 procedure TCocoaFont.SetHandle(ANewHandle: NSFont);
768 var
769   pool: NSAutoreleasePool;
770   lsymTraits: NSFontSymbolicTraits;
771 begin
772   if FFont <> nil then
773   begin
774     FFont.release;
775   end;
776   Pool := NSAutoreleasePool.alloc.init;
777   FFont := ANewHandle;
778   FFont.retain;
779   FName := NSStringToString(FFont.familyName);
780   FSize := Round(FFont.pointSize);
781 
782   FStyle := [];
783   lsymTraits := FFont.fontDescriptor.symbolicTraits;
784   if (lsymTraits and NSFontBoldTrait) <> 0 then
785     Include(FStyle, cfs_Bold);
786   if (lsymTraits and NSFontItalicTrait) <> 0 then
787     Include(FStyle, cfs_Italic);
788 
789   FAntialiased := True;
790   Pool.release;
791 end;
792 
793 { TCocoaColorObject }
794 
GetColorRefnull795 function TCocoaColorObject.GetColorRef: TColorRef;
796 begin
797   Result := TColorRef(RGBToColor(FR, FG, FB));
798 end;
799 
800 constructor TCocoaColorObject.Create(const AColor: TColor; ASolid, AGlobal: Boolean);
801 begin
802   inherited Create(AGlobal);
803 
804   SetColor(AColor, ASolid);
805 end;
806 
807 procedure TCocoaColorObject.SetColor(const AColor: TColor; ASolid: Boolean);
808 begin
809   RedGreenBlue(ColorToRGB(AColor), FR, FG, FB);
810   FA := ASolid;
811 end;
812 
813 procedure TCocoaColorObject.GetRGBA(AROP2: Integer; out AR, AG, AB, AA: CGFloat);
814 var alpha:single;
815 begin
816   if FA then
817      alpha:=1
818   else
819      alpha:=0;
820 
821   case AROP2 of
822     R2_BLACK:
823     begin
824       AR := 0;
825       AG := 0;
826       AB := 0;
827       AA := alpha;
828     end;
829     R2_WHITE:
830     begin
831       AR := 1;
832       AG := 1;
833       AB := 1;
834       AA := alpha;
835     end;
836     R2_NOP:
837     begin
838       AR := 1;
839       AG := 1;
840       AB := 1;
841       AA := 0;
842     end;
843     R2_NOT, R2_NOTXORPEN:
844     begin
845       AR := 1;
846       AG := 1;
847       AB := 1;
848       AA := alpha;
849     end;
850     R2_NOTCOPYPEN:
851     begin
852       AR := (255 - FR) / 255;
853       AG := (255 - FG) / 255;
854       AB := (255 - FB) / 255;
855       AA := alpha;
856     end;
857   else // copy
858     begin
859       AR := FR / 255;
860       AG := FG / 255;
861       AB := FB / 255;
862       AA := alpha;
863     end;
864   end;
865 end;
866 
ObtainNSColornull867 function TCocoaColorObject.ObtainNSColor: NSColor;
868 begin
869   Result := NSColor.colorWithCalibratedRed_green_blue_alpha(FR / 255, FG / 255, FB / 255, Byte(FA));
870 end;
871 
872 {------------------------------------------------------------------------------
873   Method:  TCocoaBitmap.Create
874   Params:  AWidth        - Bitmap width
875            AHeight       - Bitmap height
876            ADepth        - Significant bits per pixel
877            ABitsPerPixel - The number of allocated bits per pixel (can be larger than depth)
878 //           AAlignment    - Alignment of the data for each row
879 //           ABytesPerRow  - The number of bytes between rows
880            ACopyData     - Copy supplied bitmap data (OPTIONAL)
881 
882   Creates Cocoa bitmap with the specified characteristics
883  ------------------------------------------------------------------------------}
884 constructor TCocoaBitmap.Create(AWidth, AHeight, ADepth, ABitsPerPixel: Integer;
885   AAlignment: TCocoaBitmapAlignment; AType: TCocoaBitmapType;
886   AData: Pointer; ACopyData: Boolean);
887 
888 type
889   TColorEntry = packed record
890     C1, C2, C3, C4: Byte;
891   end;
892   PColorEntry = ^TColorEntry;
893 
894   TColorEntryArray = array[0..MaxInt div SizeOf(TColorEntry) - 1] of TColorEntry;
895   PColorEntryArray = ^TColorEntryArray;
896 
897 
898   procedure CopySwappedColorComponents(ASrcData, ADestData: PColorEntryArray; ADataSize: Integer; AType: TCocoaBitmapType);
899   var
900     I: Integer;
901   begin
902     //switch B and R components
903     for I := 0 to ADataSize div SizeOf(TColorEntry) - 1 do
904     begin
905       case AType of
906         cbtABGR:
907         begin
908           ADestData^[I].C1 := ASrcData^[I].C1;
909           ADestData^[I].C2 := ASrcData^[I].C4;
910           ADestData^[I].C3 := ASrcData^[I].C3;
911           ADestData^[I].C4 := ASrcData^[I].C2;
912         end;
913         cbtBGRA:
914         begin
915           ADestData^[I].C1 := ASrcData^[I].C3;
916           ADestData^[I].C2 := ASrcData^[I].C2;
917           ADestData^[I].C3 := ASrcData^[I].C1;
918           ADestData^[I].C4 := ASrcData^[I].C4;
919         end;
920       end;
921     end;
922   end;
923 
924 begin
925   inherited Create(False);
926   {$ifdef VerboseBitmaps}
927   DebugLn(Format('[TCocoaBitmap.Create] AWidth=%d AHeight=%d ADepth=%d ABitsPerPixel=%d'
928     + ' AAlignment=%d AType=%d AData=? ACopyData=%d',
929     [AWidth, AHeight, ADepth, ABitsPerPixel, Integer(AAlignment), Integer(AType), Integer(ACopyData)]));
930   {$endif}
931   SetInfo(AWidth, AHeight, ADepth, ABitsPerPixel, AAlignment, AType);
932 
933   // Copy the image data, if necessary
934   if (AData = nil) or ACopyData then
935   begin
936     System.GetMem(FData, FDataSize);
937     FFreeData := True;
938     if AData <> nil then
939     begin
940       if AType in [cbtABGR, cbtBGRA] then
941       begin
942         Assert(AWidth * AHeight * SizeOf(TColorEntry) = FDataSize);
943         CopySwappedColorComponents(AData, FData, FDataSize, AType);
944       end
945       else
946         System.Move(AData^, FData^, FDataSize) // copy data
947     end
948     else
949       FillDWord(FData^, FDataSize shr 2, 0); // clear bitmap
950   end
951   else
952   begin
953     FData := AData;
954     FFreeData := False;
955   end;
956 
957   CreateHandle();
958 end;
959 
960 constructor TCocoaBitmap.CreateDefault;
961 begin
962   Create(1, 1, 32, 32, cbaByte, cbtARGB, nil);
963 end;
964 
965 destructor TCocoaBitmap.Destroy;
966 begin
967   FreeHandle();
968   if FFreeData then System.FreeMem(FData);
969   if FOriginalData <> nil then
970     System.FreeMem(FOriginalData);
971 
972   inherited Destroy;
973 end;
974 
975 procedure TCocoaBitmap.SetInfo(AWidth, AHeight, ADepth,
976   ABitsPerPixel: Integer; AAlignment: TCocoaBitmapAlignment;
977   AType: TCocoaBitmapType);
978 const
979   ALIGNBITS: array[TCocoaBitmapAlignment] of Integer = (0, 1, 3, 7, $F);
980 var
981   M: Integer;
982 begin
983   //WriteLn('[TCocoaBitmap.SetInfo] AWidth=', AWidth, ' AHeight=', AHeight,
984   //  ' ADepth=', ADepth, ' ABitsPerPixel=', ABitsPerPixel);
985   if AWidth < 1 then AWidth := 1;
986   if AHeight < 1 then AHeight := 1;
987   FWidth := AWidth;
988   FHeight := AHeight;
989   FDepth := ADepth;
990   FBitsPerPixel := ABitsPerPixel;
991   FType := AType;
992   FAlignment := AAlignment;
993 
994   if (FType in [cbtMono, cbtGray]) and (FDepth=0) then
995     FDepth := FBitsPerPixel;
996 
997   FBytesPerRow := ((AWidth * ABitsPerPixel) + 7) shr 3;
998   M := FBytesPerRow and ALIGNBITS[AAlignment];
999   if M <> 0 then Inc(FBytesPerRow, ALIGNBITS[AAlignment] + 1 - M);
1000 
1001   FDataSize := FBytesPerRow * FHeight;
1002 
1003   // Cocoa information
1004   case ABitsPerPixel of
1005     // Strangely, this might appear
1006     0:
1007     begin
1008       FBitsPerSample := 0;
1009       FSamplesPerPixel := 0;
1010     end;
1011     // Mono
1012     1:
1013     begin
1014       FBitsPerSample := 1;
1015       FSamplesPerPixel := 1;
1016     end;
1017     // Gray scale
1018     8:
1019     begin
1020       FBitsPerSample := 8;
1021       FSamplesPerPixel := 1;
1022     end;
1023     // ARGB
1024     32:
1025     begin
1026       FBitsPerSample := 8;
1027       if AType = cbtRGB then
1028         FSamplesPerPixel := 3
1029       else
1030         FSamplesPerPixel := 4;
1031     end;
1032   else
1033     // Other RGB
1034     FBitsPerSample := ABitsPerPixel div 3;
1035     FSamplesPerPixel := 3;
1036   end;
1037 end;
1038 
1039 procedure TCocoaBitmap.CreateHandle();
1040 var
1041   HasAlpha: Boolean;
1042   BitmapFormat: NSBitmapFormat;
1043 begin
1044   HasAlpha := FType in [cbtARGB, cbtRGBA, cbtABGR, cbtBGRA];
1045   // Non premultiplied bitmaps can't be used for bitmap context
1046   // So we need to pre-multiply ourselves, but only if we were allowed
1047   // to copy the data, otherwise we might corrupt the original
1048   if FFreeData then
1049     PreMultiplyAlpha();
1050   BitmapFormat := 0;
1051   if FType in [cbtARGB, cbtABGR, cbtRGB] then
1052     BitmapFormat := BitmapFormat or NSAlphaFirstBitmapFormat;
1053 
1054   //WriteLn('[TCocoaBitmap.Create] FSamplesPerPixel=', FSamplesPerPixel,
1055   //  ' FData=', DebugShowData());
1056 
1057   // Create the associated NSImageRep
1058   Assert(FImagerep = nil);
1059   FImagerep := NSBitmapImageRep(NSBitmapImageRep.alloc.initWithBitmapDataPlanes_pixelsWide_pixelsHigh__colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel(
1060     @FData, // planes, BitmapDataPlanes
1061     FWidth, // width, pixelsWide
1062     FHeight,// height, PixelsHigh
1063     FBitsPerSample,// bitsPerSample, bps
1064     FSamplesPerPixel, // samplesPerPixel, spp
1065     HasAlpha, // hasAlpha
1066     False, // isPlanar
1067     GetColorSpace, // colorSpaceName
1068     BitmapFormat, // bitmapFormat
1069     FBytesPerRow, // bytesPerRow
1070     FBitsPerPixel //bitsPerPixel
1071     ));
1072 
1073   // Create the associated NSImage
1074   Assert(FImage = nil);
1075   FImage := NSImage.alloc.initWithSize(NSMakeSize(FWidth, FHeight));
1076   //pool := NSAutoreleasePool.alloc.init;
1077   Image.addRepresentation(Imagerep);
1078   //pool.release;
1079 end;
1080 
1081 procedure TCocoaBitmap.FreeHandle;
1082 begin
1083   if FImage <> nil then
1084   begin
1085     FImage.release;
1086     FImage := nil;
1087   end;
1088   if FImageRep <> nil then
1089   begin
1090     FImageRep.release;
1091     FImageRep := nil;
1092   end;
1093 end;
1094 
1095 procedure TCocoaBitmap.ReCreateHandle;
1096 begin
1097   FreeHandle();
1098   if (FOriginalData <> nil) and (FData <> nil) then // fix bug 28692
1099     System.Move(FOriginalData^, FData^, FDataSize);
1100   CreateHandle();
1101 end;
1102 
1103 procedure TCocoaBitmap.ReCreateHandle_IfModified;
1104 begin
1105   if FModified_SinceLastRecreate then
1106     ReCreateHandle();
1107   FModified_SinceLastRecreate := False;
1108 end;
1109 
1110 procedure TCocoaBitmap.SetModified;
1111 begin
1112   if FOriginalData <> nil then
1113   begin
1114     // the original data no longer applies, as imageRep was modified
1115     System.FreeMem(FOriginalData);
1116     FOriginalData:=nil;
1117   end;
1118   FModified_SinceLastRecreate := True;
1119 end;
1120 
CreateSubImagenull1121 function TCocoaBitmap.CreateSubImage(const ARect: TRect): CGImageRef;
1122 begin
1123   if ImageRep = nil then
1124     Result := nil
1125   else
1126     Result := CGImageCreateWithImageInRect(MacOSAll.CGImageRef(ImageRep.CGImage), RectToCGRect(ARect));
1127 end;
1128 
1129 
TCocoaBitmap.CreateMaskImagenull1130 function TCocoaBitmap.CreateMaskImage(const ARect: TRect): CGImageRef;
1131 var
1132   CGDataProvider: CGDataProviderRef;
1133   Mask: CGImageRef;
1134 begin
1135   CGDataProvider := CGDataProviderCreateWithData(nil, FData, FDataSize, nil);
1136   try
1137     Mask := CGImageMaskCreate(FWidth, FHeight, FBitsPerPixel,
1138       FBitsPerPixel, FBytesPerRow, CGDataProvider, nil, 0);
1139     Result := CGImageCreateWithImageInRect(Mask, RectToCGRect(ARect));
1140   finally
1141     CGDataProviderRelease(CGDataProvider);
1142     CGImageRelease(Mask);
1143   end;
1144 end;
1145 
TCocoaBitmap.GetColorSpacenull1146 function TCocoaBitmap.GetColorSpace: NSString;
1147 begin
1148   if FType in [cbtMono, cbtGray] then
1149     Result := NSDeviceWhiteColorSpace
1150   else
1151     Result := NSDeviceRGBColorSpace;
1152 end;
1153 
1154 // Cocoa cannot create a context unless the image has alpha pre-multiplied
1155 procedure TCocoaBitmap.PreMultiplyAlpha;
1156 var
1157   lByteData: PByte;
1158   i: Integer;
1159   lAlpha, lRed, lGreen, lBlue: Byte;
1160 begin
1161   if not (FType in [cbtARGB, cbtRGBA]) then Exit;
1162   if FData = nil then Exit;
1163 
1164   // Keep the original data in a copy, otherwise we cant get access to it
1165   // because pre-multiplying destroys the original value if we had alpha=0
1166   if FOriginalData <> nil then
1167     System.FreeMem(FOriginalData);
1168   System.GetMem(FOriginalData, FDataSize);
1169   System.Move(FData^, FOriginalData^, FDataSize); // copy data
1170 
1171   // Pre-Multiply
1172   lByteData := PByte(FData);
1173   i := 0;
1174   while i < FDataSize - 3 do
1175   begin
1176     if FType = cbtARGB then
1177     begin
1178       lAlpha := lByteData[i];
1179       lRed := lByteData[i+1];
1180       lGreen := lByteData[i+2];
1181       lBlue := lByteData[i+3];
1182 
1183       lByteData[i+1] := (lRed * lAlpha) div $FF;
1184       lByteData[i+2] := (lGreen * lAlpha) div $FF;
1185       lByteData[i+3] := (lBlue * lAlpha) div $FF;
1186     end
1187     else if FType = cbtRGBA then
1188     begin
1189       lAlpha := lByteData[i+3];
1190       lRed := lByteData[i];
1191       lGreen := lByteData[i+1];
1192       lBlue := lByteData[i+2];
1193 
1194       lByteData[i] := (lRed * lAlpha) div $FF;
1195       lByteData[i+1] := (lGreen * lAlpha) div $FF;
1196       lByteData[i+2] := (lBlue * lAlpha) div $FF;
1197     end;
1198 
1199     Inc(i, 4);
1200   end;
1201 end;
1202 
1203 // The Alpha pre-multiplication will prevent us from obtaining the original image
RawImage_FromCocoaBitmapnull1204 // raw data for the function RawImage_FromCocoaBitmap,
1205 // so we need to store it
1206 function TCocoaBitmap.GetNonPreMultipliedData(): PByte;
1207 begin
1208   if FOriginalData <> nil then
1209     Result := FOriginalData
1210   else
1211     Result := PByte(FData);
1212 end;
1213 
DebugShowDatanull1214 function TCocoaBitmap.DebugShowData: string;
1215 var
1216   i: Integer;
1217 begin
1218   Result := '';
1219   for i := 0 to FDataSize -1 do
1220   begin
1221     Result := Result + IntToHex(PByte(FData)[i], 2);
1222     if i mod 4 = 3 then
1223       Result := Result + ' - '
1224   end;
1225 end;
1226 
1227 constructor TCocoaBitmap.Create(ABitmap: TCocoaBitmap);
1228 begin
1229   Create(ABitmap.Width, ABitmap.Height, ABitmap.Depth, ABitmap.FBitsPerPixel,
1230     ABitmap.FAlignment, ABitmap.FType, ABitmap.Data);
1231 end;
1232 
1233 { TCocoaCursor }
1234 constructor TCocoaCursor.CreateStandard(const ACursor: NSCursor);
1235 begin
1236   FBitmap := nil;
1237   FCursor := ACursor;
1238   FStandard := True;
1239 end;
1240 
1241 constructor TCocoaCursor.CreateFromBitmap(const ABitmap: TCocoaBitmap; const hotSpot: NSPoint);
1242 begin
1243   FBitmap := ABitmap;            // takes ownership, no ref count change required
1244   FCursor := NSCursor.alloc.initWithImage_hotSpot(ABitmap.Image, hotSpot);
1245   FStandard := False;
1246 end;
1247 
1248 constructor TCocoaCursor.CreateFromCustomCursor(const ACursor: NSCursor);
1249 begin
1250   FCursor := ACursor;
1251   FStandard := False;
1252 end;
1253 
1254 destructor TCocoaCursor.Destroy;
1255 begin
1256   FreeAndNil(FBitmap);
1257   if not Standard then
1258     FCursor.release;
1259   inherited;
1260 end;
1261 
TCocoaCursor.Installnull1262 function TCocoaCursor.Install: TCocoaCursor;
1263 begin
1264   FCursor.push;
1265   // also request form cursors invalidation
1266   CocoaWidgetSet.NSApp.keyWindow.resetCursorRects;
1267   Result := nil;
1268 end;
1269 
1270 procedure TCocoaCursor.SetCursor;
1271 begin
1272  FCursor.set_;
1273 end;
1274 
1275 class procedure TCocoaCursor.SetDefaultCursor;
1276 begin
1277  NSCursor.arrowCursor.set_;
1278 end;
1279 
1280 { TCocoaTextLayout }
1281 
1282 procedure TCocoaTextLayout.UpdateFont;
1283 const
1284   UnderlineStyle = NSUnderlineStyleSingle or NSUnderlinePatternSolid;
1285 var
1286   Range: NSRange;
1287 begin
1288   if Assigned(FFont) then
1289   begin
1290     Range := GetTextRange;
1291     if (Range.length <= 0) or (FFont.Font = nil) then Exit;
1292     // apply font itself
1293     FTextStorage.addAttribute_value_range(NSFontAttributeName, FFont.Font, Range);
1294     // aply font attributes which are not in NSFont
1295     if cfs_Underline in FFont.Style then
1296       FTextStorage.addAttribute_value_range(NSUnderlineStyleAttributeName, NSNumber.numberWithInteger(UnderlineStyle), Range)
1297      else
1298      FTextStorage.removeAttribute_range(NSUnderlineStyleAttributeName, Range);
1299 
1300     if cfs_Strikeout in FFont.Style then
1301       FTextStorage.addAttribute_value_range(NSStrikethroughStyleAttributeName, NSNumber.numberWithInteger(UnderlineStyle), Range)
1302     else
1303       FTextStorage.removeAttribute_range(NSStrikethroughStyleAttributeName, Range);
1304   end;
1305 end;
1306 
1307 procedure TCocoaTextLayout.UpdateColor;
1308 begin
1309   FTextStorage.addAttribute_value_range(NSForegroundColorAttributeName, ColorToNSColor(ForegroundColor), GetTextRange);
1310   FTextStorage.addAttribute_value_range(NSBackgroundColorAttributeName, ColorToNSColor(BackgroundColor), GetTextRange);
1311 end;
1312 
TCocoaTextLayout.GetTextRangenull1313 function TCocoaTextLayout.GetTextRange: NSRange;
1314 begin
1315   Result.location := 0;
1316   Result.length := FTextStorage.length;
1317 end;
1318 
1319 procedure TCocoaTextLayout.EvalSurrogate(s: NSString);
1320 var
1321   res  : NSRange;
1322   i    : integer;
1323   ln   : integer;
1324   scnt : integer;
1325   ch   : integer;
1326 begin
1327   FSurrCount := 0;
1328   i := 0;
1329   ln := s.length;
1330   // must analyze the string to presence of surrogate pairs.
1331   // this is required for the use
1332   ch := 0;
1333   while i < ln do
1334   begin
1335     res := s.rangeOfComposedCharacterSequenceAtIndex(i); //s.rangeOfComposedCharacterSequencesForRange(src);
1336     inc(i, res.length);
1337     if res.length>1 then
1338     begin
1339       if length(FSurr) = FSurrCount then
1340       begin
1341         if FSurrCount = 0 then SetLength(FSurr, 4)
1342         else SetLength(FSurr, FSurrCount * 2)
1343       end;
1344       FSurr[FSurrCount] := res;
1345       inc(fSurrCount);
1346     end;
1347     inc(ch);
1348   end;
1349   if ((FSurrCount = 0) and (length(FSurr)<>0)) or (length(FSurr) div 2>FSurrCount) then
1350     SetLength(FSurr, FSurrCount);
1351 end;
1352 
1353 procedure TCocoaTextLayout.SetForegoundColor(AValue: TColor);
1354 begin
1355   if FForegroundColor <> AValue then
1356   begin
1357     FForegroundColor := AValue;
1358     FTextStorage.beginEditing;
1359     UpdateColor;
1360     FTextStorage.endEditing;
1361   end;
1362 end;
1363 
1364 procedure TCocoaTextLayout.SetBackgoundColor(AValue: TColor);
1365 begin
1366   if FBackgroundColor <> AValue then
1367   begin
1368     FBackgroundColor := AValue;
1369     FTextStorage.beginEditing;
1370     UpdateColor;
1371     FTextStorage.endEditing;
1372   end;
1373 end;
1374 
1375 constructor TCocoaTextLayout.Create;
1376 var
1377   LocalPool: NSAutoReleasePool;
1378 begin
1379   inherited Create;
1380   LocalPool := NSAutoReleasePool.alloc.init;
1381   FTextStorage := NSTextStorage.alloc.initWithString(NSSTR(''));
1382   FLayout := NSLayoutManager.alloc.init;
1383   FTextStorage.addLayoutManager(FLayout);
1384   FTextContainer := NSTextContainer.alloc.init;
1385   FTextContainer.setLineFragmentPadding(0);
1386   FLayout.addTextContainer(FTextContainer);
1387 
1388   LocalPool.release;
1389 
1390   FFont := DefaultFont;
1391   FFont.AddRef;
1392   FText := '';
1393   FBackgroundColor := clWhite;
1394   FForegroundColor := clBlack;
1395 end;
1396 
1397 destructor TCocoaTextLayout.Destroy;
1398 begin
1399   FLayout.release;
1400   FTextContainer.release;
1401   FTextStorage.release;
1402   if Assigned(FFont) then
1403     FFont.Release;
1404   inherited Destroy;
1405 end;
1406 
1407 procedure TCocoaTextLayout.SetFont(AFont: TCocoaFont);
1408 begin
1409   if TCocoaGDIObject.UpdateRefs(FFont, AFont) then
1410   begin
1411     FFont := AFont as TCocoaFont;
1412     FTextStorage.beginEditing;
1413     updateFont;
1414     FTextStorage.endEditing;
1415   end;
1416 end;
1417 
1418 procedure TCocoaTextLayout.SetText(UTF8Text: PChar; ByteSize: Integer);
1419 var
1420   NewText: String;
1421   S: NSString;
1422 begin
1423   if ByteSize >= 0 then
1424     System.SetString(NewText, UTF8Text, ByteSize)
1425   else
1426     NewText := StrPas(UTF8Text);
1427   if FText <> NewText then
1428   begin
1429     FText := NewText;
1430     S := NSStringUTF8(NewText);
1431     try
1432       FSurrCount:=-1; // invalidating surragete pair search
1433       FTextStorage.beginEditing;
1434       if S <> nil then
1435         FTextStorage.replaceCharactersInRange_withString(GetTextRange, S);
1436       updateFont;
1437       updateColor;
1438       FTextStorage.endEditing;
1439     except
1440     end;
1441     S.release;
1442   end;
1443 end;
1444 
GetSizenull1445 function TCocoaTextLayout.GetSize: TSize;
1446 var
1447   Range: NSRange;
1448   bnds: NSRect;
1449 begin
1450   Range := FLayout.glyphRangeForTextContainer(FTextContainer);
1451   //for text with soft-breaks (#13) the vertical bounds is too high!
1452   //(feels like it tryes to span it from top to bottom)
1453   //bnds := FLayout.boundingRectForGlyphRange_inTextContainer(Range, FTextContainer);
1454   bnds := FLayout.usedRectForTextContainer(FTextContainer);
1455   Result.cx := Round(bnds.size.width);
1456   Result.cy := Round(bnds.size.height);
1457 end;
1458 
TCocoaTextLayout.GetGlyphsnull1459 function TCocoaTextLayout.GetGlyphs: TGlyphArray;
1460 var
1461   Range: NSRange;
1462 begin
1463   Range := FLayout.glyphRangeForTextContainer(FTextContainer);
1464   // required length + 1 space
1465   SetLength(Result, Range.length + 1);
1466   FLayout.getGlyphs_range(@Result[0], Range);
1467   SetLength(Result, Range.length);
1468 end;
1469 
1470 procedure TCocoaTextLayout.Draw(ctx: NSGraphicsContext; X, Y: Integer; FillBackground: Boolean; DX: PInteger);
1471 var
1472   Range: NSRange;
1473   Pt: NSPoint;
1474   Context: NSGraphicsContext;
1475   Locations: array of NSPoint;
1476   Indexes: array of NSUInteger;
1477   I,j, Count, ii: NSUInteger;
1478   si: Integer;
1479   transform : NSAffineTransform;
1480 begin
1481   Range := FLayout.glyphRangeForTextContainer(FTextContainer);
1482   if Range.length = 0 then
1483     Exit; // cannot render anything. string is empty or invalid characters
1484 
1485   if not ctx.isFlipped then
1486     Context := NSGraphicsContext.graphicsContextWithGraphicsPort_flipped(ctx.graphicsPort, True)
1487   else
1488     Context := ctx;
1489 
1490   NSGraphicsContext.classSaveGraphicsState;
1491   NSGraphicsContext.setCurrentContext(Context);
1492   ctx.setShouldAntialias(FFont.Antialiased);
1493   if FFont.RotationDeg<>0 then
1494   begin
1495     transform := NSAffineTransform.transform;
1496     transform.translateXBy_yBy(X, Y);
1497     if ctx.isFlipped then
1498       transform.rotateByDegrees( FFont.RotationDeg )
1499     else
1500       transform.rotateByDegrees( -FFont.RotationDeg );
1501     transform.translateXBy_yBy(-X, -Y);
1502     transform.concat;
1503   end;
1504 
1505   Pt.x := X;
1506   Pt.y := Y;
1507   if Assigned(DX) then
1508   begin
1509     // DX - is provided for UTF8 characters. UTF8 doesn't have surrogate pairs
1510     // UTF16 does. UTF16 is the base for NSTextLayout and NSString.
1511     // Thus for any character in DX, there might be mulitple "characeters"
1512     // in NSTextLayout. See #35675.
1513     if FSurrCount<0 then EvalSurrogate(FTextStorage.string_);
1514 
1515     Count := Range.length;
1516     SetLength(Locations, Count);
1517     SetLength(Indexes, Count);
1518     Locations[0] := FLayout.locationForGlyphAtIndex(0);
1519     Indexes[0] := 0;
1520     for I := 1 to Count - 1 do Indexes[I] := I;
1521 
1522     // no surrogate pairs
1523     I := 1;
1524     j := 0;
1525     if FSurrCount > 0 then
1526     begin
1527       si := 0;
1528       for si:=0 to FSurrCount - 1 do
1529       begin
1530         for ii := i to FSurr[si].location do
1531         begin
1532           Locations[I] := Locations[I - 1];
1533           Locations[I].x := Locations[I].x + DX[J];
1534           inc(i);
1535           inc(j);
1536         end;
1537         for ii := 2 to FSurr[si].length do
1538         begin
1539           Locations[I] := Locations[I - 1];
1540           inc(I);
1541         end;
1542       end;
1543     end;
1544 
1545     // remaining DX offsets
1546     for I := I to Count - 1 do
1547     begin
1548       Locations[I] := Locations[I - 1];
1549       Locations[I].x := Locations[I].x + DX[J];
1550       inc(j);
1551     end;
1552     FLayout.setLocations_startingGlyphIndexes_count_forGlyphRange(@Locations[0], @Indexes[0], Count, Range);
1553   end;
1554 
1555   if FillBackground then
1556     FLayout.drawBackgroundForGlyphRange_atPoint(Range, Pt);
1557   FLayout.drawGlyphsForGlyphRange_atPoint(Range, Pt);
1558   NSGraphicsContext.classRestoreGraphicsState;
1559 end;
1560 
1561 { TCocoaContext }
1562 
CGContextnull1563 function TCocoaContext.CGContext: CGContextRef;
1564 begin
1565   Result := CGContextRef(ctx.lclCGContext);
1566 end;
1567 
1568 procedure TCocoaContext.SetAntialiasing(AValue: Boolean);
1569 begin
1570   if not AValue then
1571     ctx.setImageInterpolation(NSImageInterpolationNone)
1572   else
1573     ctx.setImageInterpolation(NSImageInterpolationDefault);
1574   ctx.setShouldAntialias(AValue);
1575 end;
1576 
GetLogicalOffsetnull1577 function TCocoaContext.GetLogicalOffset: TPoint;
1578 begin
1579   GetWindowViewTranslate(WindowOfs, ViewportOfs, Result.X, Result.Y);
1580 end;
1581 
GetClipRectnull1582 function TCocoaContext.GetClipRect: TRect;
1583 begin
1584   Result := CGRectToRect(CGContextGetClipBoundingBox(CGContext));
1585 end;
1586 
SetClipRegionnull1587 function TCocoaContext.SetClipRegion(AClipRegion: TCocoaRegion; Mode: TCocoaCombine): TCocoaRegionType;
1588 begin
1589   ClearClipping;
1590   FClipped := False;
1591 
1592   if not Assigned(AClipRegion) then
1593     FClipRegion.Clear
1594   else
1595   begin
1596     CGContextSaveGState(CGContext());
1597     FClipRegion.CombineWith(AClipRegion, Mode);
1598     FClipRegion.Apply(Self);
1599     FClipped := True;
1600   end;
1601   Result := FClipRegion.GetType;
1602 end;
1603 
CopyClipRegionnull1604 function TCocoaContext.CopyClipRegion(ADstRegion: TCocoaRegion): TCocoaRegionType;
1605 begin
1606   if Assigned(ADstRegion) then
1607     Result := ADstRegion.CombineWith(FClipRegion, cc_Copy)
1608   else
1609     Result := crt_Error;
1610 end;
1611 
GetTextColornull1612 function TCocoaContext.GetTextColor: TColor;
1613 begin
1614   Result := FText.ForegroundColor;
1615 end;
1616 
GetFontnull1617 function TCocoaContext.GetFont: TCocoaFont;
1618 begin
1619   Result := FText.Font;
1620 end;
1621 
1622 procedure TCocoaContext.SetBkColor(AValue: TColor);
1623 begin
1624   AValue := ColorToRGB(AValue);
1625   FBkColor := AValue;
1626   FBkBrush.SetColor(AValue, BkMode = OPAQUE);
1627 end;
1628 
1629 procedure TCocoaContext.SetBkMode(AValue: Integer);
1630 begin
1631   if FBkMode <> AValue then
1632   begin
1633     FBkMode := AValue;
1634     FBkBrush.SetColor(FBkColor, FBkMode = OPAQUE);
1635   end;
1636 end;
1637 
1638 procedure TCocoaContext.SetBrush(const AValue: TCocoaBrush);
1639 begin
1640   if TCocoaGDIObject.UpdateRefs(FBrush, AValue) then
1641   begin
1642     FBrush := AValue;
1643     if Assigned(FBrush) then FBrush.Apply(Self);
1644   end;
1645 end;
1646 
1647 procedure TCocoaContext.SetFont(const AValue: TCocoaFont);
1648 begin
1649   FText.Font := AValue;        // UpdateRefs done within property setter
1650 end;
1651 
1652 procedure TCocoaContext.SetPen(const AValue: TCocoaPen);
1653 begin
1654   if TCocoaGDIObject.UpdateRefs(FPen, AValue) then
1655   begin
1656     FPen := AValue;
1657     if Assigned(FPen) then FPen.Apply(Self);
1658   end;
1659 end;
1660 
1661 procedure TCocoaContext.SetRegion(const AValue: TCocoaRegion);
1662 begin
1663   if TCocoaGDIObject.UpdateRefs(FRegion, AValue) then
1664   begin
1665     FRegion := AValue;
1666     if Assigned(FRegion) then FRegion.Apply(Self);
1667   end;
1668 end;
1669 
1670 procedure TCocoaContext.SetROP2(AValue: Integer);
1671 begin
1672   if FROP2 <> AValue then
1673   begin
1674     FROP2 := AValue;
1675     Pen.Apply(Self);
1676     Brush.Apply(Self);
1677   end;
1678 end;
1679 
1680 procedure TCocoaContext.SetTextColor(AValue: TColor);
1681 begin
1682   FText.ForegroundColor := TColor(ColorToRGB(AValue));
1683 end;
1684 
1685 procedure TCocoaContext.UpdateContextOfs(const AWindowOfs, AViewOfs: TPoint);
1686 var
1687   dx, dy: Integer;
1688 begin
1689   if isSamePoint(AWindowOfs, FWindowOfs) and isSamePoint(AViewOfs, FViewPortOfs) then Exit;
1690   GetWindowViewTranslate(FWindowOfs, FViewPortOfs, dx{%H-}, dy{%H-});
1691   CGContextTranslateCTM(CGContext, -dx, -dy);
1692 
1693   FWindowOfs := AWindowOfs;
1694   FViewPortOfs := AViewOfs;
1695   GetWindowViewTranslate(FWindowOfs, FViewPortOfs, dx, dy);
1696   CGContextTranslateCTM(CGContext, dx, dy);
1697 end;
1698 
1699 procedure TCocoaContext.SetViewPortOfs(AValue: TPoint);
1700 begin
1701   UpdateContextOfs(WindowOfs, AValue);
1702 end;
1703 
1704 procedure TCocoaContext.SetWindowOfs(AValue: TPoint);
1705 begin
1706   UpdateContextOfs(AValue, ViewPortOfs);
1707 end;
1708 
SaveDCDatanull1709 function TCocoaContext.SaveDCData: TCocoaDCData;
1710 begin
1711   Result := TCocoaDCData.Create;
1712 
1713   Result.CurrentFont := Font;
1714   Result.CurrentBrush := FBrush;
1715   Result.CurrentPen := FPen;
1716   Result.CurrentRegion := FRegion;
1717 
1718   // Add references for retained state
1719   if Assigned(Result.CurrentFont) then Result.CurrentFont.AddRef;
1720   if Assigned(Result.CurrentBrush) then Result.CurrentBrush.AddRef;
1721   if Assigned(Result.CurrentPen) then Result.CurrentPen.AddRef;
1722   if Assigned(Result.CurrentRegion) then Result.CurrentRegion.AddRef;
1723 
1724   Result.BkColor := FBkColor;
1725   Result.BkMode := FBkMode;
1726   Result.BkBrush := FBkBrush;
1727 
1728   Result.TextColor := TextColor;
1729 
1730   Result.ROP2 := FROP2;
1731   Result.PenPos := FPenPos;
1732 
1733   Result.WindowOfs := FWindowOfs;
1734   Result.ViewportOfs := FViewportOfs;
1735 
1736   Result.isClipped := FClipped;
1737   Result.ClipShape := FClipRegion.GetShapeCopy;
1738 end;
1739 
1740 destructor TCocoaDCData.Destroy;
1741 begin
1742   // Remove references for retained state
1743   if Assigned(CurrentFont) then CurrentFont.Release;
1744   if Assigned(CurrentBrush) then CurrentBrush.Release;
1745   if Assigned(CurrentPen) then CurrentPen.Release;
1746   if Assigned(CurrentRegion) then CurrentRegion.Release;
1747 end;
1748 
1749 procedure TCocoaContext.RestoreDCData(const AData: TCocoaDCData);
1750 begin
1751   Font := AData.CurrentFont;
1752   Brush := AData.CurrentBrush;
1753   Pen := AData.CurrentPen;
1754   Region := AData.CurrentRegion;
1755 
1756   FBkColor := AData.BkColor;
1757   FBkMode := AData.BkMode;
1758   FBkBrush := AData.BkBrush;
1759 
1760   TextColor := AData.TextColor;
1761 
1762   FROP2 := AData.ROP2;
1763   FPenPos := AData.PenPos;
1764 
1765   FWindowOfs := AData.WindowOfs;
1766   FViewportOfs := AData.ViewportOfs;
1767 
1768   FClipped := AData.isClipped;
1769   FClipRegion.Shape := AData.ClipShape;
1770 end;
1771 
1772 constructor TCocoaContext.Create(AGraphicsContext: NSGraphicsContext);
1773 begin
1774   inherited Create;
1775 
1776   ctx := AGraphicsContext;
1777   if Assigned(ctx) then
1778     ctx.retain;
1779 
1780   FBkBrush := TCocoaBrush.CreateDefault;
1781 
1782   FBrush := DefaultBrush;
1783   FBrush.AddRef;
1784   FPen := DefaultPen;
1785   FPen.AddRef;
1786   FRegion := TCocoaRegion.CreateDefault;
1787   FClipRegion := FRegion;
1788   FClipRegion.AddRef;
1789 
1790   FSavedDCList := nil;
1791   FText := TCocoaTextLayout.Create;
1792   FClipped := False;
1793 end;
1794 
1795 destructor TCocoaContext.Destroy;
1796 begin
1797   if Assigned(FBrush) then
1798     FBrush.Release;
1799   if Assigned(FPen) then
1800     FPen.Release;
1801 
1802   if Assigned(FRegion) then
1803     FRegion.Release;
1804   FClipRegion.Release;
1805 
1806   FSavedDCList.Free;
1807   FText.Free;
1808 
1809   FBkBrush.Free;
1810 
1811   if Assigned(ctx) then
1812     ctx.release;
1813   if Assigned(boxview) then boxview.release;
1814   inherited Destroy;
1815 end;
1816 
SaveDCnull1817 function TCocoaContext.SaveDC: Integer;
1818 begin
1819   ClearClipping;
1820 
1821   Result := 0;
1822 
1823   if FSavedDCList = nil then
1824     FSavedDCList := TFPObjectList.Create(True);
1825 
1826   NSGraphicsContext.classSaveGraphicsState;
1827 
1828   //ctx.saveGraphicsState;
1829   Result := FSavedDCList.Add(SaveDCData) + 1;
1830 
1831   if FClipped then
1832   begin
1833     CGContextSaveGState(CGContext());
1834     FClipRegion.Apply(Self);
1835   end;
1836 end;
1837 
RestoreDCnull1838 function TCocoaContext.RestoreDC(ASavedDC: Integer): Boolean;
1839 begin
1840   ClearClipping;
1841 
1842   Result := False;
1843   if (FSavedDCList = nil) or (ASavedDC <= 0) or (ASavedDC > FSavedDCList.Count) then
1844     Exit;
1845 
1846   while FSavedDCList.Count > ASavedDC do
1847   begin
1848     NSGraphicsContext.classRestoreGraphicsState;
1849     RestoreDCData(TCocoaDCData(FSavedDCList.Count - 1));
1850     FSavedDCList.Delete(FSavedDCList.Count - 1);
1851   end;
1852 
1853   NSGraphicsContext.classRestoreGraphicsState;
1854   RestoreDCData(TCocoaDCData(FSavedDCList[ASavedDC - 1]));
1855   FSavedDCList.Delete(ASavedDC - 1);
1856   Result := True;
1857 
1858   if FSavedDCList.Count = 0 then FreeAndNil(FSavedDCList);
1859 
1860   if FClipped then
1861   begin
1862     CGContextSaveGState(CGContext());
1863     FClipRegion.Apply(Self);
1864   end;
1865 end;
1866 
InitDrawnull1867 function TCocoaContext.InitDraw(width, height:Integer): Boolean;
1868 var
1869   cg: CGContextRef;
1870 begin
1871   cg := CGContext;
1872   Result := Assigned(cg);
1873   if not Result then Exit;
1874 
1875   FSize.cx := width;
1876   FSize.cy := height;
1877 
1878   CGContextTranslateCTM(cg, 0, height);
1879   CGContextScaleCTM(cg, 1, -1);
1880   FPenPos.x := 0;
1881   FPenPos.y := 0;
1882 end;
1883 
1884 procedure TCocoaContext.InvertRectangle(X1, Y1, X2, Y2: Integer);
1885 begin
1886   // save dest context
1887 {$if FPC_FULLVERSION < 30300}
1888   ctx.instanceSaveGraphicsState;
1889 {$else}
1890   ctx.saveGraphicsState;
1891 {$endif}
1892   try
1893     DefaultBrush.Apply(Self, False);
1894     CGContextSetBlendMode(CGContext, kCGBlendModeDifference);
1895 
1896     CGContextFillRect(CGContext, GetCGRectSorted(X1, Y1, X2, Y2));
1897   finally
1898 {$if FPC_FULLVERSION < 30300}
1899     ctx.instanceRestoreGraphicsState;
1900 {$else}
1901     ctx.restoreGraphicsState;
1902 {$endif}
1903     AttachedBitmap_SetModified();
1904   end;
1905 end;
1906 
1907 procedure TCocoaContext.MoveTo(X, Y: Integer);
1908 begin
1909   FPenPos.x := X;
1910   FPenPos.y := Y;
1911 end;
1912 
1913 procedure TCocoaContext.LineTo(X, Y: Integer);
1914 var
1915   cg: CGContextRef;
1916   deltaX, deltaY, absDeltaX, absDeltaY: Integer;
1917   clipDeltaX, clipDeltaY: Float32;
1918   tx, ty, bx, by: Float32;
1919 begin
1920   cg := CGContext;
1921   if not Assigned(cg) then Exit;
1922 
1923   bx := FPenPos.x;
1924   by := FPenPos.y;
1925   deltaX := X-FPenPos.x;
1926   deltaY := Y-FPenPos.y;
1927   if (deltaX=0) and (deltaY=0) then Exit;
1928 
1929   absDeltaX := Abs(deltaX);
1930   absDeltaY := Abs(deltaY);
1931 
1932   if (absDeltaX<=1) and (absDeltaY<=1) then
1933   begin
1934     // special case for 1-pixel lines
1935     tx := bx + 0.5 * deltaX;
1936     ty := by + 0.5 * deltay;
1937   end
1938   else
1939   begin
1940     // exclude the last pixel from the line
1941     if absDeltaX > absDeltaY then
1942     begin
1943       if deltaX > 0 then clipDeltaX := -0.5 else clipDeltaX := 0.5;
1944       clipDeltaY := clipDeltaX * deltaY / deltaX;
1945     end
1946     else
1947     begin
1948       if deltaY > 0 then clipDeltaY := -0.5 else clipDeltaY := 0.5;
1949       clipDeltaX := clipDeltaY * deltaX / deltaY;
1950     end;
1951     bx := bx + clipDeltaX;
1952     by := by + clipDeltaY;
1953     tx := X + clipDeltaX;
1954     ty := Y + clipDeltaY;
1955   end;
1956 
1957   CGContextBeginPath(cg);
1958   CGContextMoveToPoint(cg, bx + 0.5, by + 0.5);
1959   CGContextAddLineToPoint(cg, tx + 0.5, ty + 0.5);
1960   CGContextStrokePath(cg);
1961 
1962   FPenPos.x := X;
1963   FPenPos.y := Y;
1964 
1965   AttachedBitmap_SetModified();
1966 end;
1967 
GetPixelnull1968 function TCocoaContext.GetPixel(X,Y:integer): TColor;
1969 begin
1970   Result := 0;
1971 end;
1972 
1973 procedure TCocoaContext.SetPixel(X,Y:integer; AColor:TColor);
1974 var
1975   cg: CGContextRef;
1976   fillbrush: TCocoaBrush;
1977   r:CGRect;
1978 begin
1979   cg := CGContext;
1980   if not Assigned(cg) then Exit;
1981 
1982   fillbrush:=TCocoaBrush.Create(ColorToNSColor(ColorRef(AColor)));
1983   fillbrush.Apply(self);
1984 
1985   r.origin.x:=x;
1986   r.origin.y:=y;
1987   r.size.height:=1;
1988   r.size.width:=1;
1989 
1990   CGContextFillRect(cg,r);
1991 
1992   fillbrush.Free;
1993 
1994     //restore the brush
1995   if Assigned(FBrush) then
1996      FBrush.Apply(Self);
1997 
1998   AttachedBitmap_SetModified();
1999 end;
2000 
2001 procedure CGContextAddLCLPoints(cg: CGContextRef; const Points: array of TPoint;NumPts:Integer);
2002 var
2003   cp: array of CGPoint;
2004   i: Integer;
2005 begin
2006   SetLength(cp, NumPts);
2007   for i:=0 to NumPts-1 do
2008   begin
2009     cp[i].x:=Points[i].X+0.5;
2010     cp[i].y:=Points[i].Y+0.5;
2011   end;
2012   CGContextAddLines(cg, @cp[0], NumPts);
2013 end;
2014 
2015 procedure CGContextAddLCLRect(cg: CGContextRef; x1, y1, x2, y2: Integer; HalfPixel: boolean); overload;
2016 var
2017   r: CGRect;
2018 begin
2019   if HalfPixel then
2020   begin
2021     r.origin.x:=x1+0.5;
2022     r.origin.y:=y1+0.5;
2023     r.size.width:=x2-x1-1;
2024     r.size.height:=y2-y1-1;
2025   end else
2026   begin
2027     r.origin.x:=x1;
2028     r.origin.y:=y1;
2029     r.size.width:=x2-x1;
2030     r.size.height:=y2-y1;
2031   end;
2032   CGContextAddRect(cg, r);
2033 end;
2034 
2035 procedure CGContextAddLCLRect(cg: CGContextRef; const R: TRect; HalfPixel: boolean); overload;
2036 begin
2037   CGContextAddLCLRect(cg, r.Left, r.Top, r.Right, r.Bottom, HalfPixel);
2038 end;
2039 
2040 procedure TCocoaContext.Polygon(const Points:array of TPoint;NumPts:Integer;
2041   Winding:boolean);
2042 var
2043   cg: CGContextRef;
2044 begin
2045   cg := CGContext;
2046   if not Assigned(cg) or (NumPts<=0) then Exit;
2047 
2048   CGContextBeginPath(cg);
2049   CGContextAddLCLPoints(cg, Points, NumPts);
2050   CGContextClosePath(cg);
2051 
2052   if Winding then
2053     CGContextDrawPath(cg, kCGPathFillStroke)
2054   else
2055     CGContextDrawPath(cg, kCGPathEOFillStroke);
2056 
2057   AttachedBitmap_SetModified();
2058 end;
2059 
2060 procedure TCocoaContext.Polyline(const Points: array of TPoint; NumPts: Integer);
2061 var
2062   cg: CGContextRef;
2063 begin
2064   cg := CGContext;
2065   if not Assigned(cg) or (NumPts<=0) then Exit;
2066 
2067   CGContextBeginPath(cg);
2068   CGContextAddLCLPoints(cg, Points, NumPts);
2069   CGContextStrokePath(cg);
2070 
2071   AttachedBitmap_SetModified();
2072 end;
2073 
2074 procedure TCocoaContext.Rectangle(X1, Y1, X2, Y2: Integer; FillRect: Boolean; UseBrush: TCocoaBrush);
2075 var
2076   cg: CGContextRef;
2077   resetPen: Boolean;
2078 begin
2079   if (X1=X2) or (Y1=Y2) then Exit;
2080 
2081   cg := CGContext;
2082   if not Assigned(cg) then Exit;
2083 
2084   resetPen := false;
2085   CGContextBeginPath(cg);
2086 
2087   if FillRect then
2088   begin
2089     CGContextAddLCLRect(cg, X1, Y1, X2, Y2, false);
2090     //using the brush
2091     if Assigned(UseBrush) then
2092        UseBrush.Apply(Self);
2093     CGContextFillPath(cg);
2094     //restore the brush
2095     if Assigned(UseBrush) and Assigned(FBrush) then
2096        FBrush.Apply(Self);
2097   end
2098   else
2099   begin
2100     CGContextAddLCLRect(cg, X1, Y1, X2, Y2, true);
2101     // this is a "special" case, when UseBrush is provided
2102     // but "FillRect" is set to false. Use for FrameRect() function
2103     // (it deserves a redesign)
2104     if Assigned(UseBrush) then
2105     begin
2106       UseBrush.Apply(Self);
2107       UseBrush.ApplyAsPenColor(Self);
2108       resetPen := true;
2109     end;
2110   end;
2111 
2112   CGContextStrokePath(cg);
2113 
2114   AttachedBitmap_SetModified();
2115 
2116   if resetPen and Assigned(fPen) then // pen was modified by brush. Setting it back
2117     fPen.Apply(Self);
2118 end;
2119 
2120 procedure TCocoaContext.BackgroundFill(dirtyRect:NSRect);
2121 var
2122   cg: CGContextRef;
2123 
2124 begin
2125   cg := CGContext;
2126   if not Assigned(cg) then Exit;
2127 
2128   FBkBrush.Apply(Self);
2129 
2130   CGContextFillRect(cg,CGRect(dirtyRect));
2131 
2132   AttachedBitmap_SetModified();
2133 end;
2134 
2135 procedure TCocoaContext.Ellipse(X1, Y1, X2, Y2:Integer);
2136 var
2137   cg: CGContextRef;
2138   r: CGRect;
2139 begin
2140   cg := CGContext;
2141   if not Assigned(cg) then Exit;
2142   r.origin.x:=x1+0.5;
2143   r.origin.y:=y1+0.5;
2144   r.size.width:=x2-x1-1;
2145   r.size.height:=y2-y1-1;
2146   CGContextBeginPath(CGContext);
2147   CGContextAddEllipseInRect(CGContext, R);
2148   CGContextDrawPath(CGContext, kCGPathFillStroke);
2149 
2150   AttachedBitmap_SetModified();
2151 end;
2152 
2153 procedure TCocoaContext.TextOut(X, Y: Integer; Options: Longint; Rect: PRect; UTF8Chars: PChar; Count: Integer; CharsDelta: PInteger);
2154 var
2155   BrushSolid, FillBg: Boolean;
2156 begin
2157   CGContextSaveGState(CGContext());
2158 
2159   if Assigned(Rect) then
2160   begin
2161     // fill background
2162     //debugln(['TCocoaContext.TextOut ',UTF8Chars,' ',dbgs(Rect^)]);
2163     if (Options and ETO_OPAQUE) <> 0 then
2164     begin
2165       BrushSolid := BkBrush.Solid;
2166       BkBrush.Solid := True;
2167       with Rect^ do
2168         Rectangle(Left, Top, Right, Bottom, True, BkBrush);
2169       BkBrush.Solid := BrushSolid;
2170     end;
2171 
2172     if ((Options and ETO_CLIPPED) <> 0) and (Count > 0) then
2173     begin
2174       CGContextBeginPath(CGContext);
2175       CGContextAddRect(CGContext, RectToCGrect(Rect^));
2176       CGContextClip(CGContext);
2177     end;
2178   end;
2179 
2180   if (Count > 0) then
2181   begin
2182     FillBg := BkMode = OPAQUE;
2183     if FillBg then
2184       FText.BackgroundColor := BkBrush.ColorRef;
2185     FText.SetText(UTF8Chars, Count);
2186     FText.Draw(ctx, X, Y, FillBg, CharsDelta);
2187   end;
2188 
2189   CGContextRestoreGState(CGContext());
2190 
2191   AttachedBitmap_SetModified();
2192 end;
2193 
2194 procedure TCocoaContext.Frame(const R: TRect);
2195 begin
2196   Rectangle(R.Left, R.Top, R.Right + 1, R.Bottom + 1, False, nil);
2197   AttachedBitmap_SetModified();
2198 end;
2199 
2200 procedure TCocoaContext.Frame3d(var ARect: TRect; const FrameWidth: integer; const Style: TBevelCut);
2201 var
2202   dx,dy: integer;
2203   ns : NSRect;
2204   r  : TRect;
2205   yy : double;
2206 begin
2207   if Style = bvNone then Exit;
2208 
2209   if (Style = bvRaised) or (Style = bvLowered) then
2210   begin
2211     if not Assigned(boxview) then
2212     begin
2213       boxview := NSBox.alloc.initWithFrame(NSMakeRect(0,0,0,0));
2214       boxview.setTitle(NSString.string_);
2215       boxview.setTitlePosition(NSNoTitle);
2216     end;
2217 
2218     dx:=3; // layout<->frame adjustement for the box
2219     dy:=3; // (should be aquired using 10.7 apis)
2220     if Style=bvRaised then
2221       boxview.setBoxType(NSBoxPrimary)
2222     else
2223       boxview.setBoxType(NSBoxSecondary);
2224     r:=ARect;
2225     InflateRect(r, dx, dy);
2226     ns := RectToNSRect(r);
2227     // used for size only, position is ignored
2228     boxview.setFrame(ns);
2229     yy := ns.size.height+ns.origin.y+1;
2230     CGContextTranslateCTM(ctx.lclCGContext, ns.origin.x, yy);
2231     CGContextScaleCTM(ctx.lclCGContext, 1, -1);
2232 
2233     boxview.displayRectIgnoringOpacity_inContext(
2234       NSMakeRect(0,0,ns.size.width, ns.size.height)
2235       , ctx);
2236 
2237     CGContextScaleCTM(ctx.lclCGContext, 1, -1);
2238     CGContextTranslateCTM(ctx.lclCGContext, -ns.origin.x,-yy);
2239   end;
2240   AttachedBitmap_SetModified();
2241 end;
2242 
2243 procedure TCocoaContext.FrameRect(const ARect: TRect; const ABrush: TCocoaBrush);
2244 begin
2245   Rectangle(Arect.Left,ARect.Top,Arect.Right,ARect.Bottom, False, ABrush);
2246   AttachedBitmap_SetModified();
2247 end;
2248 
2249 procedure TCocoaContext.SetCGFillping(Ctx: CGContextRef; Width, Height: CGFloat);
2250 begin
2251   if Width < 0 then
2252   begin
2253     CGContextTranslateCTM(Ctx, -Width, 0);
2254     CGContextScaleCTM(Ctx, -1, 1);
2255   end;
2256 
2257   if Height < 0 then
2258   begin
2259     CGContextTranslateCTM(Ctx, 0, -Height);
2260     CGContextScaleCTM(Ctx, 1, -1);
2261   end;
2262 end;
2263 
2264 procedure TCocoaContext.RestoreCGFillping(Ctx: CGContextRef; Width, Height: CGFloat);
2265 begin
2266   if Height < 0 then
2267   begin
2268     CGContextTranslateCTM(Ctx, 0, Height);
2269     CGContextScaleCTM(Ctx, 1, -1);
2270   end;
2271 
2272   if Width < 0 then
2273   begin
2274     CGContextScaleCTM(Ctx, -1, 1);
2275     CGContextTranslateCTM(Ctx, Width, 0);
2276   end;
2277 end;
2278 
2279 procedure TCocoaContext.ApplyTransform(Trans: CGAffineTransform);
2280 var
2281   T2: CGAffineTransform;
2282 begin
2283   T2 := CGContextGetCTM(CGContext);
2284   // restore old CTM since CTM may changed after the clipping
2285   if CGAffineTransformEqualToTransform(Trans, T2) = 0 then
2286     CGContextTranslateCTM(CGContext, Trans.a * Trans.tx - T2.a * T2.tx,
2287        Trans.d * Trans.ty - T2.d * T2.ty);
2288 end;
2289 
2290 procedure TCocoaContext.ClearClipping;
2291 var
2292   Trans: CGAffineTransform;
2293   cgc: CGContextRef;
2294 begin
2295   if FClipped then
2296   begin
2297     cgc := CGContext();
2298     Trans := CGContextGetCTM(cgc);
2299     CGContextRestoreGState(cgc);
2300     ApplyTransform(Trans);
2301   end;
2302 end;
2303 
2304 procedure TCocoaContext.AttachedBitmap_SetModified;
2305 begin
2306 
2307 end;
2308 
DrawImageRepnull2309 function TCocoaContext.DrawImageRep(dstRect: NSRect; const srcRect: NSRect;
2310   ImageRep: NSBitmapImageRep): Boolean;
2311 var
2312   Context: NSGraphicsContext;
2313 begin
2314   NSGraphicsContext.classSaveGraphicsState;
2315   try
2316     // we flip the context on it initialization (see InitDraw) so to draw
2317     // a bitmap correctly we need to create a flipped context and to draw onto it
2318 
2319     if not ctx.isFlipped then
2320       Context := NSGraphicsContext.graphicsContextWithGraphicsPort_flipped(ctx.graphicsPort, True)
2321     else
2322       Context := ctx;
2323     NSGraphicsContext.setCurrentContext(Context);
2324     Result := ImageRep.drawInRect_fromRect_operation_fraction_respectFlipped_hints(
2325       dstRect, srcRect, NSCompositeSourceOver, 1.0, True, nil
2326       );
2327   finally
2328     NSGraphicsContext.classRestoreGraphicsState;
2329   end;
2330   AttachedBitmap_SetModified();
2331 end;
2332 
StretchDrawnull2333 function TCocoaContext.StretchDraw(X, Y, Width, Height: Integer;
2334   SrcDC: TCocoaBitmapContext; XSrc, YSrc, SrcWidth, SrcHeight: Integer;
2335   Msk: TCocoaBitmap; XMsk, YMsk: Integer; Rop: DWORD): Boolean;
2336 var
2337   Bmp: TCocoaBitmap;
2338   MskImage: CGImageRef;
2339   ImgRect: CGRect;
2340 begin
2341   Bmp := SrcDC.Bitmap;
2342   if not Assigned(Bmp) then
2343     Exit(False);
2344 
2345   // Make sure that bitmap is the most up-to-date
2346   Bmp.ReCreateHandle_IfModified(); // Fix for bug 28102
2347 
2348   // see https://bugs.freepascal.org/view.php?id=34197
2349   // Bitmap context windowsofs should be used when rendering a bitmap
2350   inc(XSrc, -SrcDC.WindowOfs.X);
2351   inc(YSrc, -SrcDC.WindowOfs.Y);
2352 
2353   //apply window offset
2354   if (Msk <> nil) and (Msk.Image <> nil) then
2355   begin
2356     MskImage := Msk.CreateMaskImage(Bounds(XMsk, YMsk, SrcWidth, SrcHeight));
2357     ImgRect := CGRectMake(x, -y, SrcWidth, SrcHeight);
2358     CGContextSaveGState(CGContext);
2359     CGContextScaleCTM(CGContext, 1, -1);
2360     CGContextTranslateCTM(CGContext, 0, -SrcHeight);
2361     CGContextClipToMask(CGContext, ImgRect, MskImage );
2362 
2363     NSGraphicsContext.setCurrentContext(ctx);
2364     Result := bmp.ImageRep.drawInRect_fromRect_operation_fraction_respectFlipped_hints(
2365       GetNSRect(X, -Y, Width, Height), GetNSRect(XSrc, YSrc, SrcWidth, SrcHeight), NSCompositeSourceOver, 1.0, True, nil );
2366 
2367     CGImageRelease(MskImage);
2368     CGContextRestoreGState(CGContext);
2369   end
2370   else
2371   begin
2372     // convert Y coordinate of the source bitmap
2373     YSrc := Bmp.Height - (SrcHeight + YSrc);
2374     Result := DrawImageRep(GetNSRect(X, Y, Width, Height),GetNSRect(XSrc, YSrc, SrcWidth, SrcHeight), bmp.ImageRep);
2375   end;
2376   AttachedBitmap_SetModified();
2377 end;
2378 
2379 {------------------------------------------------------------------------------
2380   Method:  GetTextExtentPoint
2381   Params:  Str   - Text string
2382            Count - Number of characters in string
2383            Size  - The record for the dimensions of the string
2384   Returns: If the function succeeds
2385 
2386   Computes the width and height of the specified string of text
2387  ------------------------------------------------------------------------------}
GetTextExtentPointnull2388 function TCocoaContext.GetTextExtentPoint(AStr: PChar; ACount: Integer; var Size: TSize): Boolean;
2389 begin
2390   FText.SetText(AStr, ACount);
2391   Size := FText.GetSize;
2392   Result := True;
2393 end;
2394 
2395 {------------------------------------------------------------------------------
2396   Method:  TCocoaContext.GetTextMetrics
2397   Params:  TM - The Record for the text metrics
2398   Returns: If the function succeeds
2399 
2400   Fills the specified buffer with the metrics for the currently selected font
2401  ------------------------------------------------------------------------------}
GetTextMetricsnull2402 function TCocoaContext.GetTextMetrics(var TM: TTextMetric): Boolean;
2403 var
2404   Glyphs: TGlyphArray;
2405   Adjustments: array of NSSize;
2406   I: Integer;
2407   A: Single;
2408   lNSFont: NSFont;
2409 begin
2410   result := False;
2411   if not Assigned(Font) then
2412     exit;
2413 
2414   FillChar(TM, SizeOf(TM), 0);
2415 
2416   lNSFont := Font.Font;
2417   TM.tmAscent := Round(lNSFont.ascender);
2418   TM.tmDescent := -Round(lNSFont.descender);
2419   TM.tmHeight := TM.tmAscent + TM.tmDescent;
2420 
2421   TM.tmInternalLeading := Round(lNSFont.leading);
2422   TM.tmExternalLeading := 0;
2423 
2424   TM.tmMaxCharWidth := Round(lNSFont.maximumAdvancement.width);
2425   FText.SetText('WMTigq[_|^', 10);
2426   Glyphs := FText.GetGlyphs;
2427   if Length(Glyphs) > 0 then
2428   begin
2429     SetLength(Adjustments, Length(Glyphs));
2430     lNSFont.getAdvancements_forGlyphs_count(@Adjustments[0], @Glyphs[0], Length(Glyphs));
2431     A := 0;
2432     for I := 0 to High(Adjustments) do
2433       A := A + Adjustments[I].width;
2434     TM.tmAveCharWidth := Round(A / Length(Adjustments));
2435     SetLength(Adjustments, 0);
2436     SetLength(Glyphs, 0);
2437   end
2438   else
2439     TM.tmAveCharWidth := TM.tmMaxCharWidth;
2440 
2441   TM.tmOverhang := 0;
2442   TM.tmDigitizedAspectX := 0;
2443   TM.tmDigitizedAspectY := 0;
2444   TM.tmFirstChar := 'a';
2445   TM.tmLastChar := 'z';
2446   TM.tmDefaultChar := 'x';
2447   TM.tmBreakChar := '?';
2448 
2449   TM.tmWeight := Font.CocoaFontWeightToWin32FontWeight(NSFontManager.sharedFontManager.weightOfFont(Font.Font));
2450 
2451   if cfs_Bold in Font.Style then
2452     TM.tmWeight := Min(FW_BOLD, TM.tmWeight);
2453 
2454   if cfs_Italic in Font.Style then
2455     TM.tmItalic := 1;
2456 
2457   if cfs_Underline in Font.Style then
2458     TM.tmUnderlined := 1;
2459 
2460   if cfs_StrikeOut in Font.Style then
2461     TM.tmStruckOut := 1;
2462 
2463   TM.tmPitchAndFamily := TRUETYPE_FONTTYPE;
2464   if Font.Font.isFixedPitch then
2465     TM.tmPitchAndFamily := TM.tmPitchAndFamily or FIXED_PITCH;
2466 
2467   // we can take charset from Font.Charset also but leave it to default for now
2468   TM.tmCharSet := DEFAULT_CHARSET;
2469 
2470   Result := True;
2471 end;
2472 
2473 procedure TCocoaContext.DrawBitmap(X, Y: Integer; ABitmap: TCocoaBitmap);
2474 begin
2475   NSGraphicsContext.classSaveGraphicsState();
2476   NSGraphicsContext.setCurrentContext(ctx);
2477   ABitmap.imagerep.drawAtPoint(NSMakePoint(X, Y));
2478   NSGraphicsContext.classRestoreGraphicsState();
2479   AttachedBitmap_SetModified();
2480 end;
2481 
2482 procedure TCocoaContext.DrawFocusRect(ARect: TRect);
2483 var
2484   {$ifdef CocoaUseHITheme}
2485   AOutSet: SInt32;
2486   {$else}
2487   lCanvas: TCanvas;
2488   lDrawer: TCDDrawer;
2489   {$endif}
2490 begin
2491   {$ifdef CocoaUseHITheme}
2492   // LCL thinks that focus cannot be drawn outside focus rects, but carbon do that
2493   // => correct rect
2494   GetThemeMetric(kThemeMetricFocusRectOutset, AOutSet);
2495   InflateRect(ARect, -AOutSet, -AOutSet);
2496   HIThemeDrawFocusRect(RectToCGRect(ARect), True, CGContext, kHIThemeOrientationNormal);
2497   {$else}
2498   lCanvas := TCanvas.Create;
2499   try
2500     lDrawer := GetDrawer(dsMacOSX);
2501     lCanvas.Handle := HDC(Self);
2502     lDrawer.DrawFocusRect(lCanvas, Types.Point(ARect.Left, ARect.Top), Types.Size(ARect));
2503   finally
2504     lCanvas.Handle := 0;
2505     lCanvas.Free;
2506   end;
2507   {$endif}
2508   AttachedBitmap_SetModified();
2509 end;
2510 
2511 { TCocoaBitmapContext }
2512 
2513 procedure TCocoaBitmapContext.SetBitmap(const AValue: TCocoaBitmap);
2514 var pool:NSAutoReleasePool;
2515 begin
2516   if Assigned(ctx) then
2517   begin
2518     ctx.release;
2519     ctx := nil;
2520   end;
2521 
2522   // ToDo: Should we free the old FBitmap???
2523   FBitmap := AValue;
2524   if FBitmap <> nil then
2525   begin
2526     pool:=NSAutoreleasePool.alloc.init;
2527     ctx := NSGraphicsContext.graphicsContextWithBitmapImageRep(Bitmap.ImageRep);
2528     ctx.retain; // extend life beyond NSAutoreleasePool
2529     InitDraw(Bitmap.Width, Bitmap.Height);
2530     pool.release;
2531   end;
2532 end;
2533 
2534 procedure TCocoaBitmapContext.AttachedBitmap_SetModified;
2535 begin
2536   if FBitmap = nil then Exit;
2537   FBitmap.SetModified();
2538 end;
2539 
2540 constructor TCocoaBitmapContext.Create;
2541 begin
2542   inherited Create(nil);
2543   FBitmap := DefaultBitmap;
2544 end;
2545 
2546 destructor TCocoaBitmapContext.Destroy;
2547 begin
2548   inherited Destroy;
2549 end;
2550 
GetPixelnull2551 function TCocoaBitmapContext.GetPixel(X,Y:integer): TColor;
2552 var
2553   cg: CGContextRef;
2554   color: NSColor;
2555   R,G, B: Byte;
2556 begin
2557   Result := 0;
2558   cg := CGContext;
2559   if not Assigned(cg) then Exit;
2560 
2561   color := FBitmap.Imagerep.colorAtX_Y(X, Y);
2562   R := Round(color.redComponent * $FF);
2563   G := Round(color.greenComponent * $FF);
2564   B := Round(color.blueComponent * $FF);
2565   Result := Graphics.RGBToColor(R, G, B);
2566 end;
2567 
2568 { TCocoaRegion }
2569 
2570 {------------------------------------------------------------------------------
2571   Method:  TCocoaRegion.Create
2572 
2573   Creates a new empty Cocoa region
2574  ------------------------------------------------------------------------------}
2575 constructor TCocoaRegion.CreateDefault(AGlobal: Boolean = False);
2576 begin
2577   inherited Create(AGlobal);
2578 
2579   FShape := HIShapeCreateEmpty;
2580 end;
2581 
2582 {------------------------------------------------------------------------------
2583   Method:  TCocoaRegion.Create
2584   Params:  X1, Y1, X2, Y2 - Region bounding rectangle
2585 
2586   Creates a new rectangular Cocoa region
2587  ------------------------------------------------------------------------------}
2588 constructor TCocoaRegion.Create(const X1, Y1, X2, Y2: Integer);
2589 begin
2590   inherited Create(False);
2591   FShape := HIShapeCreateWithRect(GetCGRect(X1, Y1, X2, Y2));
2592 end;
2593 
2594 {------------------------------------------------------------------------------
2595   Method:  TCocoaRegion.Create
2596   Params:  Points   - Pointer to array of polygon points
2597            NumPts   - Number of points passed
2598            FillMode - Filling mode
2599 
2600   Creates a new polygonal Cocoa region from the specified points
2601  ------------------------------------------------------------------------------}
2602 constructor TCocoaRegion.Create(Points: PPoint; NumPts: Integer; isAlter: Boolean);
2603 var
2604   Bounds: TRect;
2605   Context: CGContextRef;
2606   W, H: Integer;
2607   Data: Pointer;
2608   PData: PByte;
2609   P: PPoint;
2610   I: Integer;
2611   X, Y, SX: Integer;
2612   LC, C: Byte;
2613   //Line: String;
2614 
2615   function GetPolygonBounds: TRect;
2616   var
2617     I: Integer;
2618   begin
2619     P := Points;
2620     Result := Classes.Rect(P^.X, P^.Y, P^.X, P^.Y);
2621     for I := 1 to NumPts - 1 do
2622     begin
2623       Inc(P);
2624       if P^.X < Result.Left then Result.Left := P^.X;
2625       if P^.X > Result.Right then Result.Right := P^.X;
2626       if P^.Y < Result.Top then Result.Top := P^.Y;
2627       if P^.Y > Result.Bottom then Result.Bottom := P^.Y;
2628     end;
2629   end;
2630 
2631   procedure AddPart(X1, X2, Y: Integer);
2632   var
2633     R: HIShapeRef;
2634   begin
2635     //DebugLn('AddPart:' + DbgS(X1) + ' - ' + DbgS(X2) + ', ' + DbgS(Y));
2636 
2637     R := HIShapeCreateWithRect(GetCGRect(X1, Y, X2, Y + 1));
2638     HIShapeUnion(FShape, R, FShape);
2639     CFRelease(R);
2640   end;
2641 
2642 begin
2643   inherited Create(False);
2644 
2645 (*
2646   The passed polygon is drawed into grayscale context, the region is constructed
2647   per rows from rectangles of drawed polygon parts.
2648   *)
2649 
2650   FShape := HIShapeCreateMutable;
2651 
2652   if (NumPts <= 2) or (Points = nil) then Exit;
2653   Bounds := GetPolygonBounds;
2654   W := Bounds.Right - Bounds.Left + 2;
2655   H := Bounds.Bottom - Bounds.Top + 2;
2656 
2657   if (W <= 0) or (H <= 0) then Exit;
2658 
2659   System.GetMem(Data, W * H);
2660   System.FillChar(Data^, W * H, 0); // clear bitmap context data to black
2661   try
2662     Context := CGBitmapContextCreate(Data, W, H, 8, W, CGColorSpaceCreateDeviceGray,
2663       kCGImageAlphaNone);
2664     try
2665       CGContextSetShouldAntialias(Context, 0); // disable anti-aliasing
2666       CGContextSetGrayFillColor(Context, 1.0, 1.0); // draw white polygon
2667 
2668       P := Points;
2669       CGContextBeginPath(Context);
2670       CGContextMoveToPoint(Context, P^.X, P^.Y);
2671 
2672       for I := 1 to NumPts - 1 do
2673       begin
2674         Inc(P);
2675         CGContextAddLineToPoint(Context, P^.X, P^.Y);
2676       end;
2677 
2678       CGContextClosePath(Context);
2679 
2680       if isAlter then
2681         CGContextEOFillPath(Context)
2682       else
2683         CGContextFillPath(Context);
2684 
2685       //SetLength(Line, W);
2686 
2687       PData := Data;
2688       for Y := 0 to Pred(H) do
2689       begin
2690         LC := 0; // edge is black
2691         for X := 0 to Pred(W) do
2692         begin
2693           C := PData^;
2694           //Line[X + 1] := Chr(Ord('0') + C div 255);
2695 
2696           if (C = $FF) and (LC = 0) then
2697             SX := X; // start of painted row part
2698           if (C = 0) and (LC = $FF) then
2699             // end of painted row part (SX, X)
2700             AddPart(SX, X,  Pred(H) - Y);
2701 
2702           LC := C;
2703           Inc(PData);
2704         end;
2705         //DebugLn(DbgS(Pred(H) - Y) + ':' + Line);
2706       end;
2707 
2708     finally
2709       CGContextRelease(Context);
2710     end;
2711   finally
2712     System.FreeMem(Data);
2713   end;
2714 end;
2715 
2716 {------------------------------------------------------------------------------
2717   Method:  TCocoaRegion.Destroy
2718 
2719   Destroys Cocoa region
2720  ------------------------------------------------------------------------------}
2721 destructor TCocoaRegion.Destroy;
2722 begin
2723   CFRelease(FShape);
2724 
2725   inherited Destroy;
2726 end;
2727 
2728 {------------------------------------------------------------------------------
2729   Method:  TCocoaRegion.Apply
2730   Params:  ADC - Context to apply to
2731 
2732   Applies region to the specified context
2733   Note: Clipping region is only reducing
2734  ------------------------------------------------------------------------------}
2735 procedure TCocoaRegion.Apply(ADC: TCocoaContext);
2736 var
2737   DeviceShape: HIShapeRef;
2738 begin
2739   if ADC = nil then Exit;
2740   if ADC.CGContext = nil then Exit;
2741   DeviceShape := HIShapeCreateMutableCopy(Shape);
2742   try
2743     with ADC.GetLogicalOffset do
2744       HIShapeOffset(DeviceShape, -X, -Y);
2745     if HIShapeIsEmpty(DeviceShape) or (HIShapeReplacePathInCGContext(DeviceShape, ADC.CGContext) <> noErr) then
2746       Exit;
2747     CGContextClip(ADC.CGContext);
2748   finally
2749     CFRelease(DeviceShape);
2750   end;
2751 end;
2752 
2753 {------------------------------------------------------------------------------
2754   Method:  TCocoaRegion.GetBounds
2755   Returns: The bounding box of Cocoa region
2756  ------------------------------------------------------------------------------}
GetBoundsnull2757 function TCocoaRegion.GetBounds: TRect;
2758 var
2759   R: HIRect;
2760 begin
2761   if HIShapeGetBounds(FShape, R) = nil then begin
2762     System.FillChar(Result, sizeof(Result), 0);
2763     Exit;
2764   end;
2765 
2766   Result := CGRectToRect(R);
2767 end;
2768 
2769 {------------------------------------------------------------------------------
2770   Method:  TCocoaRegion.GetType
2771   Returns: The type of Cocoa region
2772  ------------------------------------------------------------------------------}
GetTypenull2773 function TCocoaRegion.GetType: TCocoaRegionType;
2774 begin
2775   if not Assigned(FShape) or HIShapeIsEmpty(FShape) then
2776     Result := crt_Empty
2777   else if HIShapeIsRectangular(FShape) then
2778     Result := crt_Rectangle
2779   else
2780     Result := crt_Complex;
2781 end;
2782 
2783 {------------------------------------------------------------------------------
2784   Method:  TCocoaRegion.ContainsPoint
2785   Params:  P - Point
2786   Returns: If the specified point lies in Cocoa region
2787  ------------------------------------------------------------------------------}
ContainsPointnull2788 function TCocoaRegion.ContainsPoint(const P: TPoint): Boolean;
2789 var
2790   cp : CGPoint;
2791 begin
2792   cp.x:=P.x+0.5;
2793   cp.y:=P.y+0.5;
2794   Result := HIShapeContainsPoint(FShape, cp);
2795 end;
2796 
2797 procedure TCocoaRegion.SetShape(AShape: HIShapeRef);
2798 begin
2799   if Assigned(FShape) then CFRelease(FShape);
2800   FShape := AShape;
2801 end;
2802 
2803 procedure TCocoaRegion.Clear;
2804 begin
2805   HIShapeSetEmpty(FShape)
2806 end;
2807 
CombineWithnull2808 function TCocoaRegion.CombineWith(ARegion: TCocoaRegion; CombineMode: TCocoaCombine): TCocoaRegionType;
2809 var
2810   sh1, sh2: HIShapeRef;
2811 const
2812   MinCoord=-35000;
2813   MaxSize=65000;
2814 begin
2815   if not Assigned(ARegion) then
2816     Result := crt_Error
2817   else
2818   begin
2819     if (CombineMode in [cc_AND, cc_OR, cc_XOR]) and HIShapeIsEmpty(FShape) then
2820       CombineMode := cc_Copy;
2821 
2822     case CombineMode of
2823       cc_AND:
2824         begin
2825           Shape := HIShapeCreateIntersection(FShape, ARegion.Shape);
2826           Result := GetType;
2827         end;
2828       cc_XOR:
2829       begin
2830         sh1 := HIShapeCreateUnion(FShape, ARegion.Shape);
2831         sh2 := HIShapeCreateIntersection(FShape, ARegion.Shape);
2832         Shape := HIShapeCreateDifference(sh1, sh2);
2833         CFRelease(sh1);
2834         CFRelease(sh2);
2835         Result := GetType;
2836       end;
2837       cc_OR:
2838         begin
2839           Shape := HIShapeCreateUnion(FShape, ARegion.Shape);
2840           Result := GetType;
2841         end;
2842       cc_DIFF:
2843       begin
2844         if HIShapeIsEmpty(FShape) then
2845           {HIShapeCreateDifference doesn't work properly if original shape is empty}
2846           {to simulate "emptieness" very big shape is created }
2847           Shape := HIShapeCreateWithRect(GetCGRect(MinCoord, MinCoord, MaxSize, MaxSize)); // create clip nothing.
2848 
2849         Shape := HIShapeCreateDifference(FShape, ARegion.Shape);
2850         Result := GetType;
2851       end;
2852       cc_COPY:
2853         begin
2854           Shape := HIShapeCreateCopy(ARegion.Shape);
2855           Result := GetType;
2856         end
2857     else
2858       Result := crt_Error;
2859     end;
2860   end;
2861 end;
2862 
2863 procedure TCocoaRegion.Offset(dx, dy: Integer);
2864 begin
2865   MakeMutable;
2866   HIShapeOffset(FShape, dx, dy);
2867 end;
2868 
TCocoaRegion.GetShapeCopynull2869 function TCocoaRegion.GetShapeCopy: HIShapeRef;
2870 begin
2871   Result := HIShapeCreateCopy(Shape);
2872 end;
2873 
2874 procedure TCocoaRegion.MakeMutable;
2875 begin
2876   Shape := HIShapeCreateMutableCopy(Shape);
2877 end;
2878 
2879 { TCocoaPen }
2880 
2881 procedure CalcDashes(
2882   const Src: array of CGFloat;
2883   var Dst: array of CGFloat;
2884   out Len: Integer;
2885   mul: CGFloat;
2886   ofs: CGFloat = 0.0 // pixels are "half" offset in Cocoa drawing
2887   );
2888 var
2889   i: Integer;
2890 begin
2891   Len := Min(length(Src), length(Dst));
2892   for i := 0 to Len - 1 do
2893     Dst[i] := Src[i] * mul + ofs;
2894 end;
2895 
2896 procedure TCocoaPen.Apply(ADC: TCocoaContext; UseROP2: Boolean = True);
2897 var
2898   AR, AG, AB, AA: CGFloat;
2899   AROP2: Integer;
2900   ADashes: array [0..15] of CGFloat;
2901   ADashLen: Integer;
2902   StatDash: PCocoaStatDashes;
2903   isCosm  : Boolean;
2904   WidthMul : array [Boolean] of CGFloat;
2905 begin
2906   if ADC = nil then Exit;
2907   if ADC.CGContext = nil then Exit;
2908 
2909   if UseROP2 then
2910     AROP2 := ADC.ROP2
2911   else
2912     AROP2 := R2_COPYPEN;
2913 
2914   GetRGBA(AROP2, AR, AG, AB, AA);
2915 
2916   case AROP2 of
2917     R2_NOT, R2_NOTXORPEN:
2918       CGContextSetBlendMode(ADC.CGContext, kCGBlendModeDifference);
2919   else
2920     CGContextSetBlendMode(ADC.CGContext, kCGBlendModeNormal)
2921   end;
2922 
2923   CGContextSetRGBStrokeColor(ADC.CGContext, AR, AG, AB, AA);
2924   CGContextSetLineWidth(ADC.CGContext, FWidth);
2925 
2926   if IsExtPen then
2927   begin
2928     if IsGeometric then
2929     begin
2930       CGContextSetLineCap(ADC.CGContext, FEndCap);
2931       CGContextSetLineJoin(ADC.CGContext, FJoinStyle);
2932     end;
2933   end;
2934 
2935   case FStyle of
2936     PS_DASH..PS_DASHDOTDOT:
2937       begin
2938         isCosm := not IsGeometric;
2939         WidthMul[false]:=1.0;
2940         WidthMul[true]:=Width;
2941         StatDash := @CocoaPenDash[isCosm][FStyle];
2942         CalcDashes( Slice(StatDash^.dash, StatDash^.len), ADashes, ADashLen, WidthMul[IsGeometric]);
2943         CGContextSetLineDash(ADC.CGContext, 0, @ADashes[0], ADashLen);
2944       end;
2945     PS_USERSTYLE:
2946       if Length(Dashes) > 0 then
2947         CGContextSetLineDash(ADC.CGContext, 0, @Dashes[0], Length(Dashes))
2948       else
2949         CGContextSetLineDash(ADC.CGContext, 0, nil, 0)
2950   else
2951     CGContextSetLineDash(ADC.CGContext, 0, nil, 0);
2952   end;
2953 end;
2954 
2955 constructor TCocoaPen.CreateDefault(const AGlobal: Boolean = False);
2956 begin
2957   inherited Create(clBlack, True, AGlobal);
2958   FStyle := PS_SOLID;
2959   FWidth := 1;
2960   FIsExtPen := False;
2961   Dashes := nil;
2962 end;
2963 
2964 constructor TCocoaPen.Create(const ALogPen: TLogPen; const AGlobal: Boolean = False);
2965 begin
2966   case ALogPen.lopnStyle of
2967     PS_SOLID..PS_DASHDOTDOT,
2968     PS_INSIDEFRAME:
2969       begin
2970         inherited Create(ColorToRGB(TColor(ALogPen.lopnColor)), True, AGlobal);
2971         FWidth := Max(1, ALogPen.lopnWidth.x);
2972       end;
2973     else
2974     begin
2975       inherited Create(ColorToRGB(TColor(ALogPen.lopnColor)), False, AGlobal);
2976       FWidth := 1;
2977     end;
2978   end;
2979 
2980   FStyle := ALogPen.lopnStyle;
2981 end;
2982 
2983 constructor TCocoaPen.Create(dwPenStyle, dwWidth: DWord; const lplb: TLogBrush;
2984   dwStyleCount: DWord; lpStyle: PDWord);
2985 var
2986   i: integer;
2987 begin
2988   case dwPenStyle and PS_STYLE_MASK of
2989     PS_SOLID..PS_DASHDOTDOT,
2990     PS_USERSTYLE:
2991       begin
2992         inherited Create(ColorToRGB(TColor(lplb.lbColor)), True, False);
2993       end;
2994     else
2995     begin
2996       inherited Create(ColorToRGB(TColor(lplb.lbColor)), False, False);
2997     end;
2998   end;
2999 
3000   FIsExtPen := True;
3001   FIsGeometric := (dwPenStyle and PS_TYPE_MASK) = PS_GEOMETRIC;
3002 
3003   if IsGeometric then
3004   begin
3005     case dwPenStyle and PS_JOIN_MASK of
3006       PS_JOIN_ROUND: FJoinStyle := kCGLineJoinRound;
3007       PS_JOIN_BEVEL: FJoinStyle := kCGLineJoinBevel;
3008       PS_JOIN_MITER: FJoinStyle := kCGLineJoinMiter;
3009     end;
3010 
3011     case dwPenStyle and PS_ENDCAP_MASK of
3012       PS_ENDCAP_ROUND: FEndCap := kCGLineCapRound;
3013       PS_ENDCAP_SQUARE: FEndCap := kCGLineCapSquare;
3014       PS_ENDCAP_FLAT: FEndCap := kCGLineCapButt;
3015     end;
3016     FWidth := Max(1, dwWidth);
3017   end
3018   else
3019     FWidth := 1;
3020 
3021   if (dwPenStyle and PS_STYLE_MASK) = PS_USERSTYLE then
3022   begin
3023     SetLength(Dashes, dwStyleCount);
3024     for i := 0 to dwStyleCount - 1 do
3025       Dashes[i] := lpStyle[i];
3026   end;
3027 
3028   FStyle := dwPenStyle and PS_STYLE_MASK;
3029 end;
3030 
3031 constructor TCocoaPen.Create(const ABrush: TCocoaBrush; const AGlobal: Boolean);
3032 begin
3033   inherited Create(ABrush.ColorRef, True, AGlobal);
3034   FStyle := PS_SOLID;
3035   FWidth := 1;
3036   FIsExtPen := False;
3037   Dashes := nil;
3038 end;
3039 
3040 constructor TCocoaPen.Create(const AColor: TColor; AGlobal: Boolean);
3041 begin
3042   inherited Create(AColor, True, AGlobal);
3043   FStyle := PS_SOLID;
3044   FWidth := 1;
3045   FIsExtPen := False;
3046   Dashes := nil;
3047 end;
3048 
3049 constructor TCocoaPen.Create(const AColor: TColor; AStyle: TFPPenStyle;
3050   ACosmetic: Boolean; AWidth: Integer; AMode: TFPPenMode; AEndCap: TFPPenEndCap;
3051   AJoinStyle: TFPPenJoinStyle; AGlobal: Boolean);
3052 begin
3053   inherited Create(AColor, True, AGlobal);
3054 
3055   case AStyle of
3056     psSolid:       FStyle := PS_SOLID;
3057     psDash:        FStyle := PS_DASH;
3058     psDot:         FStyle := PS_DOT;
3059     psDashDot:     FStyle := PS_DASHDOT;
3060     psDashDotDot:  FStyle := PS_DASHDOTDOT;
3061     psinsideFrame: FStyle := PS_INSIDEFRAME;
3062     psPattern:     FStyle := PS_USERSTYLE;
3063     psClear:       FStyle := PS_NULL;
3064   end;
3065 
3066   if ACosmetic then
3067   begin
3068     FWidth := 1;
3069     FIsGeometric := False;
3070   end
3071   else
3072   begin
3073     FIsGeometric := True;
3074 
3075     case AJoinStyle of
3076       pjsRound: FJoinStyle := kCGLineJoinRound;
3077       pjsBevel: FJoinStyle := kCGLineJoinBevel;
3078       pjsMiter: FJoinStyle := kCGLineJoinMiter;
3079     end;
3080 
3081     case AEndCap of
3082       pecRound: FEndCap := kCGLineCapRound;
3083       pecSquare: FEndCap := kCGLineCapSquare;
3084       pecFlat: FEndCap := kCGLineCapButt;
3085     end;
3086     FWidth := Max(1, AWidth);
3087   end;
3088   FIsExtPen := False;
3089   Dashes := nil;
3090 end;
3091 
3092 { TCocoaBrush }
3093 
3094 procedure DrawBitmapPattern(info: UnivPtr; c: CGContextRef); MWPascal;
3095 var
3096   ABrush: TCocoaBrush absolute info;
3097 begin
3098   ABrush.DrawPattern(c);
3099 end;
3100 
3101 procedure TCocoaBrush.SetHatchStyle(AHatch: PtrInt);
3102 const
3103   HATCH_DATA: array[HS_HORIZONTAL..HS_DIAGCROSS] of array[0..7] of Byte =
3104  (
3105  { HS_HORIZONTAL } ($FF, $FF, $FF, $00, $FF, $FF, $FF, $FF),
3106  { HS_VERTICAL   } ($F7, $F7, $F7, $F7, $F7, $F7, $F7, $F7),
3107  { HS_FDIAGONAL  } ($7F, $BF, $DF, $EF, $F7, $FB, $FD, $FE),
3108  { HS_BDIAGONAL  } ($FE, $FD, $FB, $F7, $EF, $DF, $BF, $7F),
3109  { HS_CROSS      } ($F7, $F7, $F7, $00, $F7, $F7, $F7, $F7),
3110  { HS_DIAGCROSS  } ($7E, $BD, $DB, $E7, $E7, $DB, $BD, $7E)
3111   );
3112 var
3113   ACallBacks: CGPatternCallbacks;
3114   CGDataProvider: CGDataProviderRef;
3115 begin
3116   if AHatch in [HS_HORIZONTAL..HS_DIAGCROSS] then
3117   begin
3118     FillChar(ACallBacks, SizeOf(ACallBacks), 0);
3119     ACallBacks.drawPattern := @DrawBitmapPattern;
3120     if (FBitmap <> nil) then FBitmap.Release;
3121     FBitmap := TCocoaBitmap.Create(8, 8, 1, 1, cbaByte, cbtMask, @HATCH_DATA[AHatch]);
3122     if FImage <> nil then CGImageRelease(FImage);
3123     CGDataProvider := CGDataProviderCreateWithData(nil, @HATCH_DATA[AHatch], 8, nil);
3124     FImage := CGImageMaskCreate(8, 8, 1, 1, 1, CGDataProvider, nil, 0);
3125     CGDataProviderRelease(CGDataProvider);
3126     FPatternColorMode := cpmBrushColor;
3127     if FCGPattern <> nil then CGPatternRelease(FCGPattern);
3128     FCGPattern := CGPatternCreate(Self, GetCGRect(0, 0, 8, 8),
3129       CGAffineTransformIdentity, 8.0, 8.0, kCGPatternTilingConstantSpacing,
3130       0, ACallBacks);
3131   end;
3132 end;
3133 
3134 procedure TCocoaBrush.SetBitmap(ABitmap: TCocoaBitmap);
3135 var
3136   AWidth, AHeight: Integer;
3137   ACallBacks: CGPatternCallbacks;
3138   CGDataProvider: CGDataProviderRef;
3139 begin
3140   AWidth := ABitmap.Width;
3141   AHeight := ABitmap.Height;
3142   FillChar(ACallBacks, SizeOf(ACallBacks), 0);
3143   ACallBacks.drawPattern := @DrawBitmapPattern;
3144   if (FBitmap <> nil) then FBitmap.Release;
3145   FBitmap := TCocoaBitmap.Create(ABitmap);
3146   if FImage <> nil then CGImageRelease(FImage);
3147   if FBitmap.BitmapType = cbtMono then
3148   begin
3149     with FBitmap do
3150     begin
3151       CGDataProvider := CGDataProviderCreateWithData(nil, Data, DataSize, nil);
3152       FImage := CGImageMaskCreate(Width, Height, BitsPerSample, BitsPerPixel, BytesPerRow, CGDataProvider, nil, 0);
3153       CGDataProviderRelease(CGDataProvider);
3154     end;
3155     FPatternColorMode := cpmContextColor;
3156   end
3157   else
3158   begin
3159     FImage := CGImageCreateCopy(MacOSAll.CGImageRef( FBitmap.imageRep.CGImageForProposedRect_context_hints(nil, nil, nil)));
3160     FPatternColorMode := cpmBitmap;
3161   end;
3162   if FCGPattern <> nil then CGPatternRelease(FCGPattern);
3163   FCGPattern := CGPatternCreate(Self, GetCGRect(0, 0, AWidth, AHeight),
3164     CGAffineTransformIdentity, CGFloat(AWidth), CGFloat(AHeight), kCGPatternTilingConstantSpacing,
3165     Ord(FPatternColorMode = cpmBitmap), ACallBacks);
3166 end;
3167 
3168 procedure TCocoaBrush.SetImage(AImage: NSImage);
3169 var
3170   ACallBacks: CGPatternCallbacks;
3171   Rect: CGRect;
3172 begin
3173   FillChar(ACallBacks, SizeOf(ACallBacks), 0);
3174   ACallBacks.drawPattern := @DrawBitmapPattern;
3175   if FImage <> nil then CGImageRelease(FImage);
3176   FImage := CGImageCreateCopy(MacOSAll.CGImageRef( AImage.CGImageForProposedRect_context_hints(nil, nil, nil)));
3177   FPatternColorMode := cpmBitmap;
3178   Rect.origin.x := 0;
3179   Rect.origin.y := 0;
3180   Rect.size := CGSize(AImage.size);
3181   if FCGPattern <> nil then CGPatternRelease(FCGPattern);
3182   FCGPattern := CGPatternCreate(Self, Rect,
3183     CGAffineTransformIdentity, Rect.size.width, Rect.size.height, kCGPatternTilingConstantSpacing,
3184     1, ACallBacks);
3185 end;
3186 
3187 procedure TCocoaBrush.SetColor(AColor: NSColor);
3188 var
3189   RGBColor, PatternColor: NSColor;
3190 begin
3191   Clear;
3192 
3193   FColor := AColor;
3194   FColor.retain;
3195 
3196   RGBColor := AColor.colorUsingColorSpaceName(NSDeviceRGBColorSpace);
3197 
3198   if Assigned(RGBColor) then
3199     SetColor(NSColorToRGB(RGBColor), True)
3200   else
3201   begin
3202     PatternColor := AColor.colorUsingColorSpaceName(NSPatternColorSpace);
3203     if Assigned(PatternColor) then
3204     begin
3205       SetColor(NSColorToColorRef(PatternColor.patternImage.backgroundColor), False);
3206       SetImage(PatternColor.patternImage);
3207     end
3208     else
3209       SetColor(0, True);
3210   end;
3211 end;
3212 
3213 constructor TCocoaBrush.CreateDefault(const AGlobal: Boolean = False);
3214 begin
3215   inherited Create(clWhite, True, AGlobal);
3216   FBitmap := nil;
3217   FImage := nil;
3218   FCGPattern := nil;
3219   FColor := nil;
3220 end;
3221 
3222 constructor TCocoaBrush.Create(const ALogBrush: TLogBrush; const AGlobal: Boolean = False);
3223 begin
3224   FCGPattern := nil;
3225   FBitmap := nil;
3226   FImage := nil;
3227   FColor := nil;
3228   case ALogBrush.lbStyle of
3229     BS_SOLID:
3230         inherited Create(ColorToRGB(TColor(ALogBrush.lbColor)), True, AGlobal);
3231     BS_HATCHED:        // Hatched brush.
3232       begin
3233         inherited Create(ColorToRGB(TColor(ALogBrush.lbColor)), True, AGlobal);
3234         SetHatchStyle(ALogBrush.lbHatch);
3235       end;
3236     BS_DIBPATTERN,
3237     BS_DIBPATTERN8X8,
3238     BS_DIBPATTERNPT,
3239     BS_PATTERN,
3240     BS_PATTERN8X8:
3241       begin
3242         inherited Create(ColorToRGB(TColor(ALogBrush.lbColor)), False, AGlobal);
3243         SetBitmap(TCocoaBitmap(ALogBrush.lbHatch));
3244       end
3245     else
3246       inherited Create(ColorToRGB(TColor(ALogBrush.lbColor)), False, AGlobal);
3247   end;
3248 end;
3249 
3250 constructor TCocoaBrush.Create(const AColor: NSColor; const AGlobal: Boolean);
3251 var
3252   RGBColor, PatternColor: NSColor;
3253 begin
3254   FColor := AColor;
3255   FColor.retain;
3256 
3257   FCGPattern := nil;
3258   FBitmap := nil;
3259   FImage := nil;
3260   RGBColor := AColor.colorUsingColorSpaceName(NSDeviceRGBColorSpace);
3261   if Assigned(RGBColor) then
3262     inherited Create(NSColorToRGB(RGBColor), True, AGlobal)
3263   else
3264   begin
3265     PatternColor := AColor.colorUsingColorSpaceName(NSPatternColorSpace);
3266     if Assigned(PatternColor) then
3267     begin
3268       inherited Create(NSColorToColorRef(PatternColor.patternImage.backgroundColor), False, AGlobal);
3269       SetImage(PatternColor.patternImage);
3270     end
3271     else
3272       inherited Create(0, True, AGlobal);
3273   end;
3274 end;
3275 
3276 constructor TCocoaBrush.Create(const AColor: TColor; AStyle: TFPBrushStyle; APattern: TBrushPattern;
3277   AGlobal: Boolean);
3278 begin
3279   case AStyle of
3280   bsSolid:
3281   begin
3282     inherited Create(AColor, True, AGlobal);
3283   end;
3284   // bsHorizontal, bsVertical, bsFDiagonal,
3285   // bsBDiagonal, bsCross, bsDiagCross,
3286   // bsImage, bsPattern
3287   else // bsClear
3288     inherited Create(AColor, False, AGlobal);
3289   end;
3290 end;
3291 
3292 procedure TCocoaBrush.DrawPattern(c: CGContextRef);
3293 var
3294   R: CGRect;
3295   sR, sG, sB: single;
3296 begin
3297   R:=CGRectMake(0, 0, CGImageGetWidth(FImage), CGImageGetHeight(FImage));
3298   if FPatternColorMode = cpmContextColor then
3299   begin
3300     CGContextSetRGBFillColor(c, Red/255, Green/255, Blue/255, 1);
3301     CGContextFillRect(c, R);
3302     ColorToRGBFloat(FFgColor, sR, sG, sB);
3303     CGContextSetRGBFillColor(c, sR, sG, sB, 1);
3304   end;
3305   CGContextDrawImage(c, R, FImage);
3306 end;
3307 
3308 procedure TCocoaBrush.Clear;
3309 begin
3310   if FColor <> nil then
3311   begin
3312     FColor.release;
3313     FColor := nil;
3314   end;
3315 
3316   if FCGPattern <> nil then
3317   begin
3318     CGPatternRelease(FCGPattern);
3319     FCGPattern := nil;
3320   end;
3321 
3322   if FBitmap <> nil then
3323   begin
3324     FBitmap.Release;
3325     FBitmap := nil;
3326   end;
3327 
3328   if FImage <> nil then
3329   begin
3330     CGImageRelease(FImage);
3331     FImage := nil;
3332   end;
3333 end;
3334 
3335 destructor TCocoaBrush.Destroy;
3336 begin
3337   Clear;
3338   inherited Destroy;
3339 end;
3340 
3341 procedure TCocoaBrush.Apply(ADC: TCocoaContext; UseROP2: Boolean = True);
3342 var
3343   RGBA: array[0..3] of CGFloat;
3344   AROP2: Integer;
3345   APatternSpace: CGColorSpaceRef;
3346   BaseSpace: CGColorSpaceRef;
3347   sR, sG, sB: single;
3348   sz: CGSize;
3349   offset: TPoint;
3350 begin
3351   if ADC = nil then Exit;
3352 
3353   if ADC.CGContext = nil then
3354     Exit;
3355 
3356   if UseROP2 then
3357     AROP2 := ADC.ROP2
3358   else
3359     AROP2 := R2_COPYPEN;
3360 
3361   GetRGBA(AROP2, RGBA[0], RGBA[1], RGBA[2], RGBA[3]);
3362 
3363   //if AROP2 <> R2_NOT then
3364     //CGContextSetBlendMode(ADC.CGContext, kCGBlendModeNormal)
3365   //else
3366     //CGContextSetBlendMode(ADC.CGContext, kCGBlendModeDifference);
3367 
3368   if Assigned(FCGPattern) then
3369   begin
3370     // Set proper pattern alignment
3371     offset:=ADC.GetLogicalOffset;
3372     with CGPointApplyAffineTransform(CGPointMake(0,0), CGContextGetCTM(ADC.CGContext)) do
3373     begin
3374       sz.width:=x - offset.X;
3375       sz.height:=y + offset.Y;
3376       sz.width:=Round(sz.width) mod CGImageGetWidth(FImage);
3377       sz.height:=Round(sz.height) mod CGImageGetHeight(FImage);
3378     end;
3379     CGContextSetPatternPhase(ADC.CGContext, sz);
3380 
3381     case FPatternColorMode of
3382       cpmBitmap:
3383         begin
3384           BaseSpace := nil;
3385           RGBA[0] := 1.0;
3386         end;
3387       cpmBrushColor:
3388         begin
3389           BaseSpace := CGColorSpaceCreateDeviceRGB;
3390         end;
3391       cpmContextColor:
3392         begin
3393           BaseSpace := CGColorSpaceCreateDeviceRGB;
3394           SetColor(ADC.BkColor, True);
3395           FFgColor:=ColorToRGB(ADC.TextColor);
3396           ColorToRGBFloat(FFgColor, sR, sG, sB);
3397           RGBA[0]:=sR;
3398           RGBA[1]:=sG;
3399           RGBA[2]:=sB;
3400           RGBA[3]:=1.0;
3401         end;
3402     end;
3403     APatternSpace := CGColorSpaceCreatePattern(BaseSpace);
3404     CGContextSetFillColorSpace(ADC.CGContext, APatternSpace);
3405     CGColorSpaceRelease(APatternSpace);
3406     if Assigned(BaseSpace) then CGColorSpaceRelease(BaseSpace);
3407     CGContextSetFillPattern(ADC.CGcontext, FCGPattern, @RGBA[0]);
3408   end
3409   else
3410   begin
3411     CGContextSetRGBFillColor(ADC.CGContext, RGBA[0], RGBA[1], RGBA[2], RGBA[3]);
3412   end;
3413 end;
3414 
3415 procedure TCocoaBrush.ApplyAsPenColor(ADC: TCocoaContext; UseROP2: Boolean);
3416 var
3417   AR, AG, AB, AA: CGFloat;
3418   AROP2: Integer;
3419   ADashes: array [0..15] of CGFloat;
3420   ADashLen: Integer;
3421   StatDash: PCocoaStatDashes;
3422   isCosm  : Boolean;
3423   WidthMul : array [Boolean] of CGFloat;
3424 begin
3425   if ADC = nil then Exit;
3426   if ADC.CGContext = nil then Exit;
3427 
3428   if UseROP2 then
3429     AROP2 := ADC.ROP2
3430   else
3431     AROP2 := R2_COPYPEN;
3432 
3433   GetRGBA(AROP2, AR, AG, AB, AA);
3434 
3435   CGContextSetRGBStrokeColor(ADC.CGContext, AR, AG, AB, AA);
3436 end;
3437 
3438 { TCocoaGDIObject }
3439 
3440 constructor TCocoaGDIObject.Create(AGlobal: Boolean);
3441 begin
3442   FRefCount := 1;
3443   FGlobal := AGlobal;
3444 end;
3445 
3446 destructor TCocoaGDIObject.Destroy;
3447 begin
3448   if not FGlobal then
3449   begin
3450     Dec(FRefCount);
3451     if FRefCount <> 0 then
3452     begin
3453       //DebugLn('TCocoaGDIObject.Destroy Error - ', dbgsName(self), ' RefCount = ', dbgs(FRefCount));
3454       FRefCount := FRefCount;
3455     end;
3456   end;
3457 end;
3458 
TCocoaGDIObject.UpdateRefsnull3459 class function TCocoaGDIObject.UpdateRefs(ATarget: TCocoaGDIObject; ASource: TCocoaGDIObject): Boolean; static;
3460 begin
3461   result := ASource <> ATarget;
3462   if result then
3463   begin
3464     if Assigned(ASource) then
3465       ASource.AddRef;
3466     if Assigned(ATarget) then
3467       ATarget.Release;
3468   end;
3469 end;
3470 
3471 procedure TCocoaGDIObject.AddRef;
3472 begin
3473   if FGlobal then Exit;
3474   inc(FRefCount);
3475 end;
3476 
3477 procedure TCocoaGDIObject.Release;
3478 begin
3479   if FGlobal then Exit;
3480   if FRefCount <= 1 then
3481     self.Free                     // the last reference, so free it using the destructor
3482   else
3483     Dec(FRefCount);
3484 end;
3485 
3486 initialization
3487 
3488 
3489 finalization
3490 
3491 
3492 end.
3493