BaiduFMX

Форк
0
/
FMX.Graphics.NativeCanvas.pas 
1780 строк · 70.5 Кб
1
// ***************************************************************************
2
//
3
// Firemonkey Native Canvas Class
4
//
5
// Copyright 2017 Aone (amtbonechen@gmail.com), 谢顿 (zhaoyipeng@hotmail.com)
6
//
7
// This unit is based on Aone's FMX.Graphics.Native.pas, 谢顿 changed it
8
// to INativeCanvas, the original version information is below
9
//
10
// ***************************************************************************
11
{ ------------------------------------------ }
12
{ }
13
{ (c) 2017 by Aone }
14
{ }
15
{ QQ: 1467948783 }
16
{ }
17
{ http://www.cnblogs.com/onechen }
18
{ }
19
{ ------------------------------------------ }
20
{ Start: 2017.01.16 }
21
{ ------------------------------------------ }
22
// [原创] 改善 Firemonkey Canvas 几何绘图质量问题(移动平台)by Aone
23

24
// The basic usage of this unit:
25
// write these code in you paint method
26
// var
27
// Canvas: INativeCanvas;
28
// begin
29
// Canvas := Self.Canvas.ToNativeCanvas(TDrawMethod.Native);
30
//
31
// Canvas.NativeDraw(LocalRect, procedure begin
32
// Canvas....
33
// end);
34

35
unit FMX.Graphics.NativeCanvas;
36

37
interface
38

39
uses
40
  System.Types,
41
  System.UITypes,
42
  System.UIConsts,
43
  System.Math,
44
  System.Math.Vectors,
45
  System.Classes,
46
  System.SysUtils,
47
  System.Generics.Collections,
48

49
{$IFDEF ANDROID}
50
  Androidapi.JNI.JavaTypes,
51
  Androidapi.JNI.GraphicsContentViewText,
52
  Androidapi.JNIBridge,
53
  Androidapi.Helpers,
54
  Androidapi.Bitmap,
55
  Androidapi.JNI.App,
56
  FMX.Surfaces,
57
  FMX.Helpers.Android,
58
{$ENDIF}
59
{$IFDEF IOS}
60
  iOSapi.CocoaTypes,
61
  iOSapi.UIKit,
62
  iOSapi.Foundation,
63
  iOSapi.CoreText,
64
  iOSapi.CoreGraphics,
65
  Macapi.CoreFoundation,
66
  Macapi.ObjectiveC,
67
  Macapi.Helpers,
68
  FMX.FontGlyphs.iOS,
69
  FMX.Helpers.iOS,
70
{$ENDIF}
71
  FMX.Types,
72
  FMX.Graphics,
73
  FMX.Platform,
74
  FMX.Graphics.INativeCanvas;
75

76
type
77
{$IFDEF IOS64}
78
  TDashArray = TArray<CGFloat>; // iOS 64 必需使用 Double 或 CGFloat 否則無法正常顯示虛線
79
{$ENDIF}
80

81
  TFiremonkeyCanvas = class(TAbstractCanvas)
82
  public
83
    procedure NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc); override;
84
    // 涂色 + 线色一次完成
85
    procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const ACornerType: TCornerType = TCornerType.Round; const Inside: Boolean = False); overload; override;
86
    procedure DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); overload; override;
87
    procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
88
    procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
89
    procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); overload; override;
90

91
    // 下列为 Canvas 原有函数
92
    procedure DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean = False); override;
93
    procedure FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign: TTextAlign; const AVTextAlign: TTextAlign = TTextAlign.Center); override;
94

95
    procedure SetMatrix(const M: TMatrix); override;
96

97
    procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single); overload; override;
98
    procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
99

100
    procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
101
    procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType = TCornerType.Round); overload; override;
102
    procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
103
    procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TStrokeBrush; const ACornerType: TCornerType = TCornerType.Round); overload; override;
104

105
    procedure FillPath(const APath: TPathData; const AOpacity: Single); overload; override;
106
    procedure FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush); overload; override;
107
    procedure DrawPath(const APath: TPathData; const AOpacity: Single); overload; override;
108
    procedure DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
109

110
    procedure FillEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
111
    procedure FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush); overload; override;
112
    procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
113
    procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
114

115
    procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
116
    procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush); overload; override;
117
    procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
118
    procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
119

120
    procedure FillPolygon(const Points: TPolygon; const AOpacity: Single); override;
121
    procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single); overload; override;
122

123
    procedure IntersectClipRect(const ARect: TRectF); override;
124
    procedure ExcludeClipRect(const ARect: TRectF); override;
125
  end;
126

127
{$IF Defined(ANDROID) or Defined(IOS)}
128

129
  TCustomNativeCanvas = class(TAbstractCanvas)
130
  private
131
    class var FModulateColor: TAlphaColor;
132
  public
133
    // 涂色 + 线色一次完成
134
    procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const ACornerType: TCornerType = TCornerType.Round; const Inside: Boolean = False); overload; override;
135
    procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
136
    procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
137
    procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); overload; override;
138

139
    // 下列为 Canvas 原有函数
140
    procedure FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign: TTextAlign; const AVTextAlign: TTextAlign = TTextAlign.Center); override;
141
    procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single); overload; override;
142

143
    procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
144
    procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType = TCornerType.Round); overload; override;
145
    procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
146
    procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TStrokeBrush; const ACornerType: TCornerType = TCornerType.Round); overload; override;
147

148
    procedure FillPath(const APath: TPathData; const AOpacity: Single); overload; override;
149
    procedure FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush); overload; override;
150
    procedure DrawPath(const APath: TPathData; const AOpacity: Single); overload; override;
151
    procedure DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
152

153
    procedure FillEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
154
    procedure FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush); overload; override;
155
    procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
156
    procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
157

158
    procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
159
    procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush); overload; override;
160
    procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
161
    procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
162

163
    procedure FillPolygon(const Points: TPolygon; const AOpacity: Single); override;
164
    procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single); overload; override;
165

166
    class property ModulateColor: TAlphaColor read FModulateColor write FModulateColor;
167
  end;
168
{$ENDIF}
169
{$IFDEF ANDROID}
170

171
  TAndroidNativeCanvas = class(TCustomNativeCanvas)
172
  private
173
    GlobalCanvas: JCanvas;
174
    procedure ApplyGradient(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF);
175
    procedure ApplyFill(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
176
    procedure DrawFill(const Paint1: JPaint; const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
177
    procedure ApplyStroke(const Paint1: JPaint; const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
178
  public
179
    procedure NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc); override;
180
    procedure DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean = False); override;
181
    procedure DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); override;
182

183
    // 下列为 Canvas 原有函数
184
    procedure FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign: TTextAlign; const AVTextAlign: TTextAlign = TTextAlign.Center); override;
185
    procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
186

187
    procedure IntersectClipRect(const ARect: TRectF); override;
188
    procedure ExcludeClipRect(const ARect: TRectF); override;
189
  end;
190
{$ENDIF}
191
{$IFDEF IOS}
192

193
  TIOSNativeCanvas = class(TCustomNativeCanvas)
194
  private
195
    GlobalCanvas: CGContextRef;
196
    procedure ApplyGradient(const ABrush: TBrush; const ARect: TRectF);
197
    procedure ApplyFill(const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
198
    procedure DrawFill(const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
199
    procedure ApplyStroke(const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
200
    function GetPostScriptFontName: CFStringRef;
201
  public
202
    constructor Create(ACanvas: TCanvas); override;
203
    procedure NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc); override;
204
    procedure DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean = False); override;
205

206
    procedure SetMatrix(const M: TMatrix); override;
207

208
    procedure DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); override;
209

210
    // 下列为 Canvas 原有函数
211
    procedure FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign: TTextAlign; const AVTextAlign: TTextAlign = TTextAlign.Center); override;
212
    procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
213

214
    procedure IntersectClipRect(const ARect: TRectF); override;
215
    procedure ExcludeClipRect(const ARect: TRectF); override;
216
  end;
217
{$ENDIF}
218

219
  TCanvasHelper = class helper for TCanvas
220
  public
221
    function ToNativeCanvas(AMethod: TDrawMethod): INativeCanvas;
222
  end;
223

224
implementation
225

226
type
227
  TMyCanvas = class(TCanvas)
228

229
  end;
230
{$IFDEF IOS}
231
  NSStringClass = interface(NSObjectClass)
232
    ['{B324D490-B58F-4BE8-A5F4-7DAD2E142A5E}']
233
    {class} function availableStringEncodings: PNSStringEncoding; cdecl;
234
    {class} function defaultCStringEncoding: NSStringEncoding; cdecl;
235
    {class} function localizedNameOfStringEncoding(encoding: NSStringEncoding): Pointer; cdecl;
236
    {class} function localizedStringWithFormat(localizedStringWithFormat: NSString): Pointer; cdecl;
237
    {class} function pathWithComponents(components: NSArray): Pointer; cdecl;
238
    {class} function stringWithCString(bytes: MarshaledAString): Pointer; cdecl; overload;
239
    {class} function stringWithCString(cString: MarshaledAString; encoding: NSStringEncoding): Pointer; cdecl; overload;
240
//    {class} function stringWithCString(bytes: MarshaledAString; length: NSUInteger): Pointer; cdecl; overload;
241
    {class} function stringWithCharacters(characters: MarshaledString; length: NSUInteger): Pointer; cdecl;
242
    {class} function stringWithContentsOfFile(path: NSString): Pointer; cdecl; overload;
243
    {class} function stringWithContentsOfFile(path: NSString; encoding: NSStringEncoding; error: PPointer): Pointer; cdecl; overload;
244
    {class} function stringWithContentsOfFile(path: NSString; usedEncoding: PNSStringEncoding; error: PPointer): Pointer; cdecl; overload;
245
    {class} function stringWithContentsOfURL(url: NSURL): Pointer; cdecl; overload;
246
    {class} function stringWithContentsOfURL(url: NSURL; encoding: NSStringEncoding; error: PPointer): Pointer; cdecl; overload;
247
    {class} function stringWithContentsOfURL(url: NSURL; usedEncoding: PNSStringEncoding; error: PPointer): Pointer; cdecl; overload;
