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