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