248
    {class} function stringWithFormat(stringWithFormat: NSString): Pointer; cdecl;
249
    {class} function stringWithString(string_: NSString): Pointer; cdecl;
250
    {class} function stringWithUTF8String(nullTerminatedCString: MarshaledAString): Pointer; cdecl;
251
  end;
252

253
   NSString = interface(NSObject)
254
    ['{A62E83E4-AEB3-405F-8AFA-5B873D6E057F}']
255
    function UTF8String: MarshaledAString; cdecl;
256
    function boolValue: Boolean; cdecl;
257
    function cString: MarshaledAString; cdecl;
258
    function cStringLength: NSUInteger; cdecl;
259
    function cStringUsingEncoding(encoding: NSStringEncoding): MarshaledAString; cdecl;
260
    function canBeConvertedToEncoding(encoding: NSStringEncoding): Boolean; cdecl;
261
    function capitalizedString: NSString; cdecl;
262
    function caseInsensitiveCompare(string_: NSString): NSComparisonResult; cdecl;
263
    function characterAtIndex(index: NSUInteger): unichar; cdecl;
264
    function commonPrefixWithString(aString: NSString; options: NSStringCompareOptions): NSString; cdecl;
265
    function compare(string_: NSString): NSComparisonResult; cdecl; overload;
266
    function compare(string_: NSString; options: NSStringCompareOptions): NSComparisonResult; cdecl; overload;
267
    function compare(string_: NSString; options: NSStringCompareOptions; range: NSRange): NSComparisonResult; cdecl; overload;
268
    function compare(string_: NSString; options: NSStringCompareOptions; range: NSRange; locale: Pointer): NSComparisonResult; cdecl; overload;
269
    function completePathIntoString(outputName: NSString; caseSensitive: Boolean; matchesIntoArray: NSArray; filterTypes: NSArray): NSUInteger; cdecl;
270
    function componentsSeparatedByCharactersInSet(separator: NSCharacterSet): NSArray; cdecl;
271
    function componentsSeparatedByString(separator: NSString): NSArray; cdecl;
272
    function dataUsingEncoding(encoding: NSStringEncoding): NSData; cdecl; overload;
273
    function dataUsingEncoding(encoding: NSStringEncoding; allowLossyConversion: Boolean): NSData; cdecl; overload;
274
    function decomposedStringWithCanonicalMapping: NSString; cdecl;
275
    function decomposedStringWithCompatibilityMapping: NSString; cdecl;
276
    function description: NSString; cdecl;
277
    function doubleValue: double; cdecl;
278
    function fastestEncoding: NSStringEncoding; cdecl;
279
    function fileSystemRepresentation: MarshaledAString; cdecl;
280
    function floatValue: Single; cdecl;
281
    function getBytes(buffer: Pointer; maxLength: NSUInteger; usedLength: NSUInteger; encoding: NSStringEncoding; options: NSStringEncodingConversionOptions; range: NSRange; remainingRange: PNSRange): Boolean; cdecl;
282
    procedure getCString(bytes: MarshaledAString); cdecl; overload;
283
    procedure getCString(bytes: MarshaledAString; maxLength: NSUInteger); cdecl; overload;
284
    function getCString(buffer: MarshaledAString; maxLength: NSUInteger; encoding: NSStringEncoding): Boolean; cdecl; overload;
285
    procedure getCString(bytes: MarshaledAString; maxLength: NSUInteger; range: NSRange; remainingRange: PNSRange); cdecl; overload;
286
    procedure getCharacters(buffer: MarshaledString); cdecl; overload;
287
    procedure getCharacters(buffer: MarshaledString; range: NSRange); cdecl; overload;
288
    function getFileSystemRepresentation(cname: MarshaledAString; maxLength: NSUInteger): Boolean; cdecl;
289
    function hasPrefix(aString: NSString): Boolean; cdecl;
290
    function hasSuffix(aString: NSString): Boolean; cdecl;
291
    function hash: NSUInteger; cdecl;
292
    function init: Pointer; cdecl;
293
    function initWithBytes(bytes: Pointer; length: NSUInteger; encoding: NSStringEncoding): Pointer; cdecl;
294
    function initWithBytesNoCopy(bytes: Pointer; length: NSUInteger; encoding: NSStringEncoding; freeWhenDone: Boolean): Pointer; cdecl;
295
    function initWithCString(bytes: MarshaledAString): Pointer; cdecl; overload;
296
    function initWithCString(nullTerminatedCString: MarshaledAString; encoding: NSStringEncoding): Pointer; cdecl; overload;
297
//    function initWithCString(bytes: PAnsiChar; length: NSUInteger): Pointer; cdecl; overload;
298
    function initWithCStringNoCopy(bytes: MarshaledAString; length: NSUInteger; freeWhenDone: Boolean): Pointer; cdecl;
299
    function initWithCharacters(characters: MarshaledString; length: NSUInteger): Pointer; cdecl;
300
    function initWithCharactersNoCopy(characters: MarshaledString; length: NSUInteger; freeWhenDone: Boolean): Pointer; cdecl;
301
    function initWithContentsOfFile(path: NSString): Pointer; cdecl; overload;
302
    function initWithContentsOfFile(path: NSString; encoding: NSStringEncoding; error: PPointer): Pointer; cdecl; overload;
303
    function initWithContentsOfFile(path: NSString; usedEncoding: PNSStringEncoding; error: PPointer): Pointer; cdecl; overload;
304
    function initWithContentsOfURL(url: NSURL): Pointer; cdecl; overload;
305
    function initWithContentsOfURL(url: NSURL; encoding: NSStringEncoding; error: PPointer): Pointer; cdecl; overload;
306
    function initWithContentsOfURL(url: NSURL; usedEncoding: PNSStringEncoding; error: PPointer): Pointer; cdecl; overload;
307
    function initWithData(data: NSData; encoding: NSStringEncoding): Pointer; cdecl;
308
    function initWithFormat(initWithFormat: NSString): Pointer; cdecl; overload;
309
    function initWithFormat(format: NSString; locale: Pointer): Pointer; cdecl; overload;
310
    function initWithString(aString: NSString): Pointer; cdecl;
311
    function initWithUTF8String(nullTerminatedCString: MarshaledAString): Pointer; cdecl;
312
    function intValue: Integer; cdecl;
313
    function integerValue: NSInteger; cdecl;
314
    function isAbsolutePath: Boolean; cdecl;
315
    function isEqualToString(aString: NSString): Boolean; cdecl;
316
    function lastPathComponent: NSString; cdecl;
317
    function length: NSUInteger; cdecl;
318
    function lengthOfBytesUsingEncoding(enc: NSStringEncoding): NSUInteger; cdecl;
319
    function lineRangeForRange(range: NSRange): NSRange; cdecl;
320
    function localizedCaseInsensitiveCompare(string_: NSString): NSComparisonResult; cdecl;
321
    function localizedCompare(string_: NSString): NSComparisonResult; cdecl;
322
    function localizedStandardCompare(string_: NSString): NSComparisonResult; cdecl;
323
    function longLongValue: Int64; cdecl;
324
    function lossyCString: MarshaledAString; cdecl;
325
    function lowercaseString: NSString; cdecl;
326
    function maximumLengthOfBytesUsingEncoding(enc: NSStringEncoding): NSUInteger; cdecl;
327
    function paragraphRangeForRange(range: NSRange): NSRange; cdecl;
328
    function pathComponents: NSArray; cdecl;
329
    function pathExtension: NSString; cdecl;
330
    function precomposedStringWithCanonicalMapping: NSString; cdecl;
331
    function precomposedStringWithCompatibilityMapping: NSString; cdecl;
332
    function propertyList: Pointer; cdecl;
333
    function propertyListFromStringsFileFormat: NSDictionary; cdecl;
334
    function rangeOfCharacterFromSet(aSet: NSCharacterSet): NSRange; cdecl; overload;
335
    function rangeOfCharacterFromSet(aSet: NSCharacterSet; options: NSStringCompareOptions): NSRange; cdecl; overload;
336
    function rangeOfCharacterFromSet(aSet: NSCharacterSet; options: NSStringCompareOptions; range: NSRange): NSRange; cdecl; overload;
337
    function rangeOfComposedCharacterSequenceAtIndex(index: NSUInteger): NSRange; cdecl;
338
    function rangeOfComposedCharacterSequencesForRange(range: NSRange): NSRange; cdecl;
339
    function rangeOfString(aString: NSString): NSRange; cdecl; overload;
340
    function rangeOfString(aString: NSString; options: NSStringCompareOptions): NSRange; cdecl; overload;
341
    function rangeOfString(aString: NSString; options: NSStringCompareOptions; range: NSRange): NSRange; cdecl; overload;
342
    function rangeOfString(aString: NSString; options: NSStringCompareOptions; range: NSRange; locale: NSLocale): NSRange; cdecl; overload;
343
    function smallestEncoding: NSStringEncoding; cdecl;
344
    function stringByAbbreviatingWithTildeInPath: NSString; cdecl;
345
    function stringByAddingPercentEscapesUsingEncoding(enc: NSStringEncoding): NSString; cdecl;
346
    function stringByAppendingFormat(stringByAppendingFormat: NSString): NSString; cdecl;
347
    function stringByAppendingPathComponent(str: NSString): NSString; cdecl;
348
    function stringByAppendingPathExtension(str: NSString): NSString; cdecl;
349
    function stringByAppendingString(aString: NSString): NSString; cdecl;
350
    function stringByDeletingLastPathComponent: NSString; cdecl;
351
    function stringByDeletingPathExtension: NSString; cdecl;
352
    function stringByExpandingTildeInPath: NSString; cdecl;
353
    function stringByFoldingWithOptions(options: NSStringCompareOptions; locale: NSLocale): NSString; cdecl;
354
    function stringByPaddingToLength(newLength: NSUInteger; withString: NSString; startingAtIndex: NSUInteger): NSString; cdecl;
355
    function stringByReplacingCharactersInRange(range: NSRange; withString: NSString): NSString; cdecl;
356
    function stringByReplacingOccurrencesOfString(target: NSString; withString: NSString): NSString; cdecl; overload;
357
    function stringByReplacingOccurrencesOfString(target: NSString; withString: NSString; options: NSStringCompareOptions; range: NSRange): NSString; cdecl; overload;
