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