BaiduFMX
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
35unit FMX.Graphics.NativeCanvas;
36
37interface
38
39uses
40System.Types,
41System.UITypes,
42System.UIConsts,
43System.Math,
44System.Math.Vectors,
45System.Classes,
46System.SysUtils,
47System.Generics.Collections,
48
49{$IFDEF ANDROID}
50Androidapi.JNI.JavaTypes,
51Androidapi.JNI.GraphicsContentViewText,
52Androidapi.JNIBridge,
53Androidapi.Helpers,
54Androidapi.Bitmap,
55Androidapi.JNI.App,
56FMX.Surfaces,
57FMX.Helpers.Android,
58{$ENDIF}
59{$IFDEF IOS}
60iOSapi.CocoaTypes,
61iOSapi.UIKit,
62iOSapi.Foundation,
63iOSapi.CoreText,
64iOSapi.CoreGraphics,
65Macapi.CoreFoundation,
66Macapi.ObjectiveC,
67Macapi.Helpers,
68FMX.FontGlyphs.iOS,
69FMX.Helpers.iOS,
70{$ENDIF}
71FMX.Types,
72FMX.Graphics,
73FMX.Platform,
74FMX.Graphics.INativeCanvas;
75
76type
77{$IFDEF IOS64}
78TDashArray = TArray<CGFloat>; // iOS 64 必需使用 Double 或 CGFloat 否則無法正常顯示虛線
79{$ENDIF}
80
81TFiremonkeyCanvas = class(TAbstractCanvas)
82public
83procedure NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc); override;
84// 涂色 + 线色一次完成
85procedure 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;
86procedure DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); overload; override;
87procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
88procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
89procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); overload; override;
90
91// 下列为 Canvas 原有函数
92procedure DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean = False); override;
93procedure 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
95procedure SetMatrix(const M: TMatrix); override;
96
97procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single); overload; override;
98procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
99
100procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
101procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType = TCornerType.Round); overload; override;
102procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
103procedure 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
105procedure FillPath(const APath: TPathData; const AOpacity: Single); overload; override;
106procedure FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush); overload; override;
107procedure DrawPath(const APath: TPathData; const AOpacity: Single); overload; override;
108procedure DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
109
110procedure FillEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
111procedure FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush); overload; override;
112procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
113procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
114
115procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
116procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush); overload; override;
117procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
118procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
119
120procedure FillPolygon(const Points: TPolygon; const AOpacity: Single); override;
121procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single); overload; override;
122
123procedure IntersectClipRect(const ARect: TRectF); override;
124procedure ExcludeClipRect(const ARect: TRectF); override;
125end;
126
127{$IF Defined(ANDROID) or Defined(IOS)}
128
129TCustomNativeCanvas = class(TAbstractCanvas)
130private
131class var FModulateColor: TAlphaColor;
132public
133// 涂色 + 线色一次完成
134procedure 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;
135procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
136procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False); overload; override;
137procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); overload; override;
138
139// 下列为 Canvas 原有函数
140procedure 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;
141procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single); overload; override;
142
143procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
144procedure FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType = TCornerType.Round); overload; override;
145procedure DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round); overload; override;
146procedure 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
148procedure FillPath(const APath: TPathData; const AOpacity: Single); overload; override;
149procedure FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush); overload; override;
150procedure DrawPath(const APath: TPathData; const AOpacity: Single); overload; override;
151procedure DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
152
153procedure FillEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
154procedure FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush); overload; override;
155procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single); overload; override;
156procedure DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
157
158procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
159procedure FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush); overload; override;
160procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single); overload; override;
161procedure DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
162
163procedure FillPolygon(const Points: TPolygon; const AOpacity: Single); override;
164procedure DrawPolygon(const Points: TPolygon; const AOpacity: Single); overload; override;
165
166class property ModulateColor: TAlphaColor read FModulateColor write FModulateColor;
167end;
168{$ENDIF}
169{$IFDEF ANDROID}
170
171TAndroidNativeCanvas = class(TCustomNativeCanvas)
172private
173GlobalCanvas: JCanvas;
174procedure ApplyGradient(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF);
175procedure ApplyFill(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
176procedure DrawFill(const Paint1: JPaint; const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
177procedure ApplyStroke(const Paint1: JPaint; const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
178public
179procedure NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc); override;
180procedure DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean = False); override;
181procedure DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); override;
182
183// 下列为 Canvas 原有函数
184procedure 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;
185procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
186
187procedure IntersectClipRect(const ARect: TRectF); override;
188procedure ExcludeClipRect(const ARect: TRectF); override;
189end;
190{$ENDIF}
191{$IFDEF IOS}
192
193TIOSNativeCanvas = class(TCustomNativeCanvas)
194private
195GlobalCanvas: CGContextRef;
196procedure ApplyGradient(const ABrush: TBrush; const ARect: TRectF);
197procedure ApplyFill(const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
198procedure DrawFill(const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
199procedure ApplyStroke(const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
200function GetPostScriptFontName: CFStringRef;
201public
202constructor Create(ACanvas: TCanvas); override;
203procedure NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc); override;
204procedure DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean = False); override;
205
206procedure SetMatrix(const M: TMatrix); override;
207
208procedure DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush); override;
209
210// 下列为 Canvas 原有函数
211procedure 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;
212procedure DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush); overload; override;
213
214procedure IntersectClipRect(const ARect: TRectF); override;
215procedure ExcludeClipRect(const ARect: TRectF); override;
216end;
217{$ENDIF}
218
219TCanvasHelper = class helper for TCanvas
220public
221function ToNativeCanvas(AMethod: TDrawMethod): INativeCanvas;
222end;
223
224implementation
225
226type
227TMyCanvas = class(TCanvas)
228
229end;
230{$IFDEF IOS}
231NSStringClass = 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;
251end;
252
253NSString = interface(NSObject)
254['{A62E83E4-AEB3-405F-8AFA-5B873D6E057F}']
255function UTF8String: MarshaledAString; cdecl;
256function boolValue: Boolean; cdecl;
257function cString: MarshaledAString; cdecl;
258function cStringLength: NSUInteger; cdecl;
259function cStringUsingEncoding(encoding: NSStringEncoding): MarshaledAString; cdecl;
260function canBeConvertedToEncoding(encoding: NSStringEncoding): Boolean; cdecl;
261function capitalizedString: NSString; cdecl;
262function caseInsensitiveCompare(string_: NSString): NSComparisonResult; cdecl;
263function characterAtIndex(index: NSUInteger): unichar; cdecl;
264function commonPrefixWithString(aString: NSString; options: NSStringCompareOptions): NSString; cdecl;
265function compare(string_: NSString): NSComparisonResult; cdecl; overload;
266function compare(string_: NSString; options: NSStringCompareOptions): NSComparisonResult; cdecl; overload;
267function compare(string_: NSString; options: NSStringCompareOptions; range: NSRange): NSComparisonResult; cdecl; overload;
268function compare(string_: NSString; options: NSStringCompareOptions; range: NSRange; locale: Pointer): NSComparisonResult; cdecl; overload;
269function completePathIntoString(outputName: NSString; caseSensitive: Boolean; matchesIntoArray: NSArray; filterTypes: NSArray): NSUInteger; cdecl;
270function componentsSeparatedByCharactersInSet(separator: NSCharacterSet): NSArray; cdecl;
271function componentsSeparatedByString(separator: NSString): NSArray; cdecl;
272function dataUsingEncoding(encoding: NSStringEncoding): NSData; cdecl; overload;
273function dataUsingEncoding(encoding: NSStringEncoding; allowLossyConversion: Boolean): NSData; cdecl; overload;
274function decomposedStringWithCanonicalMapping: NSString; cdecl;
275function decomposedStringWithCompatibilityMapping: NSString; cdecl;
276function description: NSString; cdecl;
277function doubleValue: double; cdecl;
278function fastestEncoding: NSStringEncoding; cdecl;
279function fileSystemRepresentation: MarshaledAString; cdecl;
280function floatValue: Single; cdecl;
281function getBytes(buffer: Pointer; maxLength: NSUInteger; usedLength: NSUInteger; encoding: NSStringEncoding; options: NSStringEncodingConversionOptions; range: NSRange; remainingRange: PNSRange): Boolean; cdecl;
282procedure getCString(bytes: MarshaledAString); cdecl; overload;
283procedure getCString(bytes: MarshaledAString; maxLength: NSUInteger); cdecl; overload;
284function getCString(buffer: MarshaledAString; maxLength: NSUInteger; encoding: NSStringEncoding): Boolean; cdecl; overload;
285procedure getCString(bytes: MarshaledAString; maxLength: NSUInteger; range: NSRange; remainingRange: PNSRange); cdecl; overload;
286procedure getCharacters(buffer: MarshaledString); cdecl; overload;
287procedure getCharacters(buffer: MarshaledString; range: NSRange); cdecl; overload;
288function getFileSystemRepresentation(cname: MarshaledAString; maxLength: NSUInteger): Boolean; cdecl;
289function hasPrefix(aString: NSString): Boolean; cdecl;
290function hasSuffix(aString: NSString): Boolean; cdecl;
291function hash: NSUInteger; cdecl;
292function init: Pointer; cdecl;
293function initWithBytes(bytes: Pointer; length: NSUInteger; encoding: NSStringEncoding): Pointer; cdecl;
294function initWithBytesNoCopy(bytes: Pointer; length: NSUInteger; encoding: NSStringEncoding; freeWhenDone: Boolean): Pointer; cdecl;
295function initWithCString(bytes: MarshaledAString): Pointer; cdecl; overload;
296function initWithCString(nullTerminatedCString: MarshaledAString; encoding: NSStringEncoding): Pointer; cdecl; overload;
297// function initWithCString(bytes: PAnsiChar; length: NSUInteger): Pointer; cdecl; overload;
298function initWithCStringNoCopy(bytes: MarshaledAString; length: NSUInteger; freeWhenDone: Boolean): Pointer; cdecl;
299function initWithCharacters(characters: MarshaledString; length: NSUInteger): Pointer; cdecl;
300function initWithCharactersNoCopy(characters: MarshaledString; length: NSUInteger; freeWhenDone: Boolean): Pointer; cdecl;
301function initWithContentsOfFile(path: NSString): Pointer; cdecl; overload;
302function initWithContentsOfFile(path: NSString; encoding: NSStringEncoding; error: PPointer): Pointer; cdecl; overload;
303function initWithContentsOfFile(path: NSString; usedEncoding: PNSStringEncoding; error: PPointer): Pointer; cdecl; overload;
304function initWithContentsOfURL(url: NSURL): Pointer; cdecl; overload;
305function initWithContentsOfURL(url: NSURL; encoding: NSStringEncoding; error: PPointer): Pointer; cdecl; overload;
306function initWithContentsOfURL(url: NSURL; usedEncoding: PNSStringEncoding; error: PPointer): Pointer; cdecl; overload;
307function initWithData(data: NSData; encoding: NSStringEncoding): Pointer; cdecl;
308function initWithFormat(initWithFormat: NSString): Pointer; cdecl; overload;
309function initWithFormat(format: NSString; locale: Pointer): Pointer; cdecl; overload;
310function initWithString(aString: NSString): Pointer; cdecl;
311function initWithUTF8String(nullTerminatedCString: MarshaledAString): Pointer; cdecl;
312function intValue: Integer; cdecl;
313function integerValue: NSInteger; cdecl;
314function isAbsolutePath: Boolean; cdecl;
315function isEqualToString(aString: NSString): Boolean; cdecl;
316function lastPathComponent: NSString; cdecl;
317function length: NSUInteger; cdecl;
318function lengthOfBytesUsingEncoding(enc: NSStringEncoding): NSUInteger; cdecl;
319function lineRangeForRange(range: NSRange): NSRange; cdecl;
320function localizedCaseInsensitiveCompare(string_: NSString): NSComparisonResult; cdecl;
321function localizedCompare(string_: NSString): NSComparisonResult; cdecl;
322function localizedStandardCompare(string_: NSString): NSComparisonResult; cdecl;
323function longLongValue: Int64; cdecl;
324function lossyCString: MarshaledAString; cdecl;
325function lowercaseString: NSString; cdecl;
326function maximumLengthOfBytesUsingEncoding(enc: NSStringEncoding): NSUInteger; cdecl;
327function paragraphRangeForRange(range: NSRange): NSRange; cdecl;
328function pathComponents: NSArray; cdecl;
329function pathExtension: NSString; cdecl;
330function precomposedStringWithCanonicalMapping: NSString; cdecl;
331function precomposedStringWithCompatibilityMapping: NSString; cdecl;
332function propertyList: Pointer; cdecl;
333function propertyListFromStringsFileFormat: NSDictionary; cdecl;
334function rangeOfCharacterFromSet(aSet: NSCharacterSet): NSRange; cdecl; overload;
335function rangeOfCharacterFromSet(aSet: NSCharacterSet; options: NSStringCompareOptions): NSRange; cdecl; overload;
336function rangeOfCharacterFromSet(aSet: NSCharacterSet; options: NSStringCompareOptions; range: NSRange): NSRange; cdecl; overload;
337function rangeOfComposedCharacterSequenceAtIndex(index: NSUInteger): NSRange; cdecl;
338function rangeOfComposedCharacterSequencesForRange(range: NSRange): NSRange; cdecl;
339function rangeOfString(aString: NSString): NSRange; cdecl; overload;
340function rangeOfString(aString: NSString; options: NSStringCompareOptions): NSRange; cdecl; overload;
341function rangeOfString(aString: NSString; options: NSStringCompareOptions; range: NSRange): NSRange; cdecl; overload;
342function rangeOfString(aString: NSString; options: NSStringCompareOptions; range: NSRange; locale: NSLocale): NSRange; cdecl; overload;
343function smallestEncoding: NSStringEncoding; cdecl;
344function stringByAbbreviatingWithTildeInPath: NSString; cdecl;
345function stringByAddingPercentEscapesUsingEncoding(enc: NSStringEncoding): NSString; cdecl;
346function stringByAppendingFormat(stringByAppendingFormat: NSString): NSString; cdecl;
347function stringByAppendingPathComponent(str: NSString): NSString; cdecl;
348function stringByAppendingPathExtension(str: NSString): NSString; cdecl;
349function stringByAppendingString(aString: NSString): NSString; cdecl;
350function stringByDeletingLastPathComponent: NSString; cdecl;
351function stringByDeletingPathExtension: NSString; cdecl;
352function stringByExpandingTildeInPath: NSString; cdecl;
353function stringByFoldingWithOptions(options: NSStringCompareOptions; locale: NSLocale): NSString; cdecl;
354function stringByPaddingToLength(newLength: NSUInteger; withString: NSString; startingAtIndex: NSUInteger): NSString; cdecl;
355function stringByReplacingCharactersInRange(range: NSRange; withString: NSString): NSString; cdecl;
356function stringByReplacingOccurrencesOfString(target: NSString; withString: NSString): NSString; cdecl; overload;
357function stringByReplacingOccurrencesOfString(target: NSString; withString: NSString; options: NSStringCompareOptions; range: NSRange): NSString; cdecl; overload;
358function stringByReplacingPercentEscapesUsingEncoding(enc: NSStringEncoding): NSString; cdecl;
359function stringByResolvingSymlinksInPath: NSString; cdecl;
360function stringByStandardizingPath: NSString; cdecl;
361function stringByTrimmingCharactersInSet(set_: NSCharacterSet): NSString; cdecl;
362function stringsByAppendingPaths(paths: NSArray): NSArray; cdecl;
363function substringFromIndex(from: NSUInteger): NSString; cdecl;
364function substringToIndex(to_: NSUInteger): NSString; cdecl;
365function substringWithRange(range: NSRange): NSString; cdecl;
366function uppercaseString: NSString; cdecl;
367function writeToFile(path: NSString; atomically: Boolean): Boolean; cdecl; overload;
368function writeToFile(path: NSString; atomically: Boolean; encoding: NSStringEncoding; error: PPointer): Boolean; cdecl; overload;
369function writeToURL(url: NSURL; atomically: Boolean): Boolean; cdecl; overload;
370function writeToURL(url: NSURL; atomically: Boolean; encoding: NSStringEncoding; error: PPointer): Boolean; cdecl; overload;
371procedure drawInRect(aRect: NSRect; withAttributes: NSDictionary); cdecl;
372procedure drawAtPoint(aPoint: NSPoint; withAttributes: NSDictionary); cdecl;
373function sizeWithAttributes(attributes: NSDictionary): NSSize; cdecl;
374function boundingRectWithSize(size: NSSize; options: NSStringDrawingOptions; attributes: NSDictionary): NSRect; cdecl;
375end;
376TNSString = class(TOCGenericImport<NSStringClass, NSString>) end;
377
378UIFontClass = 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;
391end;
392UIFont = interface(NSObject)
393['{026495EC-177F-4517-9B25-C2F2371A110D}']
394function ascender: CGFloat; cdecl;
395function capHeight: CGFloat; cdecl;
396function descender: CGFloat; cdecl;
397function familyName: NSString; cdecl;
398function fontName: NSString; cdecl;
399function fontWithSize(fontSize: CGFloat): UIFont; cdecl;
400function leading: CGFloat; cdecl;
401function lineHeight: CGFloat; cdecl;
402function pointSize: CGFloat; cdecl;
403function xHeight: CGFloat; cdecl;
404function fontDescriptor: UIFontDescriptor; cdecl;
405end;
406TUIFont = class(TOCGenericImport<UIFontClass, UIFont>) end;
407{$ENDIF}
408
409{ TFiremonkeyCanvas }
410
411procedure TFiremonkeyCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean);
412var
413R: TRectF;
414P: TPointF;
415begin
416R := RectF(Center.X - Radius.X, Center.Y - Radius.Y, Center.X + Radius.X, Center.Y + Radius.Y);
417
418// 线在区内
419if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
420begin
421R.Offset(-R.Left, -R.Top);
422InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
423end;
424
425P := PointF(R.Width / 2, R.Height / 2);
426
427FCanvas.FillArc(Center, P, StartAngle, SweepAngle, AOpacity, AFill);
428FCanvas.DrawArc(Center, P, StartAngle, SweepAngle, AOpacity, AStroke);
429end;
430
431procedure TFiremonkeyCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean);
432var
433R: TRectF;
434begin
435R := ARect;
436
437// 线在区内
438if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
439InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
440
441FCanvas.FillEllipse(R, AOpacity, AFill);
442FCanvas.DrawEllipse(R, AOpacity, AStroke);
443end;
444
445procedure TFiremonkeyCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single);
446begin
447FCanvas.DrawLine(APt1, APt2, AOpacity);
448end;
449
450procedure TFiremonkeyCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush);
451begin
452FCanvas.DrawLine(APt1, APt2, AOpacity, ABrush);
453end;
454
455procedure TFiremonkeyCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
456begin
457FCanvas.FillPath(APath, AOpacity, AFill);
458FCanvas.DrawPath(APath, AOpacity, AStroke);
459end;
460
461procedure TFiremonkeyCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush);
462begin
463FCanvas.DrawPath(APath, AOpacity, ABrush);
464end;
465
466procedure TFiremonkeyCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single);
467begin
468FCanvas.DrawPolygon(Points, AOpacity);
469end;
470
471procedure TFiremonkeyCanvas.DrawPath(const APath: TPathData; const AOpacity: Single);
472begin
473FCanvas.DrawPath(APath, AOpacity);
474end;
475
476procedure TFiremonkeyCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
477begin
478if AFill <> nil then
479FCanvas.Fill.Assign(AFill);
480
481if AStroke <> nil then
482FCanvas.Stroke.Assign(AStroke);
483
484FCanvas.FillPolygon(Points, AOpacity);
485FCanvas.DrawPolygon(Points, AOpacity);
486end;
487
488procedure TFiremonkeyCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TStrokeBrush; const ACornerType: TCornerType);
489begin
490FCanvas.DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, ABrush, ACornerType);
491end;
492
493procedure TFiremonkeyCanvas.ExcludeClipRect(const ARect: TRectF);
494begin
495FCanvas.ExcludeClipRect(ARect);
496end;
497
498procedure TFiremonkeyCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType);
499begin
500FCanvas.DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, ACornerType);
501end;
502
503procedure 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);
504var
505R: TRectF;
506begin
507R := ARect;
508
509// 线在区内
510if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
511InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
512
513FCanvas.FillRect(R, XRadius, YRadius, ACorners, AOpacity, AFill, ACornerType);
514FCanvas.DrawRect(R, XRadius, YRadius, ACorners, AOpacity, AStroke, ACornerType);
515end;
516
517procedure TFiremonkeyCanvas.FillPath(const APath: TPathData; const AOpacity: Single);
518begin
519FCanvas.FillPath(APath, AOpacity);
520end;
521
522procedure TFiremonkeyCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush);
523begin
524FCanvas.FillArc(Center, Radius, StartAngle, SweepAngle, AOpacity, ABrush);
525end;
526
527procedure TFiremonkeyCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
528begin
529FCanvas.FillArc(Center, Radius, StartAngle, SweepAngle, AOpacity);
530end;
531
532procedure TFiremonkeyCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush);
533begin
534FCanvas.FillEllipse(ARect, AOpacity, ABrush);
535end;
536
537procedure TFiremonkeyCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single);
538begin
539FCanvas.FillEllipse(ARect, AOpacity);
540end;
541
542procedure TFiremonkeyCanvas.FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush);
543begin
544FCanvas.FillPath(APath, AOpacity, ABrush);
545end;
546
547procedure TFiremonkeyCanvas.FillPolygon(const Points: TPolygon; const AOpacity: Single);
548begin
549FCanvas.FillPolygon(Points, AOpacity);
550end;
551
552procedure TFiremonkeyCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType);
553begin
554FCanvas.FillRect(ARect, XRadius, YRadius, ACorners, AOpacity, ABrush, ACornerType);
555end;
556
557procedure TFiremonkeyCanvas.FillText(const ARect: TRectF; const AText: string;
558const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags;
559const ATextAlign, AVTextAlign: TTextAlign);
560begin
561FCanvas.FillText(ARect, AText, WordWrap, AOpacity, Flags, ATextAlign, AVTextAlign);
562end;
563
564procedure TFiremonkeyCanvas.IntersectClipRect(const ARect: TRectF);
565begin
566FCanvas.IntersectClipRect(ARect);
567end;
568
569procedure TFiremonkeyCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType);
570begin
571FCanvas.FillRect(ARect, XRadius, YRadius, ACorners, AOpacity, ACornerType);
572end;
573
574procedure TFiremonkeyCanvas.NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc);
575begin
576// 绘图函数
577if Assigned(ADrawProc) then
578ADrawProc;
579end;
580
581procedure TFiremonkeyCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
582begin
583FCanvas.DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity);
584end;
585
586procedure TFiremonkeyCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush);
587begin
588FCanvas.DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, ABrush);
589end;
590
591procedure TFiremonkeyCanvas.DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean);
592begin
593FCanvas.DrawBitmap(ABitmap, SrcRect, DstRect, AOpacity, HighSpeed);
594end;
595
596procedure TFiremonkeyCanvas.SetMatrix(const M: TMatrix);
597begin
598FCanvas.SetMatrix(M);
599end;
600
601procedure TFiremonkeyCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single);
602begin
603FCanvas.DrawEllipse(ARect, AOpacity);
604end;
605
606procedure TFiremonkeyCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush);
607begin
608FCanvas.FillEllipse(ARect, AOpacity, ABrush);
609end;
610
611{ TCanvasHelper }
612
613function TCanvasHelper.ToNativeCanvas(AMethod: TDrawMethod): INativeCanvas;
614begin
615case AMethod of
616Native:
617{$IF DEFINED(ANDROID) OR DEFINED(IOS)}
618{$IFDEF ANDROID}
619Result := TAndroidNativeCanvas.Create(Self);
620{$ELSE}
621Result := TIOSNativeCanvas.Create(Self);
622{$ENDIF}
623{$ELSE}
624Result := TFiremonkeyCanvas.Create(Self);
625{$ENDIF}
626Firemonkey:
627Result := TFiremonkeyCanvas.Create(Self);
628end;
629end;
630
631{$IF Defined(ANDROID) or Defined(IOS)}
632{ TCustomNativeCanvas }
633
634procedure 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);
635begin
636end;
637
638procedure TCustomNativeCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single);
639begin
640DrawLine(APt1, APt2, AOpacity, FCanvas.Stroke);
641end;
642
643procedure 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);
644var
645Path: TPathData;
646R: TRectF;
647begin
648R := ARect;
649
650// 线在区内
651if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
652InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
653
654Path := TPathData.Create;
655try
656Path.AddRectangle(R, XRadius, YRadius, ACorners, ACornerType);
657DrawPath(Path, AOpacity, AFill, AStroke);
658finally
659Path.Free;
660end;
661end;
662
663procedure TCustomNativeCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False);
664var
665Path: TPathData;
666R: TRectF;
667begin
668R := ARect;
669
670// 线在区内
671if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
672begin
673R.Offset(-R.Left, -R.Top);
674InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
675end;
676
677Path := TPathData.Create;
678try
679Path.AddEllipse(R);
680DrawPath(Path, AOpacity, AFill, AStroke);
681finally
682Path.Free;
683end;
684end;
685
686procedure TCustomNativeCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush; const Inside: Boolean = False);
687var
688R: TRectF;
689P: TPointF;
690Path: TPathData;
691begin
692R := RectF(Center.X - Radius.X, Center.Y - Radius.Y, Center.X + Radius.X, Center.Y + Radius.Y);
693
694// 线在区内
695if Inside and (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
696begin
697R.Offset(-R.Left, -R.Top);
698InflateRect(R, -(AStroke.Thickness / 2), -(AStroke.Thickness / 2));
699end;
700
701P := PointF(R.Width / 2, R.Height / 2);
702
703Path := TPathData.Create;
704try
705Path.AddArc(Center, P, StartAngle, SweepAngle);
706DrawPath(Path, AOpacity, AFill, AStroke);
707finally
708Path.Free;
709end;
710end;
711
712procedure TCustomNativeCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
713var
714i: Integer;
715Path: TPathData;
716PathBreakFound: Boolean;
717begin
718Path := TPathData.Create;
719
720try
721PathBreakFound := False;
722
723for i := 0 to High(Points) do
724begin
725if i = 0 then
726begin
727Path.MoveTo(Points[i]);
728end
729else if (Points[i].X = PolygonPointBreak.X) and (Points[i].Y = PolygonPointBreak.Y) then
730begin
731Path.ClosePath;
732PathBreakFound := True;
733end
734else
735Path.LineTo(Points[i]);
736end;
737
738if not PathBreakFound then
739Path.ClosePath;
740
741DrawPath(Path, AOpacity, AFill, AStroke);
742finally
743Path.Free;
744end;
745end;
746
747procedure TCustomNativeCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round);
748begin
749DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, nil, FCanvas.Stroke, ACornerType);
750end;
751
752procedure TCustomNativeCanvas.DrawRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TStrokeBrush; const ACornerType: TCornerType = TCornerType.Round);
753begin
754DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, nil, ABrush, ACornerType);
755end;
756
757procedure TCustomNativeCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ACornerType: TCornerType = TCornerType.Round);
758begin
759DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, FCanvas.Fill, nil, ACornerType);
760end;
761
762procedure TCustomNativeCanvas.FillRect(const ARect: TRectF; const XRadius, YRadius: Single; const ACorners: TCorners; const AOpacity: Single; const ABrush: TBrush; const ACornerType: TCornerType = TCornerType.Round);
763begin
764DrawRect(ARect, XRadius, YRadius, ACorners, AOpacity, ABrush, nil, ACornerType);
765end;
766
767procedure TCustomNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const ABrush: TStrokeBrush);
768begin
769DrawPath(APath, AOpacity, nil, ABrush);
770end;
771
772procedure TCustomNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single);
773begin
774DrawPath(APath, AOpacity, nil, FCanvas.Stroke);
775end;
776
777procedure TCustomNativeCanvas.FillPath(const APath: TPathData; const AOpacity: Single);
778begin
779DrawPath(APath, AOpacity, FCanvas.Fill, nil);
780end;
781
782procedure TCustomNativeCanvas.FillPath(const APath: TPathData; const AOpacity: Single; const ABrush: TBrush);
783begin
784DrawPath(APath, AOpacity, ABrush, nil);
785end;
786
787procedure TCustomNativeCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single);
788begin
789DrawEllipse(ARect, AOpacity, FCanvas.Fill, nil);
790end;
791
792procedure TCustomNativeCanvas.FillEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TBrush);
793begin
794DrawEllipse(ARect, AOpacity, ABrush, nil);
795end;
796
797procedure TCustomNativeCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single);
798begin
799DrawEllipse(ARect, AOpacity, nil, FCanvas.Stroke);
800end;
801
802procedure TCustomNativeCanvas.DrawEllipse(const ARect: TRectF; const AOpacity: Single; const ABrush: TStrokeBrush);
803begin
804DrawEllipse(ARect, AOpacity, nil, ABrush);
805end;
806
807procedure TCustomNativeCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
808begin
809DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, FCanvas.Fill, nil);
810end;
811
812procedure TCustomNativeCanvas.FillArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TBrush);
813begin
814DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, ABrush, nil);
815end;
816
817procedure TCustomNativeCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single);
818begin
819DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, nil, FCanvas.Stroke);
820end;
821
822procedure TCustomNativeCanvas.DrawArc(const Center, Radius: TPointF; StartAngle, SweepAngle: Single; const AOpacity: Single; const ABrush: TStrokeBrush);
823begin
824DrawArc(Center, Radius, StartAngle, SweepAngle, AOpacity, nil, ABrush);
825end;
826
827procedure TCustomNativeCanvas.FillPolygon(const Points: TPolygon; const AOpacity: Single);
828begin
829DrawPolygon(Points, AOpacity, FCanvas.Fill, nil);
830end;
831
832procedure TCustomNativeCanvas.DrawPolygon(const Points: TPolygon; const AOpacity: Single);
833begin
834DrawPolygon(Points, AOpacity, nil, FCanvas.Stroke);
835end;
836
837{$ENDIF}
838{$IFDEF ANDROID}
839
840function JBitmapToBitmap(const AImage: JBitmap): TBitmap;
841var
842Surface: TBitmapSurface;
843begin
844Surface := TBitmapSurface.Create;
845Result := nil;
846try
847if JBitmapToSurface(AImage, Surface) then
848begin
849Result := TBitmap.Create;
850Result.Assign(Surface);
851end;
852finally
853Surface.Free;
854end;
855end;
856
857function BitmapToJBitmap(const ABitmap: TBitmap): JBitmap;
858var
859BitmapSurface: TBitmapSurface;
860begin
861Assert(ABitmap <> nil);
862
863Result := TJBitmap.JavaClass.createBitmap(ABitmap.Width, ABitmap.Height, TJBitmap_Config.JavaClass.ARGB_8888);
864BitmapSurface := TBitmapSurface.Create;
865try
866BitmapSurface.Assign(ABitmap);
867if not SurfaceToJBitmap(BitmapSurface, Result) then
868Result := nil;
869finally
870BitmapSurface.Free;
871end;
872end;
873
874procedure TAndroidNativeCanvas.ApplyFill(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
875begin
876if (ABrush.Kind = TBrushKind.Resource) and (ABrush.Resource <> nil) and (ABrush.Resource.Brush <> nil) then
877ABrush.Assign(ABrush.Resource.Brush);
878
879Paint1.setStyle(TJPaint_Style.Wrap(TJPaint_Style.JavaClass.Fill));
880
881case ABrush.Kind of
882TBrushKind.Solid:
883Paint1.setARGB(TColorRec(ABrush.Color).A, TColorRec(ABrush.Color).B, TColorRec(ABrush.Color).G, TColorRec(ABrush.Color).R);
884TBrushKind.Gradient:
885ApplyGradient(Paint1, ABrush, ARect);
886else
887Paint1.setARGB(0, 255, 255, 255);
888end;
889end;
890
891procedure TAndroidNativeCanvas.ApplyGradient(const Paint1: JPaint; const ABrush: TBrush; const ARect: TRectF);
892var
893i: Integer;
894aColors: TJavaArray<Integer>;
895aStops: TJavaArray<Single>;
896aLinearShader: JLinearGradient;
897aRadialShader: JRadialGradient;
898begin
899aColors := TJavaArray<Integer>.Create(ABrush.Gradient.Points.Count);
900aStops := TJavaArray<Single>.Create(ABrush.Gradient.Points.Count);
901
902for i := 0 to ABrush.Gradient.Points.Count - 1 do
903begin
904aColors[ABrush.Gradient.Points.Count - 1 - i] := Integer(ABrush.Gradient.Points[i].Color);
905aStops[ABrush.Gradient.Points.Count - 1 - i] := 1 - ABrush.Gradient.Points[i].Offset;
906end;
907
908case ABrush.Gradient.Style of
909// 线渐层
910TGradientStyle.Linear:
911begin
912aLinearShader := 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);
913Paint1.setShader(aLinearShader);
914aLinearShader := nil;
915end;
916// 圆渐层
917TGradientStyle.Radial:
918begin
919aRadialShader := TJRadialGradient.JavaClass.init(ARect.CenterPoint.X, ARect.CenterPoint.Y, ARect.Width / 2, aColors, aStops, TJShader_TileMode.JavaClass.CLAMP);
920Paint1.setShader(aRadialShader);
921aRadialShader := nil;
922end;
923else
924Paint1.setShader(nil);
925end;
926
927FreeAndNil(aColors);
928FreeAndNil(aStops);
929end;
930
931procedure TAndroidNativeCanvas.ApplyStroke(const Paint1: JPaint; const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
932var
933i: Integer;
934Dash: TJavaArray<Single>;
935begin
936if (AStroke.Kind = TBrushKind.Resource) and (AStroke.Resource <> nil) and (AStroke.Resource.Brush <> nil) then
937AStroke.Assign(AStroke.Resource.Brush);
938
939Paint1.setStyle(TJPaint_Style.Wrap(TJPaint_Style.JavaClass.Stroke));
940
941// Thickness = 0 还是有线
942if AStroke.Thickness > 0 then
943begin
944Paint1.setStrokeWidth(AStroke.Thickness);
945
946case AStroke.Kind of
947TBrushKind.Solid, TBrushKind.Bitmap:
948Paint1.setARGB(TColorRec(AStroke.Color).A, TColorRec(AStroke.Color).B, TColorRec(AStroke.Color).G, TColorRec(AStroke.Color).R);
949TBrushKind.Gradient:
950ApplyGradient(Paint1, AStroke, ARect);
951else
952Paint1.setARGB(0, 0, 0, 0);
953end;
954
955case AStroke.Cap of
956TStrokeCap.Flat:
957Paint1.setStrokeCap(TJPaint_Cap.JavaClass.BUTT);
958TStrokeCap.Round:
959Paint1.setStrokeCap(TJPaint_Cap.JavaClass.Round);
960end;
961
962if Length(AStroke.DashArray) > 0 then
963begin
964Dash := TJavaArray<Single>.Create(Length(AStroke.DashArray));
965
966for i := 0 to High(AStroke.DashArray) do
967begin
968Dash[i] := AStroke.DashArray[i] * AStroke.Thickness;
969if AStroke.Cap = TStrokeCap.Round then
970begin
971if Odd(i) then
972Dash[i] := (AStroke.DashArray[i] + 0.9) * AStroke.Thickness
973else
974Dash[i] := (AStroke.DashArray[i] - 0.9) * AStroke.Thickness;
975end;
976end;
977
978Paint1.setPathEffect(TJDashPathEffect.JavaClass.init(Dash, 0));
979end;
980
981case AStroke.Join of
982TStrokeJoin.Miter:
983Paint1.setStrokeJoin(TJPaint_Join.JavaClass.Miter);
984TStrokeJoin.Round:
985Paint1.setStrokeJoin(TJPaint_Join.JavaClass.Round);
986TStrokeJoin.Bevel:
987Paint1.setStrokeJoin(TJPaint_Join.JavaClass.Bevel);
988end;
989end
990else
991Paint1.setARGB(0, 0, 0, 0);
992end;
993
994procedure TAndroidNativeCanvas.DrawBitmap(const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean);
995var
996Paint1: JPaint;
997jb: JBitmap;
998src: JRect;
999dst: JRectF;
1000begin
1001if GlobalCanvas = nil then
1002Exit;
1003
1004Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1005Paint1.setAlpha(Round(AOpacity * 255));
1006Paint1.setAntiAlias(true);
1007
1008jb := BitmapToJBitmap(ABitmap);
1009src := TJRect.JavaClass.init;
1010src.&set(Round(SrcRect.Left), Round(SrcRect.Top), Round(SrcRect.Right), Round(SrcRect.Bottom));
1011dst := TJRectF.JavaClass.init;
1012dst.&set(DstRect.Left, DstRect.Top, DstRect.Right, DstRect.Bottom);
1013GlobalCanvas.DrawBitmap(jb, src, dst, Paint1);
1014end;
1015
1016procedure TAndroidNativeCanvas.DrawFill(const Paint1: JPaint; const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
1017begin
1018if (ABrush.Kind = TBrushKind.Resource) and (ABrush.Resource <> nil) and (ABrush.Resource.Brush <> nil) then
1019ABrush.Assign(ABrush.Resource.Brush);
1020
1021if ABrush.Kind = TBrushKind.Bitmap then
1022begin
1023// 未完成
1024end;
1025end;
1026
1027procedure TAndroidNativeCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush);
1028var
1029Paint1: JPaint;
1030begin
1031if GlobalCanvas = nil then
1032Exit;
1033
1034if ABrush.Kind <> TBrushKind.None then
1035begin
1036Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1037ApplyStroke(Paint1, ABrush, TRectF.Create(APt1.X, APt1.Y, APt2.X, APt2.Y), AOpacity);
1038GlobalCanvas.DrawLine(APt1.X, APt1.Y, APt2.X, APt2.Y, Paint1);
1039end;
1040end;
1041
1042procedure TAndroidNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
1043var
1044i: Integer;
1045Path1: JPath;
1046Paint1: JPaint;
1047SrcRect: TRectF;
1048begin
1049Log.d('Enter TAndroidNativeCanvas.DrawPath');
1050if GlobalCanvas = nil then
1051Exit;
1052
1053SrcRect := APath.GetBounds;
1054Path1 := TJPath.Wrap(TJPath.JavaClass.init);
1055
1056i := 0;
1057while i < APath.Count do
1058begin
1059case APath.Points[i].Kind of
1060// 移到
1061TPathPointKind.MoveTo:
1062Path1.MoveTo(APath.Points[i].Point.X, APath.Points[i].Point.Y);
1063// 线到
1064TPathPointKind.LineTo:
1065Path1.LineTo(APath.Points[i].Point.X, APath.Points[i].Point.Y);
1066// 曲线
1067TPathPointKind.CurveTo:
1068begin
1069Path1.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);
1070Inc(i, 2);
1071end;
1072// 关闭
1073TPathPointKind.Close:
1074Path1.Close;
1075end;
1076Inc(i);
1077end;
1078
1079GlobalCanvas.save;
1080Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1081
1082if (AFill <> nil) and (AFill.Kind <> TBrushKind.None) then
1083begin
1084if AFill.Kind = TBrushKind.Bitmap then
1085begin
1086GlobalCanvas.DrawPath(Path1, Paint1);
1087DrawFill(Paint1, AFill, SrcRect, SrcRect, AOpacity);
1088end
1089else
1090begin
1091Path1.setFillType(TJPath_FillType.Wrap(TJPath_FillType.JavaClass.EVEN_ODD));
1092ApplyFill(Paint1, AFill, SrcRect, AOpacity);
1093GlobalCanvas.DrawPath(Path1, Paint1);
1094end;
1095end;
1096
1097if (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
1098begin
1099ApplyStroke(Paint1, AStroke, SrcRect, AOpacity);
1100GlobalCanvas.DrawPath(Path1, Paint1);
1101end;
1102
1103GlobalCanvas.restore;
1104end;
1105
1106procedure TAndroidNativeCanvas.IntersectClipRect(const ARect: TRectF);
1107var
1108JR: JRectF;
1109begin
1110if GlobalCanvas <> nil then
1111begin
1112JR := TJRectF.JavaClass.init(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
1113
1114GlobalCanvas.clipRect(JR, TJRegion_Op.JavaClass.INTERSECT);
1115end;
1116end;
1117
1118procedure TAndroidNativeCanvas.ExcludeClipRect(const ARect: TRectF);
1119var
1120JR: JRectF;
1121begin
1122if GlobalCanvas <> nil then
1123begin
1124JR := TJRectF.JavaClass.init(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
1125
1126GlobalCanvas.clipRect(JR, TJRegion_Op.JavaClass.REPLACE);
1127end;
1128end;
1129
1130procedure TAndroidNativeCanvas.FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign, AVTextAlign: TTextAlign);
1131var
1132js: JString;
1133Paint1: JPaint;
1134tr: JRect;
1135x, y: Single;
1136align: JPaint_Align;
1137fm: JPaint_FontMetricsInt;
1138begin
1139if GlobalCanvas = nil then
1140Exit;
1141js := StringToJString(AText);
1142Paint1 := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1143Paint1.setTextSize(Font.Size);
1144if (Fill <> nil) and (Fill.Kind <> TBrushKind.None) then
1145begin
1146ApplyFill(Paint1, Fill, ARect, AOpacity);
1147end;
1148align := TJPaint_Align.Wrap(TJPaint_Align.JavaClass.LEFT);
1149Paint1.setTextAlign(align);
1150Paint1.setStrikeThruText(TFontStyle.fsStrikeOut in Font.Style);
1151Paint1.setUnderlineText(TFontStyle.fsUnderline in Font.Style);
1152Paint1.setFakeBoldText(TFontStyle.fsBold in Font.Style);
1153if TFontStyle.fsItalic in Font.Style then
1154Paint1.setTextSkewX(-0.5);
1155tr := TJRect.JavaClass.init;
1156Paint1.getTextBounds(js, 0, js.length, tr);
1157case ATextAlign of
1158TTextAlign.Center: x := (ARect.Width - tr.width) / 2;
1159TTextAlign.Leading: x := 0;
1160TTextAlign.Trailing: x := ARect.Width - tr.width - tr.left;
1161end;
1162
1163fm := Paint1.getFontMetricsInt;
1164case AVTextAlign of
1165TTextAlign.Center: y := (ARect.Height - tr.top) / 2;
1166TTextAlign.Leading: y := -fm.top;
1167TTextAlign.Trailing: y := ARect.Height - fm.bottom;
1168end;
1169x := x + ARect.Left;
1170y := y + ARect.Top;
1171GlobalCanvas.drawText(js, x, y, Paint1);
1172Log.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]);
1174end;
1175
1176procedure TAndroidNativeCanvas.NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc);
1177var
1178Bitmap1: JBitmap;
1179Paint: JPaint;
1180Bitmap: TBitmap;
1181ScreenService: IFMXScreenService;
1182Scale1: Single;
1183begin
1184Log.d('Enter TAndroidNativeCanvas.NativeDraw');
1185if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService, ScreenService) then
1186Scale1 := ScreenService.GetScreenScale
1187else
1188Scale1 := 1;
1189
1190Bitmap1 := TJBitmap.JavaClass.createBitmap(Ceil(ARect.Width * Scale1), Ceil(ARect.Height * Scale1), TJBitmap_Config.JavaClass.ARGB_8888);
1191GlobalCanvas := TJCanvas.JavaClass.init(Bitmap1);
1192GlobalCanvas.save;
1193GlobalCanvas.scale(Scale1, Scale1);
1194
1195// 透明底色
1196Paint := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG));
1197Paint.setStyle(TJPaint_Style.Wrap(TJPaint_Style.JavaClass.Fill));
1198Paint.setARGB(0, 255, 255, 255);
1199GlobalCanvas.DrawRect(GlobalCanvas.getClipBounds, Paint);
1200
1201// 绘图函数
1202if Assigned(ADrawProc) then
1203ADrawProc;
1204
1205GlobalCanvas.restore;
1206
1207// 显示
1208Bitmap := JBitmapToBitmap(Bitmap1);
1209FCanvas.DrawBitmap(Bitmap, RectF(0, 0, Bitmap.Width, Bitmap.Height), ARect, 1);
1210FreeAndNil(Bitmap);
1211end;
1212
1213{$ENDIF}
1214{$IFDEF IOS}
1215
1216function PointToCGPoint(const P: TPointF): CGPoint;
1217begin
1218Result := CGPointMake(P.X, P.Y);
1219end;
1220
1221{ TIOSNativeCanvas }
1222
1223procedure TIOSNativeCanvas.ApplyFill(const ABrush: TBrush; const ARect: TRectF; const AOpacity: Single);
1224var
1225LColor: TAlphaColorF;
1226begin
1227if GlobalCanvas = nil then
1228Exit;
1229
1230if (ABrush.Kind = TBrushKind.Resource) and (ABrush.Resource <> nil) and (ABrush.Resource.Brush <> nil) then
1231ABrush.Assign(ABrush.Resource.Brush);
1232
1233case ABrush.Kind of
1234TBrushKind.Solid:
1235begin
1236LColor := TAlphaColorF.Create(MakeColor(ABrush.Color, AOpacity));
1237CGContextSetRGBFillColor(GlobalCanvas, LColor.R, LColor.G, LColor.B, LColor.A);
1238end;
1239else
1240CGContextSetRGBFillColor(GlobalCanvas, 0, 0, 0, 0);
1241end;
1242
1243// 渐层
1244if (ABrush.Kind = TBrushKind.Gradient) and (CGContextIsPathEmpty(GlobalCanvas) = 0) then
1245begin
1246CGContextClip(GlobalCanvas);
1247ApplyGradient(ABrush, ARect);
1248end;
1249end;
1250
1251procedure TIOSNativeCanvas.ApplyGradient(const ABrush: TBrush; const ARect: TRectF);
1252var
1253i: Integer;
1254Locations: TArray<CGFloat>;
1255colorSpace: CGColorSpaceRef;
1256Gradient: CGGradientRef;
1257colors: NSMutableArray;
1258RCenter: CGPoint;
1259begin
1260if GlobalCanvas = nil then
1261Exit;
1262
1263SetLength(Locations, ABrush.Gradient.Points.Count);
1264colors := TNSMutableArray.Wrap(TNSMutableArray.OCClass.arrayWithCapacity(ABrush.Gradient.Points.Count));
1265
1266for i := 0 to ABrush.Gradient.Points.Count - 1 do
1267begin
1268colors.addObject(AlphaColorToUIColor(ABrush.Gradient.Points[i].Color).CGColor);
1269Locations[i] := ABrush.Gradient.Points[i].Offset;
1270end;
1271
1272colorSpace := CGColorSpaceCreateDeviceRGB;
1273Gradient := CGGradientCreateWithColors(colorSpace, (colors as ILocalObject).GetObjectID, @Locations[0]);
1274
1275case ABrush.Gradient.Style of
1276// 线渐层
1277TGradientStyle.Linear:
1278begin
1279CGContextDrawLinearGradient(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);
1280end;
1281// 圆渐层
1282TGradientStyle.Radial:
1283begin
1284RCenter.Create(PointF(ABrush.Gradient.RadialTransform.RotationCenter.X * ARect.Width, ABrush.Gradient.RadialTransform.RotationCenter.Y * ARect.Height) + ARect.TopLeft);
1285CGContextDrawRadialGradient(GlobalCanvas, Gradient, RCenter, ARect.Width / 2, RCenter, 0, kCGGradientDrawsBeforeStartLocation or kCGGradientDrawsAfterEndLocation);
1286end;
1287end;
1288
1289CFRelease(colorSpace);
1290CFRelease(Gradient);
1291end;
1292
1293procedure TIOSNativeCanvas.ApplyStroke(const AStroke: TStrokeBrush; const ARect: TRectF; const AOpacity: Single);
1294var
1295Dash: TDashArray;
1296i: Integer;
1297LColor: TAlphaColorF;
1298R: TRectF;
1299StrokeDash: FMX.Graphics.TDashArray;
1300begin
1301if GlobalCanvas = nil then
1302Exit;
1303
1304if (AStroke.Kind = TBrushKind.Resource) and (AStroke.Resource <> nil) and (AStroke.Resource.Brush <> nil) then
1305AStroke.Assign(AStroke.Resource.Brush);
1306
1307case AStroke.Kind of
1308TBrushKind.Solid, TBrushKind.Bitmap:
1309begin
1310LColor := TAlphaColorF.Create(MakeColor(AStroke.Color, AOpacity));
1311CGContextSetRGBStrokeColor(GlobalCanvas, LColor.R, LColor.G, LColor.B, LColor.A);
1312end;
1313else
1314CGContextSetRGBStrokeColor(GlobalCanvas, 0, 0, 0, 0);
1315end;
1316
1317case AStroke.Cap of
1318TStrokeCap.Flat:
1319CGContextSetLineCap(GlobalCanvas, kCGLineCapButt);
1320TStrokeCap.Round:
1321CGContextSetLineCap(GlobalCanvas, kCGLineCapRound);
1322end;
1323
1324if Length(AStroke.DashArray) > 0 then
1325begin
1326// select the proper dash array for the printer
1327if TMyCanvas(FCanvas).FPrinter <> nil then
1328begin
1329if AStroke.Dash <> TStrokeDash.Custom then
1330StrokeDash := TStrokeBrush.StdDash[TStrokeBrush.TDashDevice.Printer, AStroke.Dash].DashArray
1331else
1332StrokeDash := AStroke.DashArray;
1333SetLength(Dash, Length(StrokeDash));
1334for I := 0 to High(StrokeDash) do
1335begin
1336Dash[I] := StrokeDash[I];
1337end;
1338end
1339else // adjust the line dashes for the screen
1340begin
1341SetLength(Dash, Length(AStroke.DashArray));
1342for i := 0 to High(AStroke.DashArray) do
1343begin
1344Dash[i] := AStroke.DashArray[i] * AStroke.Thickness;
1345if AStroke.Cap = TStrokeCap.Round then
1346begin
1347if Odd(i) then
1348Dash[i] := (AStroke.DashArray[i] + 1) * AStroke.Thickness
1349else
1350Dash[i] := (AStroke.DashArray[i] - 1) * AStroke.Thickness;
1351end;
1352end;
1353end;
1354CGContextSetLineDash(GlobalCanvas, AStroke.DashOffset, @Dash[0], Length(AStroke.DashArray));
1355end
1356else
1357CGContextSetLineDash(GlobalCanvas, 0, nil, 0);
1358
1359case AStroke.Join of
1360TStrokeJoin.Miter:
1361CGContextSetLineJoin(GlobalCanvas, kCGLineJoinMiter);
1362
1363TStrokeJoin.Round:
1364CGContextSetLineJoin(GlobalCanvas, kCGLineJoinRound);
1365
1366TStrokeJoin.Bevel:
1367CGContextSetLineJoin(GlobalCanvas, kCGLineJoinBevel);
1368end;
1369
1370CGContextSetLineWidth(GlobalCanvas, AStroke.Thickness);
1371
1372// 渐层
1373if (AStroke.Kind = TBrushKind.Gradient) and (CGContextIsPathEmpty(GlobalCanvas) = 0) then
1374begin
1375CGContextReplacePathWithStrokedPath(GlobalCanvas);
1376CGContextClip(GlobalCanvas);
1377R := ARect;
1378InflateRect(R, AStroke.Thickness / 2, AStroke.Thickness / 2);
1379ApplyGradient(AStroke, R);
1380end;
1381end;
1382
1383constructor TIOSNativeCanvas.Create(ACanvas: TCanvas);
1384begin
1385inherited;
1386FCanvas.Font.Family := NSStrToStr(iOSapi.Foundation.NSString(TUIFont.Wrap(TUIFont.OCClass.systemFontOfSize(14)).fontName));
1387end;
1388
1389procedure TIOSNativeCanvas.DrawBitmap(const ABitmap: TBitmap; const SrcRect,
1390DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean);
1391var
1392NativeImage: UIImage;
1393image: CGImageRef;
1394begin
1395if GlobalCanvas = nil then
1396Exit;
1397
1398NativeImage := BitmapToUIImage(ABitmap);
1399image := CGImageCreateWithImageInRect(NativeImage.CGImage,
1400CGRectMake(SrcRect.Left, SrcRect.Top, SrcRect.Width, SrcRect.Height));
1401CGContextSaveGState(GlobalCanvas);
1402CGContextSetAlpha(GlobalCanvas, AOpacity);
1403CGContextTranslateCTM(GlobalCanvas, 0, height);
1404CGContextScaleCTM(GlobalCanvas, 1.0, -1.0);
1405CGContextDrawImage(GlobalCanvas,
1406CGRectMake(DstRect.Left, Height - DstRect.Bottom, DstRect.Width, DstRect.Height),
1407image);
1408CGContextRestoreGState(GlobalCanvas);
1409end;
1410
1411procedure TIOSNativeCanvas.SetMatrix(const M: TMatrix);
1412var
1413LMatrix: TMatrix;
1414Transform: CGAffineTransform;
1415begin
1416if GlobalCanvas <> nil then
1417begin
1418Transform := CGContextGetCTM(GlobalCanvas);
1419LMatrix := TMatrix.Identity;
1420LMatrix.m11 := Transform.a;
1421LMatrix.m12 := Transform.b;
1422LMatrix.m21 := Transform.c;
1423LMatrix.m22 := Transform.d;
1424LMatrix.m31 := Transform.tx;
1425LMatrix.m32 := Transform.ty;
1426LMatrix := LMatrix.Inverse;
1427
1428CGContextConcatCTM(GlobalCanvas, CGAffineTransformMake(LMatrix.m11, LMatrix.m12, LMatrix.m21, LMatrix.m22, LMatrix.m31,
1429LMatrix.m32));
1430// AdaptCoordinateSystem;
1431CGContextConcatCTM(GlobalCanvas, CGAffineTransformMake(M.m11, M.m12, M.m21, M.m22, M.m31, M.m32));
1432end;
1433end;
1434
1435procedure TIOSNativeCanvas.DrawFill(const ABrush: TBrush; const SrcRect, DesRect: TRectF; const AOpacity: Single);
1436begin
1437if ABrush.Kind = TBrushKind.Bitmap then
1438begin
1439// 未完成
1440end
1441else
1442begin
1443ApplyFill(ABrush, DesRect, AOpacity);
1444CGContextEOFillPath(GlobalCanvas);
1445end;
1446end;
1447
1448procedure TIOSNativeCanvas.DrawLine(const APt1, APt2: TPointF; const AOpacity: Single; const ABrush: TStrokeBrush);
1449var
1450R: TRectF;
1451begin
1452if GlobalCanvas = nil then
1453Exit;
1454
1455if ABrush.Kind <> TBrushKind.None then
1456begin
1457CGContextSaveGState(GlobalCanvas);
1458CGContextBeginPath(GlobalCanvas);
1459
1460CGContextMoveToPoint(GlobalCanvas, APt1.X, APt1.Y);
1461CGContextAddLineToPoint(GlobalCanvas, APt2.X, APt2.Y);
1462
1463// 加上线粗
1464R := TRectF.Create(APt1.X, APt1.Y, APt2.X, APt2.Y);
1465InflateRect(R, ABrush.Thickness / 2, ABrush.Thickness / 2);
1466
1467ApplyStroke(ABrush, R, AOpacity);
1468CGContextStrokePath(GlobalCanvas);
1469
1470CGContextRestoreGState(GlobalCanvas);
1471end;
1472end;
1473
1474procedure TIOSNativeCanvas.DrawPath(const APath: TPathData; const AOpacity: Single; const AFill: TBrush; const AStroke: TStrokeBrush);
1475procedure ShowPath;
1476var
1477i: Integer;
1478CurvePoint1, CurvePoint2: TPointF;
1479begin
1480i := 0;
1481while i < APath.Count do
1482begin
1483case APath[i].Kind of
1484TPathPointKind.MoveTo:
1485CGContextMoveToPoint(GlobalCanvas, APath[i].Point.X, APath[i].Point.Y);
1486TPathPointKind.LineTo:
1487CGContextAddLineToPoint(GlobalCanvas, APath[i].Point.X, APath[i].Point.Y);
1488TPathPointKind.CurveTo:
1489begin
1490CurvePoint1 := APath[i].Point;
1491Inc(i);
1492CurvePoint2 := APath[i].Point;
1493Inc(i);
1494CGContextAddCurveToPoint(GlobalCanvas, CurvePoint1.X, CurvePoint1.Y, CurvePoint2.X, CurvePoint2.Y, APath[i].Point.X, APath[i].Point.Y);
1495end;
1496TPathPointKind.Close:
1497CGContextClosePath(GlobalCanvas);
1498end;
1499
1500Inc(i);
1501end;
1502end;
1503
1504var
1505SrcRect: TRectF;
1506begin
1507if GlobalCanvas = nil then
1508Exit;
1509
1510SrcRect := APath.GetBounds;
1511
1512// 涂色
1513if (AFill <> nil) and (AFill.Kind <> TBrushKind.None) then
1514begin
1515CGContextSaveGState(GlobalCanvas);
1516CGContextBeginPath(GlobalCanvas);
1517ShowPath;
1518DrawFill(AFill, SrcRect, SrcRect, AOpacity);
1519CGContextRestoreGState(GlobalCanvas);
1520end;
1521
1522// 画线
1523if (AStroke <> nil) and (AStroke.Kind <> TBrushKind.None) then
1524begin
1525CGContextSaveGState(GlobalCanvas);
1526CGContextBeginPath(GlobalCanvas);
1527ShowPath;
1528ApplyStroke(AStroke, SrcRect, AOpacity);
1529CGContextStrokePath(GlobalCanvas);
1530CGContextRestoreGState(GlobalCanvas);
1531end;
1532end;
1533
1534procedure TIOSNativeCanvas.ExcludeClipRect(const ARect: TRectF);
1535var
1536LRect: array [0 .. 3] of CGRect;
1537begin
1538if GlobalCanvas <> nil then
1539begin
1540LRect[0] := CGRectFromRect(TRectF.Create(-FCanvas.Width, -FCanvas.Width, ARect.Left, FCanvas.Height));
1541LRect[1] := CGRectFromRect(TRectF.Create(ARect.Right, -FCanvas.Height, FCanvas.Width, FCanvas.Height));
1542LRect[2] := CGRectFromRect(TRectF.Create(ARect.Left, -FCanvas.Height, ARect.Right, ARect.Top));
1543LRect[3] := CGRectFromRect(TRectF.Create(ARect.Left, ARect.Bottom, ARect.Right, FCanvas.Height));
1544CGContextClipToRects(GlobalCanvas, @LRect[0], 4);
1545end;
1546end;
1547
1548function NSSTR(Str: string): NSString;
1549var
1550M: TMarshaller;
1551begin
1552Result := TNSString.Wrap(TNSString.OCClass.stringWithUTF8String(M.AsAnsi(Str, CP_UTF8).ToPointer));
1553end;
1554
1555function TIOSNativeCanvas.GetPostScriptFontName: CFStringRef;
1556var
1557LUIFont: UIFont;
1558LocalObject: ILocalObject;
1559begin
1560Result := nil;
1561LUIFont := TUIFont.Wrap(TUIFont.OCClass.fontWithName(NSString(StrToNSStr(FCanvas.Font.Family)), FCanvas.Font.Size));
1562if Supports(LUIFont, ILocalObject, LocalObject) then
1563Result := CTFontCopyPostScriptName(LocalObject.GetObjectID);
1564if 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
1566Result := CFSTR(FCanvas.Font.Family);
1567end;
1568
1569procedure TIOSNativeCanvas.FillText(const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags; const ATextAlign, AVTextAlign: TTextAlign);
1570const
1571//Rotating matrix to simulate Italic font attribute
1572ItalicMatrix: CGAffineTransform = (
1573a: 1;
1574b: 0;
1575c: 0.176326981; //~tan(10 degrees)
1576d: 1;
1577tx: 0;
1578ty: 0
1579);
1580var
1581NS: NSString;
1582dic: NSMutableDictionary;
1583ps: NSMutableParagraphStyle;
1584f: UIFont;
1585sz: CGSize;
1586tr: NSRect;
1587font, NewFontRef: CTFontRef;
1588ftName: NSString;
1589desc: UIFontDescriptor;
1590begin
1591if GlobalCanvas = nil then
1592Exit;
1593
1594font := CTFontCreateWithName(GetPostScriptFontName, FCanvas.Font.Size, nil);
1595try
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;
1607if TFontStyle.fsBold in FCanvas.Font.Style then
1608begin
1609NewFontRef := CTFontCreateCopyWithSymbolicTraits(font, FCanvas.Font.Size, nil, kCTFontBoldTrait, kCTFontBoldTrait);
1610if NewFontRef <> nil then
1611begin
1612CFRelease(font);
1613font := NewFontRef;
1614end;
1615end;
1616ftName := TNSString.wrap(CTFontCopyPostScriptName(font));
1617except
1618CFRelease(font);
1619ftName := NSStr(FCanvas.Font.Family);
1620end;
1621
1622NS := NSSTR(AText);
1623
1624dic := TNSMutableDictionary.Wrap(TNSMutableDictionary.Wrap(TNSMutableDictionary.OCClass.alloc).init);
1625if TFontStyle.fsItalic in FCanvas.Font.Style then
1626begin
1627desc := TUIFontDescriptor.OCClass.fontDescriptorWithNameMatrix(iOSapi.Foundation.NSString(ftName), ItalicMatrix);
1628F := TUIFont.Wrap((TUIFont.OCClass.fontWithDescriptor(desc, FCanvas.Font.Size)));
1629end
1630else
1631f := TUIFont.Wrap(TUIFont.OCClass.fontWithName(ftName, FCanvas.Font.Size));
1632
1633ps := TNSMutableParagraphStyle.Wrap(TNSMutableParagraphStyle.Wrap(TNSMutableParagraphStyle.OCClass.alloc).init);
1634case ATextAlign of
1635TTextAlign.Center: ps.setAlignment(UITextAlignmentCenter);
1636TTextAlign.Leading: ps.setAlignment(UITextAlignmentLeft);
1637TTextAlign.Trailing: ps.setAlignment(UITextAlignmentRight);
1638end;
1639ps.setLineBreakMode(NSLineBreakByTruncatingTail);
1640
1641dic.setValue((f as ILocalObject).GetObjectID, NSFontAttributeName);
1642dic.setValue((AlphaColorToUIColor(FCanvas.Fill.Color) as ILocalObject).GetObjectID, NSForegroundColorAttributeName);
1643dic.setValue((ps as ILocalObject).GetObjectID, NSParagraphStyleAttributeName);
1644if (TFontStyle.fsUnderline in FCanvas.Font.Style) or (TFontStyle.fsStrikeOut in FCanvas.Font.Style) then
1645begin
1646if TFontStyle.fsUnderline in FCanvas.Font.Style then
1647begin
1648dic.setValue(TNSNumber.OCClass.numberWithInt(NSUnderlineStyleSingle), NSUnderlineStyleAttributeName);
1649end;
1650if TFontStyle.fsStrikeOut in FCanvas.Font.Style then
1651begin
1652dic.setValue(TNSNumber.OCClass.numberWithInt(NSUnderlineStyleSingle), NSStrikethroughStyleAttributeName);
1653end;
1654dic.setValue(TNSNumber.OCClass.numberWithInt(0), NSBaselineOffsetAttributeName);
1655end;
1656
1657sz := NS.sizeWithAttributes(dic);
1658
1659case AVTextAlign of
1660TTextAlign.Center:
1661tr := NSRect.Create(ARect.Left, ARect.Top + (ARect.Height - sz.height)/ 2, ARect.Width, (ARect.Height - sz.height) / 2);
1662TTextAlign.Leading:
1663tr := NSRect.Create(ARect);
1664TTextAlign.Trailing:
1665tr := NSRect.Create(ARect.Left, ARect.Bottom - sz.height, ARect.Width, sz.height);
1666end;
1667
1668NS.drawInRect(tr, dic);
1669end;
1670
1671procedure TIOSNativeCanvas.IntersectClipRect(const ARect: TRectF);
1672begin
1673if GlobalCanvas <> nil then
1674CGContextClipToRect(GlobalCanvas, CGRectFromRect(ARect));
1675end;
1676
1677function MyUIImageToBitmap(const AImage: UIImage; const ARotate: Single; const AMaxSize: TSize): TBitmap;
1678
1679function ReduceImageSize(const AOriginalSize: TSize): TSize;
1680var
1681ImageRatio: Double;
1682ScaleCoef: Double;
1683MinWidth: Integer;
1684MinHeight: Integer;
1685MaxBitmapSize: Integer;
1686begin
1687Result := AOriginalSize;
1688MinWidth := Min(AOriginalSize.Width, AMaxSize.Width);
1689MinHeight := Min(AOriginalSize.Height, AMaxSize.Height);
1690ImageRatio := AOriginalSize.Width / AOriginalSize.Height;
1691if MinWidth / MinHeight < ImageRatio then
1692Result := TSize.Create(MinWidth, Round(MinWidth / ImageRatio))
1693else
1694Result := TSize.Create(Round(MinHeight * ImageRatio), MinHeight);
1695
1696MaxBitmapSize := TCanvasManager.DefaultCanvas.GetAttribute(TCanvasAttribute.MaxBitmapSize);
1697if (MaxBitmapSize > 0) and (Max(AOriginalSize.cx, AOriginalSize.cy) div MaxBitmapSize > 0) then
1698begin
1699ScaleCoef := Max(AOriginalSize.cx, AOriginalSize.cy) / MaxBitmapSize;
1700Result := TSize.Create(Round(AOriginalSize.cx / ScaleCoef), Round(AOriginalSize.cy / ScaleCoef));
1701end;
1702end;
1703
1704var
1705ImageRef: CGImageRef;
1706Bitmap: TBitmap;
1707CtxRef: CGContextRef;
1708ColorSpace: CGColorSpaceRef;
1709Data: TBitmapData;
1710BitmapSize: TSize;
1711begin
1712ImageRef := AImage.CGImage;
1713if ImageRef <> nil then
1714begin
1715BitmapSize := ReduceImageSize(TSize.Create(CGImageGetWidth(ImageRef), CGImageGetHeight(ImageRef)));
1716Bitmap := TBitmap.Create(BitmapSize.cx, BitmapSize.cy);
1717Bitmap.Clear(0);
1718ColorSpace := CGColorSpaceCreateDeviceRGB;
1719try
1720if Bitmap.Map(TMapAccess.Write, Data) then
1721try
1722CtxRef := CGBitmapContextCreate(Data.Data, Bitmap.Width, Bitmap.Height, 8, Data.Pitch, ColorSpace,
1723kCGImageAlphaPremultipliedLast or kCGBitmapByteOrder32Big);
1724try
1725CGContextDrawImage(CtxRef, CGRectMake(0, 0, Bitmap.Width, BitMap.Height), ImageRef);
1726finally
1727CGContextRelease(CtxRef);
1728end;
1729finally
1730Bitmap.Unmap(Data);
1731end;
1732finally
1733CGColorSpaceRelease(ColorSpace);
1734end;
1735Bitmap.Rotate(ARotate);
1736Result := Bitmap;
1737end
1738else
1739Result := nil;
1740end;
1741
1742procedure TIOSNativeCanvas.NativeDraw(const ARect: TRectF; const ADrawProc: TDrawProc);
1743var
1744NativeImage: UIImage;
1745Bitmap: TBitmap;
1746begin
1747NativeImage := nil;
1748
1749UIGraphicsBeginImageContextWithOptions(CGSizeMake(ARect.Width, ARect.Height), False, 0 { 目前机子Scale } );
1750
1751GlobalCanvas := UIGraphicsGetCurrentContext;
1752
1753CGContextSaveGState(GlobalCanvas);
1754
1755// 透明底色
1756TUIColor.Wrap(TUIColor.OCClass.colorWithHue(0, 0, 1, 0)).setFill;
1757CGContextFillRect(GlobalCanvas, CGContextGetClipBoundingBox(GlobalCanvas));
1758
1759// 绘图函数
1760if Assigned(ADrawProc) then
1761ADrawProc;
1762
1763CGContextRestoreGState(GlobalCanvas);
1764
1765NativeImage := TUIImage.Wrap(UIGraphicsGetImageFromCurrentImageContext);
1766
1767if Assigned(NativeImage) then
1768begin
1769Bitmap := MyUIImageToBitmap(NativeImage, 0, NativeImage.size.ToSizeF.Round);
1770// 显示
1771FCanvas.DrawBitmap(Bitmap, RectF(0, 0, Bitmap.Width, Bitmap.Height), ARect, 1);
1772FreeAndNil(Bitmap);
1773end;
1774
1775UIGraphicsEndImageContext;
1776end;
1777
1778{$ENDIF}
1779
1780end.
1781