358
    function stringByReplacingPercentEscapesUsingEncoding(enc: NSStringEncoding): NSString; cdecl;
359
    function stringByResolvingSymlinksInPath: NSString; cdecl;
360
    function stringByStandardizingPath: NSString; cdecl;
361
    function stringByTrimmingCharactersInSet(set_: NSCharacterSet): NSString; cdecl;
362
    function stringsByAppendingPaths(paths: NSArray): NSArray; cdecl;
363
    function substringFromIndex(from: NSUInteger): NSString; cdecl;
364
    function substringToIndex(to_: NSUInteger): NSString; cdecl;
365
    function substringWithRange(range: NSRange): NSString; cdecl;
366
    function uppercaseString: NSString; cdecl;
367
    function writeToFile(path: NSString; atomically: Boolean): Boolean; cdecl; overload;
368
    function writeToFile(path: NSString; atomically: Boolean; encoding: NSStringEncoding; error: PPointer): Boolean; cdecl; overload;
369
    function writeToURL(url: NSURL; atomically: Boolean): Boolean; cdecl; overload;
370
    function writeToURL(url: NSURL; atomically: Boolean; encoding: NSStringEncoding; error: PPointer): Boolean; cdecl; overload;
371
    procedure drawInRect(aRect: NSRect; withAttributes: NSDictionary); cdecl;
372
    procedure drawAtPoint(aPoint: NSPoint; withAttributes: NSDictionary); cdecl;
373
    function sizeWithAttributes(attributes: NSDictionary): NSSize; cdecl;
374
    function boundingRectWithSize(size: NSSize; options: NSStringDrawingOptions; attributes: NSDictionary): NSRect; cdecl;
375
  end;
376
  TNSString = class(TOCGenericImport<NSStringClass, NSString>)  end;
377

378
  UIFontClass = interface(NSObjectClass)
379
    ['{F21CAA74-9F23-42C5-A0F3-CECA57AFB3BC}']
380
    {class} function boldSystemFontOfSize(fontSize: CGFloat): Pointer; cdecl;
381
    {class} function buttonFontSize: CGFloat; cdecl;
382
    {class} function familyNames: NSArray; cdecl;
383
    {class} function fontNamesForFamilyName(familyName: NSString): NSArray; cdecl;
384
    {class} function fontWithName(fontName: NSString; size: CGFloat): Pointer; cdecl;
385
    {class} function italicSystemFontOfSize(fontSize: CGFloat): Pointer; cdecl;
386
    {class} function labelFontSize: CGFloat; cdecl;
387
    {class} function smallSystemFontSize: CGFloat; cdecl;
388
    {class} function systemFontOfSize(fontSize: CGFloat): Pointer; cdecl;
389
    {class} function systemFontSize: CGFloat; cdecl;
390
    {class} function fontWithDescriptor(descriptor: UIFontDescriptor; size: CGFloat): Pointer; cdecl;
391
  end;
392
  UIFont = interface(NSObject)
393
    ['{026495EC-177F-4517-9B25-C2F2371A110D}']
394
    function ascender: CGFloat; cdecl;
395
    function capHeight: CGFloat; cdecl;
396
    function descender: CGFloat; cdecl;
397
    function familyName: NSString; cdecl;
398
    function fontName: NSString; cdecl;
399
    function fontWithSize(fontSize: CGFloat): UIFont; cdecl;
400
    function leading: CGFloat; cdecl;
401
    function lineHeight: CGFloat; cdecl;
402
    function pointSize: CGFloat; cdecl;
403
    function xHeight: CGFloat; cdecl;
404
    function fontDescriptor: UIFontDescriptor; cdecl;
405
  end;
406
  TUIFont = class(TOCGenericImport<UIFontClass, UIFont>)  end;
407
{$ENDIF}
408

409
{ TFiremonkeyCanvas }
410

411
procedure TFiremonkeyCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean);
412
var
413
  R: TRectF;
414
  P: TPointF;
415
begin
416
  R := RectF(Center.X - Radius.X, Center.Y - Radius.Y, Center.X + Radius.X, Center.Y + Radius.Y);
417

418
  // 线在区内
419
  if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
420
  begin
421
    R.Offset(-R.Left, -R.Top);
422
    InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
423
  end;
424

425
  P := PointF(R.Width / 2, R.Height / 2);
426

427
  FCanvas.FillArc(Center, P, StartAngle, SweepAngle, AOpacity, AFill);
428
  FCanvas.DrawArc(Center, P, StartAngle, SweepAngle, AOpacity, AStroke);
429
end;
430

431
procedure TFiremonkeyCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean);
432
var
433
  R: TRectF;
434
begin
435
  R := ARect;
436

437
  // 线在区内
438
  if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
439
    InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
440

441
  FCanvas.FillEllipse(R, AOpacity, AFill);
442
  FCanvas.DrawEllipse(R, AOpacity, AStroke);
443
end;
444

445
procedure TFiremonkeyCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single);
446
begin
447
  FCanvas.DrawLine(APt1, APt2, AOpacity);
448
end;
449

450
procedure TFiremonkeyCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush);
451
begin
452
  FCanvas.DrawLine(APt1, APt2, AOpacity, ABrush);
453
end;
454

455
procedure TFiremonkeyCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
456
begin
457
  FCanvas.FillPath(APath, AOpacity, AFill);
458
  FCanvas.DrawPath(APath, AOpacity, AStroke);
459
end;
460

461
procedure TFiremonkeyCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush);
462
begin
463
  FCanvas.DrawPath(APath, AOpacity, ABrush);
464
end;
465

466
procedure TFiremonkeyCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single);
467
begin
468
  FCanvas.DrawPolygon(Points, AOpacity);
469
end;
470

471
procedure TFiremonkeyCanvas.DrawPath(const APath: TPathData; const AOpacity: Single);
472
begin
473
  FCanvas.DrawPath(APath, AOpacity);
474
end;
475

476
procedure TFiremonkeyCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
477
begin
478
  if AFill <> nil then
479
    FCanvas.Fill.Assign(AFill);
480

481
  if AStroke <> nil then
482
    FCanvas.Stroke.Assign(AStroke);
483

484
  FCanvas.FillPolygon(Points, AOpacity);
485
  FCanvas.DrawPolygon(Points, AOpacity);
486
end;
487

488
procedure TFiremonkeyCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TStrokeBrush; const ACornerType: TCornerType);
489
begin
490
  FCanvas.DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, ABrush, ACornerType);
491
end;
492

493
procedure TFiremonkeyCanvas.ExcludeClipRect(const ARect: TRectF);
494
begin
495
  FCanvas.ExcludeClipRect(ARect);
496
end;
497

498
procedure TFiremonkeyCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType);
499
begin
500
  FCanvas.DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, ACornerType);
501
end;
502

503
procedure TFiremonkeyCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const ACornerType: TCornerType; const Inside: Boolean);
504
var
505
  R: TRectF;
506
begin
507
  R := ARect;
508

509
  // 线在区内
510
  if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
511
    InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
512

513
  FCanvas.FillRect(R, XRadius, YRadius, ACorners, AOpacity, AFill, ACornerType);
514
  FCanvas.DrawRect(R, XRadius, YRadius, ACorners, AOpacity, AStroke, ACornerType);
515
end;
516

517
procedure TFiremonkeyCanvas.FillPath(const APath: TPathData; const AOpacity: Single);
518
begin
519
  FCanvas.FillPath(APath, AOpacity);
520
end;
521

522
procedure TFiremonkeyCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush);
523
begin
524
  FCanvas.FillArc(Center, Radius, StartAngle, SweepAngle, AOpacity, ABrush);
525
end;
526

527
procedure TFiremonkeyCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
528
begin
529
  FCanvas.FillArc(Center, Radius, StartAngle, SweepAngle, AOpacity);
530
end;
531

532
procedure TFiremonkeyCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush);
533
begin
534
  FCanvas.FillEllipse(ARect, AOpacity, ABrush);
535
end;
536

537
procedure TFiremonkeyCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single);
538
begin
539
  FCanvas.FillEllipse(ARect, AOpacity);
540
end;
541

542
procedure TFiremonkeyCanvas.FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush);
543
begin
544
  FCanvas.FillPath(APath, AOpacity, ABrush);
545
end;
546

547
procedure TFiremonkeyCanvas.FillPolygon(const Points: TPolygon; const AOpacity: Single);
548
begin
549
  FCanvas.FillPolygon(Points, AOpacity);
550
end;
551

552
procedure TFiremonkeyCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType);
553
begin
554
  FCanvas.FillRect(ARect, XRadius, YRadius, ACorners, AOpacity, ABrush, ACornerType);
555
end;
556

557
procedure TFiremonkeyCanvas.FillText(const ARect: TRectF; const AText: string;
558
  const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags;
559
  const ATextAlign, AVTextAlign: TTextAlign);
560
begin
561
  FCanvas.FillText(ARect, AText, WordWrap, AOpacity, Flags, ATextAlign, AVTextAlign);
562
end;
563

564
procedure TFiremonkeyCanvas.IntersectClipRect(const ARect: TRectF);
565
begin
566
  FCanvas.IntersectClipRect(ARect);
567
end;
568

569
procedure TFiremonkeyCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType);
570
begin
571
  FCanvas.FillRect(ARect, XRadius, YRadius, ACorners, AOpacity, ACornerType);
572
end;
573

574
procedure TFiremonkeyCanvas.NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc);
575
begin
576
  // 绘图函数
577
  if Assigned(ADrawProc) then
578
    ADrawProc;
579
end;
580

581
procedure TFiremonkeyCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
582
begin
583
  FCanvas.DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity);
584
end;
585

586
procedure TFiremonkeyCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush);
587
begin
588
  FCanvas.DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, ABrush);
589
end;
590

591
procedure TFiremonkeyCanvas.DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean);
592
begin
593
  FCanvas.DrawBitmap(ABitmap, SrcRect, DstRect, AOpacity, HighSpeed);
594
end;
595

596
procedure TFiremonkeyCanvas.SetMatrix(const M: TMatrix);
597
begin
598
  FCanvas.SetMatrix(M);
599
end;
600

601
procedure TFiremonkeyCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single);
602
begin
603
  FCanvas.DrawEllipse(ARect, AOpacity);
604
end;
605

606
procedure TFiremonkeyCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush);
607
begin
608
  FCanvas.FillEllipse(ARect, AOpacity, ABrush);
609
end;
610

611
{ TCanvasHelper }
612

613
function TCanvasHelper.ToNativeCanvas(AMethod: TDrawMethod): INativeCanvas;
614
begin
615
  case AMethod of
616
    Native:
617
{$IF DEFINED(ANDROID) OR DEFINED(IOS)}
618
{$IFDEF ANDROID}
619
      Result := TAndroidNativeCanvas.Create(Self);
620
{$ELSE}
621
      Result := TIOSNativeCanvas.Create(Self);
622
{$ENDIF}
623
{$ELSE}
624
      Result := TFiremonkeyCanvas.Create(Self);
625
{$ENDIF}
626
    Firemonkey:
627
      Result := TFiremonkeyCanvas.Create(Self);
628
  end;
629
end;
630

631
{$IF Defined(ANDROID) or Defined(IOS)}
632
{ TCustomNativeCanvas }
633

634
procedure TCustomNativeCanvas.FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign: TTextAlign; const AVTextAlign: TTextAlign = TTextAlign.Center);
635
begin
636
end;
637

638
procedure TCustomNativeCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single);
639
begin
640
  DrawLine(APt1, APt2, AOpacity, FCanvas.Stroke);
641
end;
642

643
procedure TCustomNativeCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const ACornerType: TCornerType; const Inside: Boolean);
644
var
645
  Path: TPathData;
646
  R: TRectF;
647
begin
648
  R := ARect;
649

650
  // 线在区内
651
  if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
652
    InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
653

654
  Path := TPathData.Create;
655
  try
656
    Path.AddRectangle(R, XRadius, YRadius, ACorners, ACornerType);
657
    DrawPath(Path, AOpacity, AFill, AStroke);
658
  finally
659
    Path.Free;
660
  end;
661
end;
662

663
procedure TCustomNativeCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False);
664
var
665
  Path: TPathData;
666
  R: TRectF;
667
begin
668
  R := ARect;
669

670
  // 线在区内
671
  if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
672
  begin
673
    R.Offset(-R.Left, -R.Top);
674
    InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
675
  end;
676

677
  Path := TPathData.Create;
678
  try
679
    Path.AddEllipse(R);
680
    DrawPath(Path, AOpacity, AFill, AStroke);
681
  finally
682
    Path.Free;
683
  end;
684
end;
685

686
procedure TCustomNativeCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False);
687
var
688
  R: TRectF;
689
  P: TPointF;
690
  Path: TPathData;
691
begin
692
  R := RectF(Center.X - Radius.X, Center.Y - Radius.Y, Center.X + Radius.X, Center.Y + Radius.Y);
693

694
  // 线在区内
695
  if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
696
  begin
697
    R.Offset(-R.Left, -R.Top);
698
    InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
699
  end;
700

701
  P := PointF(R.Width / 2, R.Height / 2);
702

703
  Path := TPathData.Create;
704
  try
705
    Path.AddArc(Center, P, StartAngle, SweepAngle);
706
    DrawPath(Path, AOpacity, AFill, AStroke);
707
  finally
708
    Path.Free;
709
  end;
710
end;
711

712
procedure TCustomNativeCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
713
var
714
  i: Integer;
715
  Path: TPathData;
716
  PathBreakFound: Boolean;
717
begin
718
  Path := TPathData.Create;
719

720
  try
721
    PathBreakFound := False;
722

723
    for i := 0 to High(Points) do
724
    begin
725
      if i = 0 then
726
      begin
727
        Path.MoveTo(Points[i]);
728
      end
729
      else if (Points[i].X = PolygonPointBreak.X) and (Points[i].Y = PolygonPointBreak.Y) then
730
      begin
731
        Path.ClosePath;
732
        PathBreakFound := True;
733
      end
734
      else
735
        Path.LineTo(Points[i]);
736
    end;
737

738
    if not PathBreakFound then
739
      Path.ClosePath;
740

741
    DrawPath(Path, AOpacity, AFill, AStroke);
742
  finally
743
    Path.Free;
744
  end;
745
end;
746

747
procedure TCustomNativeCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round);
748
begin
749
  DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, nil, FCanvas.Stroke, ACornerType);
750
end;
751

752
procedure TCustomNativeCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TStrokeBrush; const ACornerType: TCornerType = TCornerType.Round);
753
begin
754
  DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, nil, ABrush, ACornerType);
755
end;
756

757
procedure TCustomNativeCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round);
758
begin
759
  DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, FCanvas.Fill, nil, ACornerType);
760
end;
761

762
procedure TCustomNativeCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType = TCornerType.Round);
763
begin
764
  DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, ABrush, nil, ACornerType);
765
end;
766

767
procedure TCustomNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush);
768
begin
769
  DrawPath(APath, AOpacity, nil, ABrush);
770
end;
771

772
procedure TCustomNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single);
773
begin
774
  DrawPath(APath, AOpacity, nil, FCanvas.Stroke);
775
end;
776

777
procedure TCustomNativeCanvas.FillPath(const APath: TPathData; const AOpacity: Single);
778
begin
779
  DrawPath(APath, AOpacity, FCanvas.Fill, nil);
780
end;
781

782
procedure TCustomNativeCanvas.FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush);
783
begin
784
  DrawPath(APath, AOpacity, ABrush, nil);
785
end;
786

787
procedure TCustomNativeCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single);
788
begin
789
  DrawEllipse(ARect, AOpacity, FCanvas.Fill, nil);
790
end;
791

792
procedure TCustomNativeCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush);
793
begin
794
  DrawEllipse(ARect, AOpacity, ABrush, nil);
795
end;
796

797
procedure TCustomNativeCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single);
798
begin
799
  DrawEllipse(ARect, AOpacity, nil, FCanvas.Stroke);
800
end;
801

802
procedure TCustomNativeCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush);
803
begin
804
  DrawEllipse(ARect, AOpacity, nil, ABrush);
805
end;
806

807
procedure TCustomNativeCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
808
begin
809
  DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, FCanvas.Fill, nil);
810
end;
811

812
procedure TCustomNativeCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush);
813
begin
814
  DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, ABrush, nil);
815
end;
816

817
procedure TCustomNativeCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
818
begin
819
  DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, nil, FCanvas.Stroke);
820
end;
821

822
procedure TCustomNativeCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush);
823
begin
824
  DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, nil, ABrush);
825
end;
826

827
procedure TCustomNativeCanvas.FillPolygon(const Points: TPolygon; const AOpacity: Single);
828
begin
829
  DrawPolygon(Points, AOpacity, FCanvas.Fill, nil);
830
end;
831

832
procedure TCustomNativeCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single);
833
begin
834
  DrawPolygon(Points, AOpacity, nil, FCanvas.Stroke);
835
end;
836

837
{$ENDIF}
838
{$IFDEF ANDROID}
839

840
function JBitmapToBitmap(const AImage: JBitmap): TBitmap;
841
var
842
  Surface: TBitmapSurface;
843
begin
844
  Surface := TBitmapSurface.Create;
845
  Result := nil;
846
  try
847
    if JBitmapToSurface(AImage, Surface) then
848
    begin
849
      Result := TBitmap.Create;
850
      Result.Assign(Surface);
851
    end;
852
  finally
853
    Surface.Free;
854
  end;
855
end;
856

857
function BitmapToJBitmap(const ABitmap: TBitmap): JBitmap;
858
var
859
  BitmapSurface: TBitmapSurface;
860
begin
861
  Assert(ABitmap <> nil);
862

863
  Result := TJBitmap.JavaClass.createBitmap(ABitmap.Width, ABitmap.Height, TJBitmap_Config.JavaClass.ARGB_8888);
864
  BitmapSurface := TBitmapSurface.Create;
865
  try
866
    BitmapSurface.Assign(ABitmap);
867
    if not SurfaceToJBitmap(BitmapSurface, Result) then
868
      Result := nil;
869
  finally
870
    BitmapSurface.Free;
871
  end;
872
end;
873

874
procedure TAndroidNativeCanvas.ApplyFill(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
875
begin
876
  if (ABrush.Kind = TBrushKind.Resource) and (ABrush.Resource <> nil) and (ABrush.Resource.Brush <> nil) then
877
    ABrush.Assign(ABrush.Resource.Brush);
878

879
  Paint1.setStyle(TJPaint_Style.Wrap(TJPaint_Style.JavaClass.Fill));
880

881
  case ABrush.Kind of
882
    TBrushKind.Solid:
883
      Paint1.setARGB(TColorRec(ABrush.Color).A, TColorRec(ABrush.Color).B, TColorRec(ABrush.Color).G, TColorRec(ABrush.Color).R);
884
    TBrushKind.Gradient:
885
      ApplyGradient(Paint1, ABrush, ARect);
886
  else
887
    Paint1.setARGB(0, 255, 255, 255);
888
  end;
889
end;
890

891
procedure TAndroidNativeCanvas.ApplyGradient(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF);
892
var
893
  i: Integer;
894
  aColors: TJavaArray<Integer>;
895
  aStops: TJavaArray<Single>;
896
  aLinearShader: JLinearGradient;
897
  aRadialShader: JRadialGradient;
898
begin
899
  aColors := TJavaArray<Integer>.Create(ABrush.Gradient.Points.Count);
900
  aStops := TJavaArray<Single>.Create(ABrush.Gradient.Points.Count);
901

902
  for i := 0 to ABrush.Gradient.Points.Count - 1 do
903
  begin
904
    aColors[ABrush.Gradient.Points.Count - 1 - i] := Integer(ABrush.Gradient.Points[i].Color);
905
    aStops[ABrush.Gradient.Points.Count - 1 - i] := 1 - ABrush.Gradient.Points[i].Offset;
906
  end;
907

908
  case ABrush.Gradient.Style of
909
    // 线渐层
910
    TGradientStyle.Linear:
911
      begin
912
        aLinearShader := TJLinearGradient.JavaClass.init(ARect.Left + ABrush.Gradient.StopPosition.X * ARect.Width, ARect.Top + ABrush.Gradient.StopPosition.Y * ARect.Height, ARect.Left + ABrush.Gradient.StartPosition.X * ARect.Width, ARect.Top + ABrush.Gradient.StartPosition.Y * ARect.Height, aColors, aStops, TJShader_TileMode.JavaClass.CLAMP);
913
        Paint1.setShader(aLinearShader);
914
        aLinearShader := nil;
915
      end;
916
    // 圆渐层
917
    TGradientStyle.Radial:
918
      begin
919
        aRadialShader := TJRadialGradient.JavaClass.init(ARect.CenterPoint.X, ARect.CenterPoint.Y, ARect.Width / 2, aColors, aStops, TJShader_TileMode.JavaClass.CLAMP);
920
        Paint1.setShader(aRadialShader);
921
        aRadialShader := nil;
922
      end;
923
  else
924
    Paint1.setShader(nil);
925
  end;
926

927
  FreeAndNil(aColors);
928
  FreeAndNil(aStops);
929
end;
930

931
procedure TAndroidNativeCanvas.ApplyStroke(const Paint1: JPaint; const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
932
var
933
  i: Integer;
934
  Dash: TJavaArray<Single>;
935
begin
936
  if (AStroke.Kind = TBrushKind.Resource) and (AStroke.Resource <> nil) and (AStroke.Resource.Brush <> nil) then
937
    AStroke.Assign(AStroke.Resource.Brush);
938

939
  Paint1.setStyle(TJPaint_Style.Wrap(TJPaint_Style.JavaClass.Stroke));
940

941
  // Thickness = 0 还是有线
942
  if AStroke.Thickness > 0 then
943
  begin
944
    Paint1.setStrokeWidth(AStroke.Thickness);
945

946
    case AStroke.Kind of
947
      TBrushKind.Solid, TBrushKind.Bitmap:
948
        Paint1.setARGB(TColorRec(AStroke.Color).A, TColorRec(AStroke.Color).B, TColorRec(AStroke.Color).G, TColorRec(AStroke.Color).R);
949
      TBrushKind.Gradient:
950
        ApplyGradient(Paint1, AStroke, ARect);
951
    else
952
      Paint1.setARGB(0, 0, 0, 0);
953
    end;
954

955
    case AStroke.Cap of
956
      TStrokeCap.Flat:
957
        Paint1.setStrokeCap(TJPaint_Cap.JavaClass.BUTT);
958
      TStrokeCap.Round:
959
        Paint1.setStrokeCap(TJPaint_Cap.JavaClass.Round);
960
    end;
961

962
    if Length(AStroke.DashArray) > 0 then
963
    begin
964
      Dash := TJavaArray<Single>.Create(Length(AStroke.DashArray));
965

966
      for i := 0 to High(AStroke.DashArray) do
967
      begin
968
        Dash[i] := AStroke.DashArray[i] * AStroke.Thickness;
969
        if AStroke.Cap = TStrokeCap.Round then
970
        begin
971
          if Odd(i) then
972
            Dash[i] := (AStroke.DashArray[i] + 0.9) * AStroke.Thickness
973
          else
974
            Dash[i] := (AStroke.DashArray[i] - 0.9) * AStroke.Thickness;
975
        end;
976
      end;
977

978
      Paint1.setPathEffect(TJDashPathEffect.JavaClass.init(Dash, 0));
979
    end;
980

981
    case AStroke.Join of
982
      TStrokeJoin.Miter:
983
        Paint1.setStrokeJoin(TJPaint_Join.JavaClass.Miter);
984
      TStrokeJoin.Round:
985
        Paint1.setStrokeJoin(TJPaint_Join.JavaClass.Round);
986
      TStrokeJoin.Bevel:
987
        Paint1.setStrokeJoin(TJPaint_Join.JavaClass.Bevel);
988
    end;
989
  end
990
  else
991
    Paint1.setARGB(0, 0, 0, 0);
992
end;
993

994
procedure TAndroidNativeCanvas.DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean);
995
var
996
  Paint1: JPaint;
997
  jb: JBitmap;
998
  src: JRect;
999
  dst: JRectF;
1000
begin
1001
  if GlobalCanvas = nil then
1002
    Exit;
1003

1004
  Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1005
  Paint1.setAlpha(Round(AOpacity * 255));
1006
  Paint1.setAntiAlias(true);
1007

1008
  jb := BitmapToJBitmap(ABitmap);
1009
  src := TJRect.JavaClass.init;
1010
  src.&set(Round(SrcRect.Left), Round(SrcRect.Top), Round(SrcRect.Right), Round(SrcRect.Bottom));
1011
  dst := TJRectF.JavaClass.init;
1012
  dst.&set(DstRect.Left, DstRect.Top, DstRect.Right, DstRect.Bottom);
1013
  GlobalCanvas.DrawBitmap(jb, src, dst, Paint1);
1014
end;
1015

1016
procedure TAndroidNativeCanvas.DrawFill(const Paint1: JPaint; const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
1017
begin
1018
  if (ABrush.Kind = TBrushKind.Resource) and (ABrush.Resource <> nil) and (ABrush.Resource.Brush <> nil) then
1019
    ABrush.Assign(ABrush.Resource.Brush);
1020

1021
  if ABrush.Kind = TBrushKind.Bitmap then
1022
  begin
1023
    // 未完成
1024
  end;
1025
end;
1026

1027
procedure TAndroidNativeCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush);
1028
var
1029
  Paint1: JPaint;
1030
begin
1031
  if GlobalCanvas = nil then
1032
    Exit;
1033

1034
  if ABrush.Kind <> TBrushKind.None then
1035
  begin
1036
    Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1037
    ApplyStroke(Paint1, ABrush, TRectF.Create(APt1.X, APt1.Y, APt2.X, APt2.Y), AOpacity);
1038
    GlobalCanvas.DrawLine(APt1.X, APt1.Y, APt2.X, APt2.Y, Paint1);
1039
  end;
1040
end;
1041

1042
procedure TAndroidNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
1043
var
1044
  i: Integer;
1045
  Path1: JPath;
1046
  Paint1: JPaint;
1047
  SrcRect: TRectF;
1048
begin
1049
  Log.d('Enter TAndroidNativeCanvas.DrawPath');
1050
  if GlobalCanvas = nil then
1051
    Exit;
1052

1053
  SrcRect := APath.GetBounds;
1054
  Path1 := TJPath.Wrap(TJPath.JavaClass.init);
1055

1056
  i := 0;
1057
  while i < APath.Count do
1058
  begin
1059
    case APath.Points[i].Kind of
1060
      // 移到
1061
      TPathPointKind.MoveTo:
1062
        Path1.MoveTo(APath.Points[i].Point.X, APath.Points[i].Point.Y);
1063
      // 线到
1064
      TPathPointKind.LineTo:
1065
        Path1.LineTo(APath.Points[i].Point.X, APath.Points[i].Point.Y);
1066
      // 曲线
1067
      TPathPointKind.CurveTo:
1068
        begin
1069
          Path1.cubicTo(APath.Points[i].Point.X, APath.Points[i].Point.Y, APath.Points[i + 1].Point.X, APath.Points[i + 1].Point.Y, APath.Points[i + 2].Point.X, APath.Points[i + 2].Point.Y);
1070
          Inc(i, 2);
1071
        end;
1072
      // 关闭
1073
      TPathPointKind.Close:
1074
        Path1.Close;
1075
    end;
1076
    Inc(i);
1077
  end;
1078

1079
  GlobalCanvas.save;
1080
  Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1081

1082
  if (AFill <> nil) and (AFill.Kind <> TBrushKind.None) then
1083
  begin
1084
    if AFill.Kind = TBrushKind.Bitmap then
1085
    begin
1086
      GlobalCanvas.DrawPath(Path1, Paint1);
1087
      DrawFill(Paint1, AFill, SrcRect, SrcRect, AOpacity);
1088
    end
1089
    else
1090
    begin
1091
      Path1.setFillType(TJPath_FillType.Wrap(TJPath_FillType.JavaClass.EVEN_ODD));
1092
      ApplyFill(Paint1, AFill, SrcRect, AOpacity);
1093
      GlobalCanvas.DrawPath(Path1, Paint1);
1094
    end;
1095
  end;
1096

1097
  if (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
1098
  begin
1099
    ApplyStroke(Paint1, AStroke, SrcRect, AOpacity);
1100
    GlobalCanvas.DrawPath(Path1, Paint1);
1101
  end;
1102

1103
  GlobalCanvas.restore;
1104
end;
1105

1106
procedure TAndroidNativeCanvas.IntersectClipRect(const ARect: TRectF);
1107
var
1108
  JR: JRectF;
1109
begin
1110
  if GlobalCanvas <> nil then
1111
  begin
1112
    JR := TJRectF.JavaClass.init(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
1113

1114
    GlobalCanvas.clipRect(JR, TJRegion_Op.JavaClass.INTERSECT);
1115
  end;
1116
end;
1117

1118
procedure TAndroidNativeCanvas.ExcludeClipRect(const ARect: TRectF);
1119
var
1120
  JR: JRectF;
1121
begin
1122
  if GlobalCanvas <> nil then
1123
  begin
1124
    JR := TJRectF.JavaClass.init(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
1125

1126
    GlobalCanvas.clipRect(JR, TJRegion_Op.JavaClass.REPLACE);
1127
  end;
1128
end;
1129

1130
procedure TAndroidNativeCanvas.FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign, AVTextAlign: TTextAlign);
1131
var
1132
  js: JString;
1133
  Paint1: JPaint;
1134
  tr: JRect;
1135
  x, y: Single;
1136
  align: JPaint_Align;
1137
  fm: JPaint_FontMetricsInt;
1138
begin
1139
  if GlobalCanvas = nil then
1140
    Exit;
1141
  js := StringToJString(AText);
1142
  Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1143
  Paint1.setTextSize(Font.Size);
1144
  if (Fill <> nil) and (Fill.Kind <> TBrushKind.None) then
1145
  begin
1146
    ApplyFill(Paint1, Fill, ARect, AOpacity);
1147
  end;
1148
  align := TJPaint_Align.Wrap(TJPaint_Align.JavaClass.LEFT);
1149
  Paint1.setTextAlign(align);
1150
  Paint1.setStrikeThruText(TFontStyle.fsStrikeOut in Font.Style);
1151
  Paint1.setUnderlineText(TFontStyle.fsUnderline in Font.Style);
1152
  Paint1.setFakeBoldText(TFontStyle.fsBold in Font.Style);
1153
  if TFontStyle.fsItalic in Font.Style then
1154
    Paint1.setTextSkewX(-0.5);
1155
  tr := TJRect.JavaClass.init;
1156
  Paint1.getTextBounds(js, 0, js.length, tr);
1157
  case ATextAlign of
1158
    TTextAlign.Center: x := (ARect.Width - tr.width) / 2;
1159
    TTextAlign.Leading: x := 0;
1160
    TTextAlign.Trailing: x := ARect.Width - tr.width - tr.left;
1161
  end;
1162

1163
  fm := Paint1.getFontMetricsInt;
1164
  case AVTextAlign of
1165
    TTextAlign.Center: y := (ARect.Height - tr.top) / 2;
1166
    TTextAlign.Leading: y := -fm.top;
1167
    TTextAlign.Trailing: y := ARect.Height - fm.bottom;
1168
  end;
1169
  x := x + ARect.Left;
1170
  y := y + ARect.Top;
1171
  GlobalCanvas.drawText(js, x, y, Paint1);
1172
  Log.d('ARect: [%f, %f, %f, %f], tr: [x:%d, y:%d, w:%d, h:%d], x: %f, y: %f, fm.top: %d, fm.leading: %d, fm.bottom: %d, s: %s',
1173
    [ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, tr.left, tr.top, tr.width, tr.height, x, y, fm.top, fm.leading, fm.bottom, AText]);
1174
end;
1175

1176
procedure TAndroidNativeCanvas.NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc);
1177
var
1178
  Bitmap1: JBitmap;
1179
  Paint: JPaint;
1180
  Bitmap: TBitmap;
1181
  ScreenService: IFMXScreenService;
1182
  Scale1: Single;
1183
begin
1184
  Log.d('Enter TAndroidNativeCanvas.NativeDraw');
1185
  if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService, ScreenService) then
1186
    Scale1 := ScreenService.GetScreenScale
1187
  else
1188
    Scale1 := 1;
1189

1190
  Bitmap1 := TJBitmap.JavaClass.createBitmap(Ceil(ARect.Width * Scale1), Ceil(ARect.Height * Scale1), TJBitmap_Config.JavaClass.ARGB_8888);
1191
  GlobalCanvas := TJCanvas.JavaClass.init(Bitmap1);
1192
  GlobalCanvas.save;
1193
  GlobalCanvas.scale(Scale1, Scale1);
1194

1195
  // 透明底色
1196
  Paint := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1197
  Paint.setStyle(TJPaint_Style.Wrap(TJPaint_Style.JavaClass.Fill));
1198
  Paint.setARGB(0, 255, 255, 255);
1199
  GlobalCanvas.DrawRect(GlobalCanvas.getClipBounds, Paint);
1200

1201
  // 绘图函数
1202
  if Assigned(ADrawProc) then
1203
    ADrawProc;
1204

1205
  GlobalCanvas.restore;
1206

1207
  // 显示
1208
  Bitmap := JBitmapToBitmap(Bitmap1);
1209
  FCanvas.DrawBitmap(Bitmap, RectF(0, 0, Bitmap.Width, Bitmap.Height), ARect, 1);
1210
  FreeAndNil(Bitmap);
1211
end;
1212

1213
{$ENDIF}
1214
{$IFDEF IOS}
1215

1216
function PointToCGPoint(const P: TPointF): CGPoint;
1217
begin
1218
  Result := CGPointMake(P.X, P.Y);
1219
end;
1220

1221
{ TIOSNativeCanvas }
1222

1223
procedure TIOSNativeCanvas.ApplyFill(const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
1224
var
1225
  LColor: TAlphaColorF;
1226
begin
1227
  if GlobalCanvas = nil then
1228
    Exit;
1229

1230
  if (ABrush.Kind = TBrushKind.Resource) and (ABrush.Resource <> nil) and (ABrush.Resource.Brush <> nil) then
1231
    ABrush.Assign(ABrush.Resource.Brush);
1232

1233
  case ABrush.Kind of
1234
    TBrushKind.Solid:
1235
      begin
1236
        LColor := TAlphaColorF.Create(MakeColor(ABrush.Color, AOpacity));
1237
        CGContextSetRGBFillColor(GlobalCanvas, LColor.R, LColor.G, LColor.B, LColor.A);
1238
      end;
1239
  else
1240
    CGContextSetRGBFillColor(GlobalCanvas, 0, 0, 0, 0);
1241
  end;
1242

1243
  // 渐层
1244
  if (ABrush.Kind = TBrushKind.Gradient) and (CGContextIsPathEmpty(GlobalCanvas) = 0) then
1245
  begin
1246
    CGContextClip(GlobalCanvas);
1247
    ApplyGradient(ABrush, ARect);
1248
  end;
1249
end;
1250

1251
procedure TIOSNativeCanvas.ApplyGradient(const ABrush: TBrush; const ARect: TRectF);
1252
var
1253
  i: Integer;
1254
  Locations: TArray<CGFloat>;
1255
  colorSpace: CGColorSpaceRef;
1256
  Gradient: CGGradientRef;
1257
  colors: NSMutableArray;
1258
  RCenter: CGPoint;
1259
begin
1260
  if GlobalCanvas = nil then
1261
    Exit;
1262

1263
  SetLength(Locations, ABrush.Gradient.Points.Count);
1264
  colors := TNSMutableArray.Wrap(TNSMutableArray.OCClass.arrayWithCapacity(ABrush.Gradient.Points.Count));
1265

1266
  for i := 0 to ABrush.Gradient.Points.Count - 1 do
1267
  begin
1268
    colors.addObject(AlphaColorToUIColor(ABrush.Gradient.Points[i].Color).CGColor);
1269
    Locations[i] := ABrush.Gradient.Points[i].Offset;
1270
  end;
1271

1272
  colorSpace := CGColorSpaceCreateDeviceRGB;
1273
  Gradient := CGGradientCreateWithColors(colorSpace, (colors as ILocalObject).GetObjectID, @Locations[0]);
1274

1275
  case ABrush.Gradient.Style of
1276
    // 线渐层
1277
    TGradientStyle.Linear:
1278
      begin
1279
        CGContextDrawLinearGradient(GlobalCanvas, Gradient, CGPointMake(ARect.Left + ABrush.Gradient.StartPosition.X * ARect.Width, ARect.Top + ABrush.Gradient.StartPosition.Y * ARect.Height), CGPointMake(ARect.Left + ABrush.Gradient.StopPosition.X * ARect.Width, ARect.Top + ABrush.Gradient.StopPosition.Y * ARect.Height), 0);
1280
      end;
1281
    // 圆渐层
1282
    TGradientStyle.Radial:
1283
      begin
1284
        RCenter.Create(PointF(ABrush.Gradient.RadialTransform.RotationCenter.X * ARect.Width, ABrush.Gradient.RadialTransform.RotationCenter.Y * ARect.Height) + ARect.TopLeft);
1285
        CGContextDrawRadialGradient(GlobalCanvas, Gradient, RCenter, ARect.Width / 2, RCenter, 0, kCGGradientDrawsBeforeStartLocation or kCGGradientDrawsAfterEndLocation);
1286
      end;
1287
  end;
1288

1289
  CFRelease(colorSpace);
1290
  CFRelease(Gradient);
1291
end;
1292

1293
procedure TIOSNativeCanvas.ApplyStroke(const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
1294
var
1295
  Dash: TDashArray;
1296
  i: Integer;
1297
  LColor: TAlphaColorF;
1298
  R: TRectF;
1299
  StrokeDash: FMX.Graphics.TDashArray;
1300
begin
1301
  if GlobalCanvas = nil then
1302
    Exit;
1303

1304
  if (AStroke.Kind = TBrushKind.Resource) and (AStroke.Resource <> nil) and (AStroke.Resource.Brush <> nil) then
1305
    AStroke.Assign(AStroke.Resource.Brush);
1306

1307
  case AStroke.Kind of
1308
    TBrushKind.Solid, TBrushKind.Bitmap:
1309
      begin
1310
        LColor := TAlphaColorF.Create(MakeColor(AStroke.Color, AOpacity));
1311
        CGContextSetRGBStrokeColor(GlobalCanvas, LColor.R, LColor.G, LColor.B, LColor.A);
1312
      end;
1313
  else
1314
    CGContextSetRGBStrokeColor(GlobalCanvas, 0, 0, 0, 0);
1315
  end;
1316

1317
  case AStroke.Cap of
1318
    TStrokeCap.Flat:
1319
      CGContextSetLineCap(GlobalCanvas, kCGLineCapButt);
1320
    TStrokeCap.Round:
1321
      CGContextSetLineCap(GlobalCanvas, kCGLineCapRound);
1322
  end;
1323

1324
  if Length(AStroke.DashArray) > 0 then
1325
  begin
1326
    // select the proper dash array for the printer
1327
    if TMyCanvas(FCanvas).FPrinter <> nil then
1328
    begin
1329
      if AStroke.Dash <> TStrokeDash.Custom then
1330
        StrokeDash := TStrokeBrush.StdDash[TStrokeBrush.TDashDevice.Printer, AStroke.Dash].DashArray
1331
      else
1332
        StrokeDash := AStroke.DashArray;
1333
      SetLength(Dash, Length(StrokeDash));
1334
      for I := 0 to High(StrokeDash) do
1335
      begin
1336
        Dash[I] := StrokeDash[I];
1337
      end;
1338
    end
1339
    else // adjust the line dashes for the screen
1340
    begin
1341
      SetLength(Dash, Length(AStroke.DashArray));
1342
      for i := 0 to High(AStroke.DashArray) do
1343
      begin
1344
        Dash[i] := AStroke.DashArray[i] * AStroke.Thickness;
1345
        if AStroke.Cap = TStrokeCap.Round then
1346
        begin
1347
          if Odd(i) then
1348
            Dash[i] := (AStroke.DashArray[i] + 1) * AStroke.Thickness
1349
          else
1350
            Dash[i] := (AStroke.DashArray[i] - 1) * AStroke.Thickness;
1351
        end;
1352
      end;
1353
    end;
1354
    CGContextSetLineDash(GlobalCanvas, AStroke.DashOffset, @Dash[0], Length(AStroke.DashArray));
1355
  end
1356
  else
1357
    CGContextSetLineDash(GlobalCanvas, 0, nil, 0);
1358

1359
  case AStroke.Join of
1360
    TStrokeJoin.Miter:
1361
      CGContextSetLineJoin(GlobalCanvas, kCGLineJoinMiter);
1362

1363
    TStrokeJoin.Round:
1364
      CGContextSetLineJoin(GlobalCanvas, kCGLineJoinRound);
1365

1366
    TStrokeJoin.Bevel:
1367
      CGContextSetLineJoin(GlobalCanvas, kCGLineJoinBevel);
1368
  end;
1369

1370
  CGContextSetLineWidth(GlobalCanvas, AStroke.Thickness);
1371

1372
  // 渐层
1373
  if (AStroke.Kind = TBrushKind.Gradient) and (CGContextIsPathEmpty(GlobalCanvas) = 0) then
1374
  begin
1375
    CGContextReplacePathWithStrokedPath(GlobalCanvas);
1376
    CGContextClip(GlobalCanvas);
1377
    R := ARect;
1378
    InflateRect(R, AStroke.Thickness / 2, AStroke.Thickness / 2);
1379
    ApplyGradient(AStroke, R);
1380
  end;
1381
end;
1382

1383
constructor TIOSNativeCanvas.Create(ACanvas: TCanvas);
1384
begin
1385
  inherited;
1386
  FCanvas.Font.Family := NSStrToStr(iOSapi.Foundation.NSString(TUIFont.Wrap(TUIFont.OCClass.systemFontOfSize(14)).fontName));
1387
end;
1388

1389
procedure TIOSNativeCanvas.DrawBitmap(const ABitmap: TBitmap; const SrcRect,
1390
  DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean);
1391
var
1392
  NativeImage: UIImage;
1393
  image: CGImageRef;
1394
begin
1395
  if GlobalCanvas = nil then
1396
    Exit;
1397

1398
  NativeImage := BitmapToUIImage(ABitmap);
1399
  image := CGImageCreateWithImageInRect(NativeImage.CGImage,
1400
    CGRectMake(SrcRect.Left, SrcRect.Top, SrcRect.Width, SrcRect.Height));
1401
  CGContextSaveGState(GlobalCanvas);
1402
  CGContextSetAlpha(GlobalCanvas, AOpacity);
1403
  CGContextTranslateCTM(GlobalCanvas, 0, height);
1404
  CGContextScaleCTM(GlobalCanvas, 1.0, -1.0);
1405
  CGContextDrawImage(GlobalCanvas,
1406
    CGRectMake(DstRect.Left, Height - DstRect.Bottom, DstRect.Width, DstRect.Height),
1407
    image);
1408
  CGContextRestoreGState(GlobalCanvas);
1409
end;
1410

1411
procedure TIOSNativeCanvas.SetMatrix(const M: TMatrix);
1412
var
1413
  LMatrix: TMatrix;
1414
  Transform: CGAffineTransform;
1415
begin
1416
  if GlobalCanvas <> nil then
1417
  begin
1418
    Transform := CGContextGetCTM(GlobalCanvas);
1419
    LMatrix := TMatrix.Identity;
1420
    LMatrix.m11 := Transform.a;
1421
    LMatrix.m12 := Transform.b;
1422
    LMatrix.m21 := Transform.c;
1423
    LMatrix.m22 := Transform.d;
1424
    LMatrix.m31 := Transform.tx;
1425
    LMatrix.m32 := Transform.ty;
1426
    LMatrix := LMatrix.Inverse;
1427

1428
    CGContextConcatCTM(GlobalCanvas, CGAffineTransformMake(LMatrix.m11, LMatrix.m12, LMatrix.m21, LMatrix.m22, LMatrix.m31,
1429
      LMatrix.m32));
1430
//    AdaptCoordinateSystem;
1431
    CGContextConcatCTM(GlobalCanvas, CGAffineTransformMake(M.m11, M.m12, M.m21, M.m22, M.m31, M.m32));
1432
  end;
1433
end;
1434

1435
procedure TIOSNativeCanvas.DrawFill(const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
1436
begin
1437
  if ABrush.Kind = TBrushKind.Bitmap then
1438
  begin
1439
    // 未完成
1440
  end
1441
  else
1442
  begin
1443
    ApplyFill(ABrush, DesRect, AOpacity);
1444
    CGContextEOFillPath(GlobalCanvas);
1445
  end;
1446
end;
1447

1448
procedure TIOSNativeCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush);
1449
var
1450
  R: TRectF;
1451
begin
1452
  if GlobalCanvas = nil then
1453
    Exit;
1454

1455
  if ABrush.Kind <> TBrushKind.None then
1456
  begin
1457
    CGContextSaveGState(GlobalCanvas);
1458
    CGContextBeginPath(GlobalCanvas);
1459

1460
    CGContextMoveToPoint(GlobalCanvas, APt1.X, APt1.Y);
1461
    CGContextAddLineToPoint(GlobalCanvas, APt2.X, APt2.Y);
1462

1463
    // 加上线粗
1464
    R := TRectF.Create(APt1.X, APt1.Y, APt2.X, APt2.Y);
1465
    InflateRect(R, ABrush.Thickness / 2, ABrush.Thickness / 2);
1466

1467
    ApplyStroke(ABrush, R, AOpacity);
1468
    CGContextStrokePath(GlobalCanvas);
1469

1470
    CGContextRestoreGState(GlobalCanvas);
1471
  end;
1472
end;
1473

1474
procedure TIOSNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
1475
  procedure ShowPath;
1476
  var
1477
    i: Integer;
1478
    CurvePoint1, CurvePoint2: TPointF;
1479
  begin
1480
    i := 0;
1481
    while i < APath.Count do
1482
    begin
1483
      case APath[i].Kind of
1484
        TPathPointKind.MoveTo:
1485
          CGContextMoveToPoint(GlobalCanvas, APath[i].Point.X, APath[i].Point.Y);
1486
        TPathPointKind.LineTo:
1487
          CGContextAddLineToPoint(GlobalCanvas, APath[i].Point.X, APath[i].Point.Y);
1488
        TPathPointKind.CurveTo:
1489
          begin
1490
            CurvePoint1 := APath[i].Point;
1491
            Inc(i);
1492
            CurvePoint2 := APath[i].Point;
1493
            Inc(i);
1494
            CGContextAddCurveToPoint(GlobalCanvas, CurvePoint1.X, CurvePoint1.Y, CurvePoint2.X, CurvePoint2.Y, APath[i].Point.X, APath[i].Point.Y);
1495
          end;
1496
        TPathPointKind.Close:
1497
          CGContextClosePath(GlobalCanvas);
1498
      end;
1499

1500
      Inc(i);
1501
    end;
1502
  end;
1503

1504
var
1505
  SrcRect: TRectF;
1506
begin
1507
  if GlobalCanvas = nil then
1508
    Exit;
1509

1510
  SrcRect := APath.GetBounds;
1511

1512
  // 涂色
1513
  if (AFill <> nil) and (AFill.Kind <> TBrushKind.None) then
1514
  begin
1515
    CGContextSaveGState(GlobalCanvas);
1516
    CGContextBeginPath(GlobalCanvas);
1517
    ShowPath;
1518
    DrawFill(AFill, SrcRect, SrcRect, AOpacity);
1519
    CGContextRestoreGState(GlobalCanvas);
1520
  end;
1521

1522
  // 画线
1523
  if (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
1524
  begin
1525
    CGContextSaveGState(GlobalCanvas);
1526
    CGContextBeginPath(GlobalCanvas);
1527
    ShowPath;
1528
    ApplyStroke(AStroke, SrcRect, AOpacity);
1529
    CGContextStrokePath(GlobalCanvas);
1530
    CGContextRestoreGState(GlobalCanvas);
1531
  end;
1532
end;
1533

1534
procedure TIOSNativeCanvas.ExcludeClipRect(const ARect: TRectF);
1535
var
1536
  LRect: array [0 .. 3] of CGRect;
1537
begin
1538
  if GlobalCanvas <> nil then
1539
  begin
1540
    LRect[0] := CGRectFromRect(TRectF.Create(-FCanvas.Width, -FCanvas.Width, ARect.Left, FCanvas.Height));
1541
    LRect[1] := CGRectFromRect(TRectF.Create(ARect.Right, -FCanvas.Height, FCanvas.Width, FCanvas.Height));
1542
    LRect[2] := CGRectFromRect(TRectF.Create(ARect.Left, -FCanvas.Height, ARect.Right, ARect.Top));
1543
    LRect[3] := CGRectFromRect(TRectF.Create(ARect.Left, ARect.Bottom, ARect.Right, FCanvas.Height));
1544
    CGContextClipToRects(GlobalCanvas, @LRect[0], 4);
1545
  end;
1546
end;
1547

1548
function NSSTR(Str: string): NSString;
1549
var
1550
  M: TMarshaller;
1551
begin
1552
  Result := TNSString.Wrap(TNSString.OCClass.stringWithUTF8String(M.AsAnsi(Str, CP_UTF8).ToPointer));
1553
end;
1554

1555
function TIOSNativeCanvas.GetPostScriptFontName: CFStringRef;
1556
var
1557
  LUIFont: UIFont;
1558
  LocalObject: ILocalObject;
1559
begin
1560
  Result := nil;
1561
  LUIFont := TUIFont.Wrap(TUIFont.OCClass.fontWithName(NSString(StrToNSStr(FCanvas.Font.Family)), FCanvas.Font.Size));
1562
  if Supports(LUIFont, ILocalObject, LocalObject) then
1563
    Result := CTFontCopyPostScriptName(LocalObject.GetObjectID);
1564
  if Result = nil then
1565
    //In case there is no direct name for the requested font returns source name and let CoreText to select appropriate font
1566
    Result := CFSTR(FCanvas.Font.Family);
1567
end;
1568

1569
procedure TIOSNativeCanvas.FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign, AVTextAlign: TTextAlign);
1570
const
1571
  //Rotating matrix to simulate Italic font attribute
1572
  ItalicMatrix: CGAffineTransform = (
1573
    a: 1;
1574
    b: 0;
1575
    c: 0.176326981; //~tan(10 degrees)
1576
    d: 1;
1577
    tx: 0;
1578
    ty: 0
1579
  );
1580
var
1581
  NS: NSString;
1582
  dic: NSMutableDictionary;
1583
  ps: NSMutableParagraphStyle;
1584
  f: UIFont;
1585
  sz: CGSize;
1586
  tr: NSRect;
1587
  font, NewFontRef: CTFontRef;
1588
  ftName: NSString;
1589
  desc: UIFontDescriptor;
1590
begin
1591
  if GlobalCanvas = nil then
1592
    Exit;
1593

1594
  font := CTFontCreateWithName(GetPostScriptFontName, FCanvas.Font.Size, nil);
1595
  try
1596
  //以下方法仅对英文有效,因此屏蔽
1597
//    if TFontStyle.fsItalic in FCanvas.Font.Style then
1598
//    begin
1599
//      NewFontRef := CTFontCreateCopyWithSymbolicTraits(font, 0, nil,
1600
//        kCTFontItalicTrait, kCTFontItalicTrait);
1601
//      if NewFontRef <> nil then
1602
//      begin
1603
//        CFRelease(font);
1604
//        font := NewFontRef;
1605
//      end;
1606
//    end;
1607
    if TFontStyle.fsBold in FCanvas.Font.Style then
1608
    begin
1609
      NewFontRef := CTFontCreateCopyWithSymbolicTraits(font, FCanvas.Font.Size, nil, kCTFontBoldTrait, kCTFontBoldTrait);
1610
      if NewFontRef <> nil then
1611
      begin
1612
        CFRelease(font);
1613
        font := NewFontRef;
1614
      end;
1615
    end;
1616
    ftName := TNSString.wrap(CTFontCopyPostScriptName(font));
1617
  except
1618
    CFRelease(font);
1619
    ftName := NSStr(FCanvas.Font.Family);
1620
  end;
1621

1622
  NS := NSSTR(AText);
1623

1624
  dic := TNSMutableDictionary.Wrap(TNSMutableDictionary.Wrap(TNSMutableDictionary.OCClass.alloc).init);
1625
  if TFontStyle.fsItalic in FCanvas.Font.Style then
1626
  begin
1627
    desc := TUIFontDescriptor.OCClass.fontDescriptorWithNameMatrix(iOSapi.Foundation.NSString(ftName), ItalicMatrix);
1628
    F := TUIFont.Wrap((TUIFont.OCClass.fontWithDescriptor(desc, FCanvas.Font.Size)));
1629
  end
1630
  else
1631
    f := TUIFont.Wrap(TUIFont.OCClass.fontWithName(ftName, FCanvas.Font.Size));
1632

1633
  ps := TNSMutableParagraphStyle.Wrap(TNSMutableParagraphStyle.Wrap(TNSMutableParagraphStyle.OCClass.alloc).init);
1634
  case ATextAlign of
1635
    TTextAlign.Center: ps.setAlignment(UITextAlignmentCenter);
1636
    TTextAlign.Leading: ps.setAlignment(UITextAlignmentLeft);
1637
    TTextAlign.Trailing: ps.setAlignment(UITextAlignmentRight);
1638
  end;
1639
  ps.setLineBreakMode(NSLineBreakByTruncatingTail);
1640

1641
  dic.setValue((f as ILocalObject).GetObjectID, NSFontAttributeName);
1642
  dic.setValue((AlphaColorToUIColor(FCanvas.Fill.Color) as ILocalObject).GetObjectID, NSForegroundColorAttributeName);
1643
  dic.setValue((ps as ILocalObject).GetObjectID, NSParagraphStyleAttributeName);
1644
  if (TFontStyle.fsUnderline in FCanvas.Font.Style) or (TFontStyle.fsStrikeOut in FCanvas.Font.Style) then
1645
  begin
1646
    if TFontStyle.fsUnderline in FCanvas.Font.Style then
1647
    begin
1648
      dic.setValue(TNSNumber.OCClass.numberWithInt(NSUnderlineStyleSingle), NSUnderlineStyleAttributeName);
1649
    end;
1650
    if TFontStyle.fsStrikeOut in FCanvas.Font.Style then
1651
    begin
1652
      dic.setValue(TNSNumber.OCClass.numberWithInt(NSUnderlineStyleSingle), NSStrikethroughStyleAttributeName);
1653
    end;
1654
    dic.setValue(TNSNumber.OCClass.numberWithInt(0), NSBaselineOffsetAttributeName);
1655
  end;
1656

1657
  sz := NS.sizeWithAttributes(dic);
1658

1659
  case AVTextAlign of
1660
    TTextAlign.Center:
1661
      tr := NSRect.Create(ARect.Left, ARect.Top + (ARect.Height - sz.height)/ 2, ARect.Width, (ARect.Height - sz.height) / 2);
1662
    TTextAlign.Leading:
1663
      tr := NSRect.Create(ARect);
1664
    TTextAlign.Trailing:
1665
      tr := NSRect.Create(ARect.Left, ARect.Bottom - sz.height, ARect.Width, sz.height);
1666
  end;
1667

1668
  NS.drawInRect(tr, dic);
1669
end;
1670

1671
procedure TIOSNativeCanvas.IntersectClipRect(const ARect: TRectF);
1672
begin
1673
  if GlobalCanvas <> nil then
1674
    CGContextClipToRect(GlobalCanvas, CGRectFromRect(ARect));
1675
end;
1676

1677
function MyUIImageToBitmap(const AImage: UIImage; const ARotate: Single; const AMaxSize: TSize): TBitmap;
1678

1679
  function ReduceImageSize(const AOriginalSize: TSize): TSize;
1680
  var
1681
    ImageRatio: Double;
1682
    ScaleCoef: Double;
1683
    MinWidth: Integer;
1684
    MinHeight: Integer;
1685
    MaxBitmapSize: Integer;
1686
  begin
1687
    Result := AOriginalSize;
1688
    MinWidth := Min(AOriginalSize.Width, AMaxSize.Width);
1689
    MinHeight := Min(AOriginalSize.Height, AMaxSize.Height);
1690
    ImageRatio := AOriginalSize.Width / AOriginalSize.Height;
1691
    if MinWidth / MinHeight < ImageRatio then
1692
      Result := TSize.Create(MinWidth, Round(MinWidth / ImageRatio))
1693
    else
1694
      Result := TSize.Create(Round(MinHeight * ImageRatio), MinHeight);
1695

1696
    MaxBitmapSize := TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize);
1697
    if (MaxBitmapSize > 0) and (Max(AOriginalSize.cx, AOriginalSize.cy) div MaxBitmapSize > 0) then
1698
    begin
1699
      ScaleCoef := Max(AOriginalSize.cx, AOriginalSize.cy) / MaxBitmapSize;
1700
      Result := TSize.Create(Round(AOriginalSize.cx / ScaleCoef), Round(AOriginalSize.cy / ScaleCoef));
1701
    end;
1702
  end;
1703

1704
var
1705
  ImageRef: CGImageRef;
1706
  Bitmap: TBitmap;
1707
  CtxRef: CGContextRef;
1708
  ColorSpace: CGColorSpaceRef;
1709
  Data: TBitmapData;
1710
  BitmapSize: TSize;
1711
begin
1712
  ImageRef := AImage.CGImage;
1713
  if ImageRef <> nil then
1714
  begin
1715
    BitmapSize := ReduceImageSize(TSize.Create(CGImageGetWidth(ImageRef), CGImageGetHeight(ImageRef)));
1716
    Bitmap := TBitmap.Create(BitmapSize.cx, BitmapSize.cy);
1717
    Bitmap.Clear(0);
1718
    ColorSpace := CGColorSpaceCreateDeviceRGB;
1719
    try
1720
      if Bitmap.Map(TMapAccess.Write, Data) then
1721
      try
1722
        CtxRef := CGBitmapContextCreate(Data.Data, Bitmap.Width, Bitmap.Height, 8, Data.Pitch, ColorSpace,
1723
          kCGImageAlphaPremultipliedLast or kCGBitmapByteOrder32Big);
1724
        try
1725
          CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, BitMap.Height), ImageRef);
1726
        finally
1727
          CGContextRelease(CtxRef);
1728
        end;
1729
      finally
1730
        Bitmap.Unmap(Data);
1731
      end;
1732
    finally
1733
      CGColorSpaceRelease(ColorSpace);
1734
    end;
1735
    Bitmap.Rotate(ARotate);
1736
    Result := Bitmap;
1737
  end
1738
  else
1739
    Result := nil;
1740
end;
1741

1742
procedure TIOSNativeCanvas.NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc);
1743
var
1744
  NativeImage: UIImage;
1745
  Bitmap: TBitmap;
1746
begin
1747
  NativeImage := nil;
1748

1749
  UIGraphicsBeginImageContextWithOptions(CGSizeMake(ARect.Width, ARect.Height), False, 0 { 目前机子Scale } );
1750

1751
  GlobalCanvas := UIGraphicsGetCurrentContext;
1752

1753
  CGContextSaveGState(GlobalCanvas);
1754

1755
  // 透明底色
1756
  TUIColor.Wrap(TUIColor.OCClass.colorWithHue(0, 0, 1, 0)).setFill;
1757
  CGContextFillRect(GlobalCanvas, CGContextGetClipBoundingBox(GlobalCanvas));
1758

1759
  // 绘图函数
1760
  if Assigned(ADrawProc) then
1761
    ADrawProc;
1762

1763
  CGContextRestoreGState(GlobalCanvas);
1764

1765
  NativeImage := TUIImage.Wrap(UIGraphicsGetImageFromCurrentImageContext);
1766

1767
  if Assigned(NativeImage) then
1768
  begin
1769
    Bitmap := MyUIImageToBitmap(NativeImage, 0, NativeImage.size.ToSizeF.Round);
1770
    // 显示
1771
    FCanvas.DrawBitmap(Bitmap, RectF(0, 0, Bitmap.Width, Bitmap.Height), ARect, 1);
1772
    FreeAndNil(Bitmap);
1773
  end;
1774

1775
  UIGraphicsEndImageContext;
1776
end;
1777

1778
{$ENDIF}
1779

1780
end.
1781

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.