   {%mainunit orca_scene2d.pas}

{*************************************************************************
                Copyright (c) PilotLogic Software House

  Package pl_ORCA
  This file is part of CodeTyphon Studio (https://www.pilotlogic.com/)

 ***** BEGIN LICENSE BLOCK *****
 * Version: LGPL-2.1 with link exception (Modified LGPL)
 *
 * The contents of this file are subject to the 
 * GNU LIBRARY GENERAL PUBLIC LICENSE Version 2.1 (the "License")
 * with the following modification:
 * 
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules,
 * and to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * ***** END LICENSE BLOCK *****
 
***************************************************************************}


Type

   TD2FilterDarwin = Class(TD2Filter)
   Published
      Class Function GetFileTypes: String; Override;
      Class Function GetImageSize(Const AFileName: String): TD2Point; Override;
      Function LoadThumbnailFromFile(Const AFileName: String; Const AFitWidth, AFitHeight: Single; Const UseEmbedded: Boolean; Var Bitmap: TD2Bitmap): Boolean; Override;
      Function LoadFromFile(Const AFileName: String; Const Rotate: Single; Var Bitmap: TD2Bitmap): Boolean; Override;
      Function SaveToFile(Const AFileName: String; Var Bitmap: TD2Bitmap; Const Params: String = ''): Boolean; Override;
      Function LoadFromStream(Const AStream: TStream; Var Bitmap: TD2Bitmap): Boolean; Override;
      Function SaveToStream(Const AStream: TStream; Var Bitmap: TD2Bitmap; Const Format: String; Const Params: String = ''): Boolean; Override;
   End;


   TD2CanvasDarwin = Class(TD2Canvas)
   Private
      fFunc: CGFunctionRef;
      fBmpRef: CGImageRef;
      fCallback: CGFunctionCallbacks;
      fShading: CGShadingRef;
      fColorSpace: CGColorSpaceRef;
   Protected
      Procedure ApplyFill(ARect: TD2Rect; Const AOpacity: Single);
      Procedure DeApplyFill(ARect: TD2Rect; Const AOpacity: Single);
      Procedure ApplyStroke(ARect: TD2Rect; Const AOpacity: Single);
      Procedure FontChanged(Sender: TObject); Override;
      Procedure UpdateBitmap(ABitmap: TD2Bitmap);
      Procedure DoDestroyBitmap(Sender: TObject);
   Public
      Constructor Create(Const AWidth, AHeight: Integer); Override;
      Constructor CreateFromBitmap(Const ABitmap: TD2Bitmap); Override;
      Destructor Destroy; Override;
      Procedure FreeBuffer; Override;
      Procedure ResizeBuffer(Const AWidth, AHeight: Integer); Override;
      Procedure FlushBuffer(Const X, Y: Integer; Const DC: Cardinal); Override;
      Procedure FlushBufferRect(Const X, Y: Integer; Const DC: Cardinal; Const ARect: TD2Rect); Override;
      Procedure Clear(Const Color: Cardinal); Override;
      Procedure ClearRect(Const ARect: TD2Rect; Const AColor: TD2Color = 0); Override;
      Class Function GetBitmapScanline(Bitmap: TD2Bitmap; y: Integer): PD2ColorArray; Override;
      Procedure SetMatrix(Const M: TD2Matrix); Override;
      Procedure MultyMatrix(Const M: TD2Matrix); Override;
      Function SaveCanvas: Cardinal; Override;
      Procedure RestoreCanvas(Const AState: Cardinal); Override;
      Procedure SetClipRects(Const ARects: Array Of TD2Rect); Override;
      Procedure IntersectClipRect(Const ARect: TD2Rect); Override;
      Procedure ExcludeClipRect(Const ARect: TD2Rect); Override;
      Procedure ResetClipRect; Override;
      Procedure DrawLine(Const APt1, APt2: TD2Point; Const AOpacity: Single); Override;
      Procedure FillRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound); Override;
      Procedure DrawRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound); Override;
      Procedure FillEllipse(Const ARect: TD2Rect; Const AOpacity: Single); Override;
      Procedure DrawEllipse(Const ARect: TD2Rect; Const AOpacity: Single); Override;
      Function PtInPath(Const APoint: TD2Point; Const ARect: TD2Rect; Const APath: TD2PathData): Boolean; Override;
      Procedure FillPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single); Override;
      Procedure DrawPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single); Override;
      Procedure DrawBitmap(Const ABitmap: TD2Bitmap; Const SrcRect, DstRect: TD2Rect; Const AOpacity: Single; Const HighSpeed: Boolean = False); Override;
      Procedure FillText(Const ARect, AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const AOpacity: Single; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter); Override;
      Procedure MeasureText(Var ARect: TD2Rect; AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter); Override;
      Function TextToPath(Path: TD2PathData; Const ARect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter): Boolean; Override;

   End;

   //==================================================================================

Const
   cnInputRange: Array [1..2] Of Single = (0, 1);

Var
   varSOpacity: Single;

Type

   TATSLayoutRec = Packed Record
      glyphID: ATSGlyphRef;
      flags: ATSGlyphInfoFlags;
      originalOffset: ByteCount;
      realPos: Fixed;
   End;

   TATSLayoutRecArray = Array Of TATSLayoutRec;
   TATSLayoutRecArray2 = Array [0..0] Of TATSLayoutRec;
   PATSLayoutRecArray2 = ^TATSLayoutRecArray2;
   TYOffsets = Array [0..0] Of Fixed;
   PYOffsets = ^TYOffsets;

   TText3DCallback = Packed Record
      Path: TD2PathData;
      SP: ^TD2Point;
      GlyphLayouts: TATSLayoutRec;
      Height: Single;
      XOffset, YOffset: Single;
   End;

Type

   TRGBFloat = Packed Record
      r, g, b, a: Single;
   End;

Function CarbonColor(Const C: TD2Color; Opacity: Single = 1): TRGBFloat;
Var
   cc: TD2Color;
Begin
   cc := d2Opacity(C, Opacity);
   Result.a := TD2ColorRec(cc).a / $FF;
   Result.r := TD2ColorRec(cc).r / $FF;
   Result.g := TD2ColorRec(cc).g / $FF;
   Result.b := TD2ColorRec(cc).b / $FF;
End;

Function GetMacRectFromRect(Const R: TD2Rect): CGRect;
Var
   xr: CGRect;
Begin
   xr.size.Width := R.Right - R.Left;
   xr.size.Height := R.Bottom - R.Top;
   xr.origin.x := R.Left;
   xr.origin.Y := R.Top;
   Result := xr;
End;

Procedure CarbonContextDrawTiledImage(CtxRef: CGContextRef; ARect: TD2Rect; Img: CGImageRef; Bitmap: TD2Bitmap);
Var
   x, y: Integer;
   CR: CGRect;
Begin
   If Bitmap.Width * Bitmap.Height = 0 Then
      Exit;
   For x := 0 To Trunc(d2RectWidth(ARect) / Bitmap.Width) Do
      For y := 0 To Trunc(d2RectHeight(ARect) / Bitmap.Height) Do
      Begin
         CR := GetMacRectFromRect(d2Rect(ARect.Left + (Bitmap.Width * x), ARect.Top + (Bitmap.Height * y), ARect.Left + (Bitmap.Width * (x + 1)), ARect.Top + (Bitmap.Height * (y + 1))));
         CR.origin.y := -ARect.Top - (Bitmap.Height * (y + 1));
         CGContextDrawImage(CtxRef, CR, Img);
      End;
End;

Function exCubicLineToProc(Const pt: Float32Point; callBackDataPtr: Pointer): OSStatus; Stdcall;
Var
   xG: TText3DCallback;
Begin
   xG := TText3DCallback(callBackDataPtr^);
   xG.Path.LineTo(d2Point(xG.XOffset + (xG.GlyphLayouts.realPos / $FFFF) + pt.x, xG.YOffset + pt.y));
   Result := noErr;
End;

Function exCubicMoveToProc(Const pt: Float32Point; callBackDataPtr: Pointer): OSStatus; Stdcall;
Var
   xG: TText3DCallback;
Begin
   xG := TText3DCallback(callBackDataPtr^);
   xG.SP^ := d2Point(xG.XOffset + (xG.GlyphLayouts.realPos / $FFFF) + pt.x, xG.YOffset + pt.y);
   xG.Path.MoveTo(d2Point(xG.XOffset + (xG.GlyphLayouts.realPos / $FFFF) + pt.x, xG.YOffset + pt.y));
   Result := noErr;
End;

Function exCubicCurveToProc(Const pt, pt2, pt3: Float32Point; callBackDataPtr: Pointer): OSStatus; Stdcall;
Var
   xG: TText3DCallback;
Begin
   xG := TText3DCallback(callBackDataPtr^);
   xG.Path.CurveTo(d2Point(xG.XOffset + (xG.GlyphLayouts.realPos / $FFFF) + pt.x, xG.YOffset + pt.y),
      d2Point(xG.XOffset + (xG.GlyphLayouts.realPos / $FFFF) + pt2.x, xG.YOffset + pt2.y),
      d2Point(xG.XOffset + (xG.GlyphLayouts.realPos / $FFFF) + pt3.x, xG.YOffset + pt3.y));
   Result := noErr;
End;

Function exCubicClosePathProc(callBackDataPtr: Pointer): OSStatus; Stdcall;
Var
   xG: TText3DCallback;
Begin
   xG := TText3DCallback(callBackDataPtr^);
   xG.Path.ClosePath;
   Result := noErr;
End;

Procedure exLinearShadingValues(info: Pointer; inp: Float32Ptr; val: Float32Ptr); Cdecl;
Var
   c: TD2Color;
Begin
   If info <> nil Then
   Begin
      c := d2Opacity(TD2Gradient(info).InterpolateColor(inp^), varSOpacity);
      val^ := TD2ColorRec(c).R / $FF;
      Inc(val);
      val^ := TD2ColorRec(c).G / $FF;
      Inc(val);
      val^ := TD2ColorRec(c).B / $FF;
      Inc(val);
      val^ := TD2ColorRec(c).A / $FF;
      Inc(val);
   End;
End;

Procedure GetGlyphIDsAndPositions(iLayout: ATSUTextLayout; iStart: UniCharArrayOffset; iLength: UniCharCount; Var oGlyphRecordArray: TATSLayoutRecArray; Var oNumGlyphs: ItemCount);
Var
   i: Integer;
   xnumDeltaYs: ItemCount;
   xdeltaYs: PYOffsets;
   xGftRecordArray: PATSLayoutRecArray2;
Begin
   ATSUDirectGetLayoutDataArrayPtrFromTextLayout(iLayout, iStart, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, @xGftRecordArray, oNumGlyphs);
   ATSUDirectGetLayoutDataArrayPtrFromTextLayout(iLayout, iStart, kATSUDirectDataBaselineDeltaFixedArray, @xdeltaYs, xnumDeltaYs);

   SetLength(oGlyphRecordArray, oNumGlyphs);
   For i := 0 To oNumGlyphs - 1 Do
   Begin
      oGlyphRecordArray[i] := xGftRecordArray[i];
   End;

   If (xdeltaYs <> nil) Then
      ATSUDirectReleaseLayoutDataArrayPtr(nil, kATSUDirectDataBaselineDeltaFixedArray, @xdeltaYs);
   ATSUDirectReleaseLayoutDataArrayPtr(nil, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, @xGftRecordArray);
End;

Function streamPutBytesCallback(info: Pointer; buffer: Pointer; Count: size_t): size_t; Cdecl;
Begin
   TStream(info).Write(buffer^, Count);
End;

Procedure streamReleaseInfoCallback(info: Pointer);
Begin
   //  TMemoryStream(info).Free;
End;

//=============== TD2FilterDarwin ==================================================


Class Function TD2FilterDarwin.GetFileTypes: String;
Begin
   Result := '*.jpg;*.jpeg;*.gif;*.tif;*.tiff;*.psd;*.jp2;*.png;*.bmp;*.tga;*.icns';
End;

Class Function TD2FilterDarwin.GetImageSize(Const AFileName: String): TD2Point;
Var
   ximgSrcRef: CGImageSourceRef;
   ximgRef: CGImageRef;
   xStrRef: CFStringRef;
   xURLRef: CFURLRef;
Begin
   Result := d2Point(0, 0);
   xStrRef := CFStringCreateWithCString(nil, PChar(AFileName), kCFStringEncodingUTF8);
   xURLRef := CFURLCreateWithFileSystemPath(nil, xStrRef, kCFURLPOSIXPathStyle, False);

   CFRelease(xStrRef);
   ximgSrcRef := CGImageSourceCreateWithURL(xURLRef, nil);
   CFRelease(xURLRef);

   If ximgSrcRef <> nil Then
   Begin
      xImgRef := CGImageSourceCreateImageAtIndex(ximgSrcRef, 0, nil);

      If xImgRef <> nil Then
      Begin
         Result := d2Point(CGImageGetWidth(xImgRef), CGImageGetHeight(xImgRef));
         CGImageRelease(ximgRef);
      End;

      CFRelease(ximgSrcRef);
   End;
End;


Function TD2FilterDarwin.LoadThumbnailFromFile(Const AFileName: String; Const AFitWidth, AFitHeight: Single; Const UseEmbedded: Boolean;
   Var Bitmap: TD2Bitmap): Boolean;
Var
   xintValue: Cardinal;
   xPointerKeys: Array [0..10] Of pointer;
   xPointerValue: Array [0..10] Of pointer;
   xStrRef: CFStringRef;
   xURLRef: CFURLRef;
   ximgSrcRef: CGImageSourceRef;
   ximgRef: CGImageRef;
   xContextRef: CGContextRef;
   xDicRef: CFDictionaryRef;
Begin
   Result := False;
   xStrRef := CFStringCreateWithCString(nil, PChar(AFileName), kCFStringEncodingUTF8);
   xURLRef := CFURLCreateWithFileSystemPath(nil, xStrRef, kCFURLPOSIXPathStyle, False);
   CFRelease(xStrRef);
   ximgSrcRef := CGImageSourceCreateWithURL(xURLRef, nil);
   CFRelease(xURLRef);
   If ximgSrcRef <> nil Then
   Begin
      xPointerKeys[0] := kCGImageSourceCreateThumbnailFromImageAlways;
      xPointerValue[0] := kCFBooleanTrue;
      xPointerKeys[1] := kCGImageSourceThumbnailMaxPixelSize;

      If AFitWidth > AFitHeight Then
         xintValue := trunc(AFitWidth)
      Else
         xintValue := trunc(AFitHeight);

      xPointerValue[1] := CFNumberCreate(nil, kCFNumberSInt32Type, @xintValue);
      xDicRef := CFDictionaryCreate(nil, @xPointerKeys[0], @xPointerValue[0], 2, nil, nil);
      xImgRef := CGImageSourceCreateThumbnailAtIndex(ximgSrcRef, 0, xDicRef);
      CFRelease(xDicRef);
      If xImgRef <> nil Then
      Begin
         Bitmap.SetSize(CGImageGetWidth(xImgRef), CGImageGetHeight(xImgRef));
         xContextRef := CGBitmapContextCreate(Bitmap.StartLine, Bitmap.Width, Bitmap.Height, 8, Bitmap.Width * 4, CGColorSpaceCreateDeviceRGB, kCGImageAlphaPremultipliedLast);
         CGContextDrawImage(xContextRef, GetMacRectFromRect(d2Rect(0, 0, Bitmap.Width, Bitmap.Height)), ximgRef);
         CGContextRelease(xContextRef);
         CGImageRelease(ximgRef);
         Result := True;
      End;
      CFRelease(ximgSrcRef);
   End;
End;

Function TD2FilterDarwin.LoadFromStream(Const AStream: TStream; Var Bitmap: TD2Bitmap): Boolean;
Var
   ximgRef: CGImageRef;
   xContextRef: CGContextRef;
   xprovider: CGDataProviderRef;
   xmemStream: TMemoryStream;
   ximgSrcRef: CGImageSourceRef;
   xcolorspace: CGColorSpaceRef;
Begin
   xmemStream := TMemoryStream.Create;
   xmemStream.CopyFrom(AStream, AStream.Size);
   xmemStream.Position := 0;
   xprovider := CGDataProviderCreateWithData(nil, xmemStream.Memory, xmemStream.Size, nil);

   If xprovider <> nil Then
   Begin
      ximgSrcRef := CGImageSourceCreateWithDataProvider(xprovider, nil);
      If ximgSrcRef <> nil Then
      Begin
         xImgRef := CGImageSourceCreateImageAtIndex(ximgSrcRef, 0, nil);

         If xImgRef <> nil Then
         Begin
            Bitmap.SetSize(CGImageGetWidth(xImgRef), CGImageGetHeight(xImgRef));
            xcolorspace := CGColorSpaceCreateDeviceRGB;
            xContextRef := CGBitmapContextCreate(Bitmap.StartLine, Bitmap.Width, Bitmap.Height, 8,
               Bitmap.Width * 4, CGColorSpaceCreateDeviceRGB, kCGImageAlphaPremultipliedLast);
            CGContextDrawImage(xContextRef, GetMacRectFromRect(d2Rect(0, 0, Bitmap.Width, Bitmap.Height)), ximgRef);
            CGContextRelease(xContextRef);
            CGColorSpaceRelease(xcolorspace);
            CGImageRelease(ximgRef);
         End;
         CFRelease(ximgSrcRef);
      End;

      CGDataProviderRelease(xprovider);
   End;
   xmemStream.Free;
End;

Function TD2FilterDarwin.SaveToStream(Const AStream: TStream; Var Bitmap: TD2Bitmap; Const Format: String; Const Params: String = ''): Boolean;
Var
   S, xparam, xname, xval: String;
   xfloatVal: Single;
   xPointerKeys: Array [0..10] Of pointer;
   xPointerValue: Array [0..10] Of pointer;
   xconsumer: CGDataConsumerRef;
   ximgDestRef: CGImageDestinationRef;
   ximgRef: CGImageRef;
   xContextRef: CGContextRef;
   xEncType: CFStringRef;
   xcallback: CGDataConsumerCallbacks;
   xcolorspace: CGColorSpaceRef;
   xDicRef: CFDictionaryRef;
Begin
   xcallback.putBytes := @streamPutBytesCallback;
   xcallback.releaseConsumer := @streamReleaseInfoCallback;
   xconsumer := CGDataConsumerCreate(AStream, xcallback);

   xEncType := kUTTypePng;
   If (LowerCase(Format) = 'jpg') Or (LowerCase(Format) = 'jpeg') Then
      xEncType := kUTTypeJpeg;

   If (LowerCase(Format) = 'jp2') Then
      xEncType := kUTTypeJPEG2000;

   If (LowerCase(Format) = 'bmp') Then
      xEncType := kUTTypeBmp;

   If (LowerCase(Format) = 'png') Then
      xEncType := kUTTypePng;

   If (LowerCase(Format) = 'tif') Or (LowerCase(Format) = 'tiff') Then
      xEncType := kUTTypeTiff;

   If (LowerCase(Format) = 'gif') Then
      xEncType := kUTTypeGif;

   ximgDestRef := CGImageDestinationCreateWithDataConsumer(xconsumer, xEncType, 1, nil);
   If ximgDestRef <> nil Then
   Begin
      xcolorSpace := CGColorSpaceCreateDeviceRGB;

      xContextRef := CGBitmapContextCreate(Bitmap.StartLine, Bitmap.Width, Bitmap.Height, 8, Bitmap.Width *
         4, xcolorSpace, kCGImageAlphaPremultipliedLast);

      ximgRef := CGBitmapContextCreateImage(xContextRef);
      If ximgRef <> nil Then
      Begin
         xDicRef := nil;

         If Params <> '' Then
         Begin
            S := Params;
            While S <> '' Do
            Begin
               xparam := d2GetToken(S, ' ');
               xname := d2GetToken(xparam, '=');
               xval := d2GetToken(xparam, '');
               If SysUtils.CompareText(xname, 'quality') = 0 Then
               Begin
                  xfloatVal := strToFloat(xval) / 100;
                  xPointerKeys[0] := kCGImageDestinationLossyCompressionQuality;
                  xPointerValue[0] := CFNumberCreate(nil, kCFNumberFloat32Type, @xfloatVal);
                  xDicRef := CFDictionaryCreate(nil, @xPointerKeys[0], @xPointerValue[0], 1, nil, nil);
                  CGImageDestinationSetProperties(ximgDestRef, xDicRef);
               End;
            End;
         End;

         CGImageDestinationAddImage(ximgDestRef, ximgRef, xDicRef);
         CGImageDestinationFinalize(ximgDestRef);

         If xDicRef <> nil Then
            CFRelease(xDicRef);
      End;
      CGImageRelease(ximgRef);
      CGContextRelease(xContextRef);
      CGColorSpaceRelease(xcolorspace);

      CFRelease(ximgDestRef);
   End;
End;


Function TD2FilterDarwin.LoadFromFile(Const AFileName: String; Const Rotate: Single; Var Bitmap: TD2Bitmap): Boolean;
Var
   M, M2: TD2Matrix;
   R: TD2Rect;
   xPts: Array [1..4] Of TD2Point;
   xStrRef: CFStringRef;
   xURLRef: CFURLRef;
   ximgSrcRef: CGImageSourceRef;
   ximgRef: CGImageRef;
   xContextRef: CGContextRef;
Begin
   Result := False;
   xStrRef := CFStringCreateWithCString(nil, PChar(AFileName), kCFStringEncodingUTF8);
   xURLRef := CFURLCreateWithFileSystemPath(nil, xStrRef, kCFURLPOSIXPathStyle, False);
   CFRelease(xStrRef);
   ximgSrcRef := CGImageSourceCreateWithURL(xURLRef, nil);
   CFRelease(xURLRef);
   If ximgSrcRef <> nil Then
   Begin
      xImgRef := CGImageSourceCreateImageAtIndex(ximgSrcRef, 0, nil);
      If xImgRef <> nil Then
      Begin
         If Rotate <> 0.0 Then
         Begin
            M := IdentityMatrix;
            M.m31 := -(CGImageGetWidth(xImgRef) / 2);
            M.m32 := -(CGImageGetHeight(xImgRef) / 2);
            M := d2MatrixMultiply(M, d2CreateRotationMatrix(d2DegToRad(-Rotate)));

            xPts[1] := d2PointFromVector(d2VectorTransform(d2Vector(0, 0), M));
            xPts[2] := d2PointFromVector(d2VectorTransform(d2Vector(CGImageGetWidth(xImgRef), 0), M));
            xPts[3] := d2PointFromVector(d2VectorTransform(d2Vector(CGImageGetWidth(xImgRef), CGImageGetHeight(xImgRef)), M));
            xPts[4] := d2PointFromVector(d2VectorTransform(d2Vector(0, CGImageGetHeight(xImgRef)), M));
            R := d2NormalizeRect(xPts);

            M2 := IdentityMatrix;
            M2.m31 := d2RectWidth(R) / 2;
            M2.m32 := d2RectHeight(R) / 2;
            M := d2MatrixMultiply(M, M2);

            Bitmap.SetSize(Trunc(d2RectWidth(R)), Trunc(d2RectHeight(R)));
            xContextRef := CGBitmapContextCreate(Bitmap.StartLine, Bitmap.Width, Bitmap.Height, 8, Bitmap.Width * 4, CGColorSpaceCreateDeviceRGB, kCGImageAlphaPremultipliedLast);
            With M Do
               CGContextConcatCTM(xContextRef, CGAffineTransformMake(m11, m12, m21, m22, m31, m32));
            CGContextDrawImage(xContextRef, GetMacRectFromRect(d2Rect(0, 0, CGImageGetWidth(xImgRef), CGImageGetHeight(xImgRef))), ximgRef);
            CGContextRelease(xContextRef);
         End Else Begin
            Bitmap.SetSize(CGImageGetWidth(xImgRef), CGImageGetHeight(xImgRef));
            xContextRef := CGBitmapContextCreate(Bitmap.StartLine, Bitmap.Width, Bitmap.Height, 8, Bitmap.Width * 4, CGColorSpaceCreateDeviceRGB, kCGImageAlphaPremultipliedLast);
            CGContextDrawImage(xContextRef, GetMacRectFromRect(d2Rect(0, 0, Bitmap.Width, Bitmap.Height)), ximgRef);
            CGContextRelease(xContextRef);
         End;
         Result := True;
         CGImageRelease(ximgRef);
      End;
      CFRelease(ximgSrcRef);
   End;
End;

Function TD2FilterDarwin.SaveToFile(Const AFileName: String; Var Bitmap: TD2Bitmap; Const Params: String = ''): Boolean;
Var
   S, xparam, xname, xval: String;
   xfloatVal: Single;
   xPointerKeys: Array [0..10] Of pointer;
   xPointerValue: Array [0..10] Of pointer;
   xStrRef: CFStringRef;
   xURLRef: CFURLRef;
   ximgDstRef: CGImageDestinationRef;
   ximgRef: CGImageRef;
   xContextRef: CGContextRef;
   xEncType: CFStringRef;
   xDicRef: CFDictionaryRef;
Begin
   xStrRef := CFStringCreateWithCString(nil, PChar(AFileName), kCFStringEncodingUTF8);
   xURLRef := CFURLCreateWithFileSystemPath(nil, xStrRef, kCFURLPOSIXPathStyle, False);
   CFRelease(xStrRef);
   xEncType := nil;

   If (LowerCase(ExtractFileExt(AFileName)) = '.jpg') Or (LowerCase(ExtractFileExt(AFileName)) = '.jpeg') Then
      xEncType := kUTTypeJpeg;
   If (LowerCase(ExtractFileExt(AFileName)) = '.jp2') Then
      xEncType := kUTTypeJPEG2000;
   If (LowerCase(ExtractFileExt(AFileName)) = '.bmp') Then
      xEncType := kUTTypeBmp;
   If (LowerCase(ExtractFileExt(AFileName)) = '.png') Then
      xEncType := kUTTypePng;
   If (LowerCase(ExtractFileExt(AFileName)) = '.tif') Or (LowerCase(ExtractFileExt(AFileName)) = '.tiff') Then
      xEncType := kUTTypeTiff;
   If (LowerCase(ExtractFileExt(AFileName)) = '.gif') Then
      xEncType := kUTTypeGif;

   If xEncType <> nil Then
   Begin
      ximgDstRef := CGImageDestinationCreateWithURL(xURLRef, xEncType, 1, nil);

      CFRelease(xURLRef);
      If ximgDstRef <> nil Then
      Begin
         xContextRef := CGBitmapContextCreate(Bitmap.StartLine, Bitmap.Width, Bitmap.Height, 8, Bitmap.Width * 4, CGColorSpaceCreateDeviceRGB, kCGImageAlphaPremultipliedLast);
         ximgRef := CGBitmapContextCreateImage(xContextRef);
         If ximgRef <> nil Then
         Begin
            xDicRef := nil;

            If Params <> '' Then
            Begin
               S := Params;
               While S <> '' Do
               Begin
                  xparam := d2GetToken(S, ' ');
                  xname := d2GetToken(xparam, '=');
                  xval := d2GetToken(xparam, '');
                  If SysUtils.CompareText(xname, 'quality') = 0 Then
                  Begin
                     xfloatVal := strToFloat(xval) / 100;
                     xPointerKeys[0] := kCGImageDestinationLossyCompressionQuality;
                     xPointerValue[0] := CFNumberCreate(nil, kCFNumberFloat32Type, @xfloatVal);
                     xDicRef := CFDictionaryCreate(nil, @xPointerKeys[0], @xPointerValue[0], 1, nil, nil);
                     CGImageDestinationSetProperties(ximgDstRef, xDicRef);
                  End;
               End;
            End;

            CGImageDestinationAddImage(ximgDstRef, ximgRef, xDicRef);
            CGImageDestinationFinalize(ximgDstRef);

            If xDicRef <> nil Then
               CFRelease(xDicRef);
         End;
         CGImageRelease(ximgRef);
         CGContextRelease(xContextRef);
         CFRelease(ximgDstRef);
      End;
   End;
End;


//============= TD2CanvasDarwin =======================================

Constructor TD2CanvasDarwin.Create(Const AWidth, AHeight: Integer);
Begin
   Inherited;
End;

Constructor TD2CanvasDarwin.CreateFromBitmap(Const ABitmap: TD2Bitmap);
Begin
   Inherited;
   FBitmap := ABitmap;
   UpdateBitmap(FBitmap);
   FHandle := FBitmap.Handle;
End;

Destructor TD2CanvasDarwin.Destroy;
Begin
   Inherited;
End;

Procedure TD2CanvasDarwin.FreeBuffer;
Begin
   If (FBitmap = nil) Then
   Begin
      If FBuffered Then
         If FHandle <> 0 Then
            CGContextRelease(CGContextRef(FHandle));

      If FBufferBits <> nil Then
         System.FreeMem(FBufferBits);
   End;
End;

Procedure TD2CanvasDarwin.ResizeBuffer(Const AWidth, AHeight: Integer);
Begin
   If (AWidth = FWidth) And (AHeight = FHeight) Then
      Exit;

   FreeBuffer;
   FWidth := AWidth;
   FHeight := AHeight;
   If FWidth <= 0 Then
      FWidth := 1;
   If FHeight <= 0 Then
      FHeight := 1;
   FResized := True;

   If FWidth * FHeight = 0 Then
      Exit;
   If FBuffered Then
   Begin
      GetMem(FBufferBits, FWidth * FHeight * 4);

      FHandle := Cardinal(CGBitmapContextCreate(FBufferBits, FWidth, FHeight, 8, FWidth * 4, CGColorSpaceCreateDeviceRGB,
         kCGImageAlphaPremultipliedLast));

      CGContextTranslateCTM(CGContextRef(Handle), 0, FHeight);
      CGContextScaleCTM(CGContextRef(Handle), 1, -1);
   End Else Begin
      GetMem(FBufferBits, 4);
   End;
End;

Procedure TD2CanvasDarwin.FlushBuffer(Const X, Y: Integer; Const DC: Cardinal);
Var
   xCGR: CGRect;
   xImgRef: CGImageRef;
Begin
   If FBuffered Then
   Begin
      If (CGContextRef(Handle) <> nil) And (DC <> 0) Then
      Begin
         xCGR.origin.x := X;
         xCGR.origin.y := -Y + FHeight;
         xCGR.size.Width := FWidth;
         xCGR.size.Height := FHeight;
         xImgRef := CGBitmapContextCreateImage(CGContextRef(Handle));
         CGContextDrawImage(CGContextRef(DC), xCGR, xImgRef);
         CFRelease(xImgRef);
      End;
   End;
End;

Procedure TD2CanvasDarwin.FlushBufferRect(Const X, Y: Integer; Const DC: Cardinal; Const ARect: TD2Rect);
Var
   R, xSubR: CGRect;
   xNewR: TD2Rect;
   xTempImgRef, xImgRef: CGImageRef;
Begin
   If FBuffered And (DC <> 0) And (CGContextRef(Handle) <> nil) Then
   Begin
      d2IntersectRect(xNewR, ARect, d2Rect(0, 0, FWidth, FHeight));
      R.origin.x := X + xNewR.Left;
      R.origin.y := -(Y + xNewR.Bottom);
      R.size.Width := d2RectWidth(xNewR);
      R.size.Height := d2RectHeight(xNewR);
      xSubR.origin.x := xNewR.Left;
      xSubR.origin.y := xNewR.Top;
      xSubR.size.Width := d2RectWidth(xNewR);
      xSubR.size.Height := d2RectHeight(xNewR);

      xImgRef := CGBitmapContextCreateImage(CGContextRef(Handle));
      xTempImgRef := CGImageCreateWithImageInRect(xImgRef, xSubR);
      If xTempImgRef <> nil Then
      Begin
         CGContextSaveGState(CGContextRef(DC));
         CGContextScaleCTM(CGContextRef(DC), 1, -1);
         CGContextDrawImage(CGContextRef(DC), R, xTempImgRef);
         CFRelease(xTempImgRef);
         CGContextRestoreGState(CGContextRef(DC));
      End;
      CFRelease(xImgRef);
   End;
End;

Procedure TD2CanvasDarwin.Clear(Const Color: Cardinal);
Begin
   If Not FBuffered Then
   Begin
      CGContextClearRect(CGContextRef(Handle), GetMacRectFromRect(d2Rect(0, 0, FWidth, FHeight)));
   End Else Begin
      d2FillLongword(FBufferBits, FWidth * FHeight, Color);
   End;
End;

Procedure TD2CanvasDarwin.ClearRect(Const ARect: TD2Rect; Const AColor: TD2Color);
Var
   R: TRect;
Begin
   If FBufferBits = nil Then
      Exit;
   R := Rect(Trunc(ARect.Left), Trunc(ARect.Top), Round(ARect.Right), Round(ARect.Bottom));
   If R.Left < 0 Then
      R.Left := 0;
   If R.Top < 0 Then
      R.Top := 0;
   If R.Top < 0 Then
      R.Top := 0;
   If R.Right > FWidth Then
      R.Right := FWidth;
   If R.Bottom > FHeight Then
      R.Bottom := FHeight;
   If R.Bottom < R.Top Then
      R.Bottom := R.Top;
   If R.Right < R.Left Then
      R.Right := R.Left;
   If (R.Right < 0) Or (R.Top < 0) Or (R.Left > FWidth) Or (R.Top > FHeight) Then
      Exit;

   If Not FBuffered Then
   Begin
      CGContextClearRect(CGContextRef(Handle), GetMacRectFromRect(ARect));
   End Else Begin
      d2FillLongwordRect(FBufferBits, FWidth, FHeight, R.Left, R.Top, R.Right, R.Bottom, AColor);
   End;
End;

Class Function TD2CanvasDarwin.GetBitmapScanline(Bitmap: TD2Bitmap; y: Integer): PD2ColorArray;
Begin
   If (y >= 0) And (y < Bitmap.Height) And (Bitmap.StartLine <> nil) Then
      Result := @PD2ColorArray(Bitmap.StartLine)[(y) * Bitmap.Width]
   Else
      Result := nil;
End;


Procedure TD2CanvasDarwin.MultyMatrix(Const M: TD2Matrix);
Var
   xIM: TD2Matrix;
   xCurM: CGAffineTransform;
Begin
   FMatrix := M;

   xCurM := CGContextGetCTM(CGContextRef(Handle));
   xIM := IdentityMatrix;
   With xIM Do
   Begin
      m11 := xCurM.a;
      m12 := xCurM.b;
      m21 := xCurM.c;
      m22 := xCurM.d;
      m31 := xCurM.tx;
      m32 := xCurM.ty;
   End;
   d2InvertMatrix(xIM);

   If CGContextRef(Handle) = nil Then
      Exit;
   With xIM Do
      CGContextConcatCTM(CGContextRef(Handle), CGAffineTransformMake(m11, m12, m21, m22, m31, m32));
   If Not FBuffered And (FBitmap = nil) Then
   Begin
      CGContextTranslateCTM(CGContextRef(Handle), 0, FHeight);
      CGContextScaleCTM(CGContextRef(Handle), 1, -1);
   End;

   With FMatrix Do
      CGContextConcatCTM(CGContextRef(Handle), CGAffineTransformMake(m11, m12, m21, m22, m31, m32));
End;

Function TD2CanvasDarwin.SaveCanvas: Cardinal;
Var
   i: Integer;
Begin
   Result := InvalideCanvasState;
   If CGContextRef(Handle) = nil Then
      Exit;

   CGContextSaveGState(CGContextRef(Handle));

   If Length(FSaveData) > 0 Then
      For i := 0 To High(FSaveData) Do
         If FSaveData[i].Index = InvalideCanvasState Then
         Begin
            Result := i;
            Break;
         End;

   If Result = InvalideCanvasState Then
   Begin
      SetLength(FSaveData, Length(FSaveData) + 1);
      Result := High(FSaveData);
   End;

   FSaveData[Result].Index := Result;
   FSaveData[Result].Matrix := FMatrix;
   FSaveData[Result].Stroke := TD2Brush.Create(d2BrushSolid, InvalideCanvasState);
   FSaveData[Result].Stroke.Assign(Stroke);
   FSaveData[Result].Fill := TD2Brush.Create(d2BrushSolid, $FF000000);
   FSaveData[Result].Fill.Assign(Fill);
   FSaveData[Result].StrokeThickness := FStrokeThickness;
   FSaveData[Result].StrokeCap := StrokeCap;
   FSaveData[Result].StrokeJoin := StrokeJoin;
   FSaveData[Result].StrokeDash := StrokeDash;
   If StrokeDash <> d2DashSolid Then
   Begin
      SetLength(FSaveData[Result].Dash, Length(FDash));
      System.Move(FDash[0], FSaveData[Result].Dash[0], SizeOf(FDash[0]) * Length(FDash));
   End;
   FSaveData[Result].DashOffset := FDashOffset;
   FSaveData[Result].Font := TD2Font.Create;
   FSaveData[Result].Font.Assign(Font);
End;

Procedure TD2CanvasDarwin.SetMatrix(Const M: TD2Matrix);
Var
   xIM: TD2Matrix;
   xCurM: CGAffineTransform;
Begin
   FMatrix := M;
   xCurM := CGContextGetCTM(CGContextRef(Handle));
   xIM := IdentityMatrix;

   With xIM Do
   Begin
      m11 := xCurM.a;
      m12 := xCurM.b;
      m21 := xCurM.c;
      m22 := xCurM.d;
      m31 := xCurM.tx;
      m32 := xCurM.ty;
   End;

   d2InvertMatrix(xIM);

   If CGContextRef(Handle) = nil Then
      Exit;
   With xIM Do
      CGContextConcatCTM(CGContextRef(Handle), CGAffineTransformMake(m11, m12, m21, m22, m31, m32));

   If FBitmap <> nil Then
      CGContextTranslateCTM(CGContextRef(Handle), 0, FBitmap.Height)
   Else
      CGContextTranslateCTM(CGContextRef(Handle), 0, FHeight);

   CGContextScaleCTM(CGContextRef(Handle), 1, -1);

   With FMatrix Do
      CGContextConcatCTM(CGContextRef(Handle), CGAffineTransformMake(m11, m12, m21, m22, m31, m32));
End;

Procedure TD2CanvasDarwin.RestoreCanvas(Const AState: Cardinal);
Begin
   If CGContextRef(Handle) = nil Then
      Exit;

   If (AState >= 0) And (AState < Length(FSaveData)) Then
   Begin
      CGContextRestoreGState(CGContextRef(Handle));
      FSaveData[AState].Index := InvalideCanvasState;

      FMatrix := FSaveData[AState].Matrix;
      Stroke.Assign(FSaveData[AState].Stroke);
      FSaveData[AState].Stroke.Free;
      Fill.Assign(FSaveData[AState].Fill);
      FSaveData[AState].Fill.Free;
      SetMatrix(FMatrix);
      FStrokeThickness := FSaveData[AState].StrokeThickness;
      FStrokeCap := FSaveData[AState].StrokeCap;
      FStrokeJoin := FSaveData[AState].StrokeJoin;
      FStrokeDash := FSaveData[AState].StrokeDash;
      If StrokeDash <> d2DashSolid Then
      Begin
         SetLength(FDash, Length(FSaveData[AState].Dash));
         System.Move(FSaveData[AState].Dash[0], FDash[0], SizeOf(FDash[0]) * Length(FDash));
      End;
      FDashOffset := FSaveData[AState].DashOffset;
      Font.Assign(FSaveData[AState].Font);
      FSaveData[AState].Font.Free;
   End;
End;

Procedure TD2CanvasDarwin.SetClipRects(Const ARects: Array Of TD2Rect);
Var
   i: Integer;
   xRcts: Array Of CGRect;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If Not FBuffered Then
      Exit;

   SetLength(xRcts, Length(ARects));
   For i := 0 To High(ARects) Do
   Begin
      xRcts[i] := GetMacRectFromRect(ARects[i]);
   End;
   CGContextClipToRects(CGContextRef(Handle), @xRcts[0], Length(xRcts));
End;

Procedure TD2CanvasDarwin.IntersectClipRect(Const ARect: TD2Rect);
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   CGContextClipToRect(CGContextRef(Handle), GetMacRectFromRect(ARect));
End;

Procedure TD2CanvasDarwin.ExcludeClipRect(Const ARect: TD2Rect);
Var
   R: TD2Rect;
   xRR: Array [0..3] Of CGRect;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   R := ARect;
   xRR[0] := GetMacRectFromRect(d2Rect(0, 0, R.Left, FHeight));
   xRR[1] := GetMacRectFromRect(d2Rect(R.Right, 0, FWidth, FHeight));
   xRR[2] := GetMacRectFromRect(d2Rect(R.Left, 0, R.Right, R.Top));
   xRR[3] := GetMacRectFromRect(d2Rect(R.Left, R.Bottom, R.Right, FHeight));
   CGContextClipToRects(CGContextRef(Handle), @xRR[0], 4);
End;

Procedure TD2CanvasDarwin.ResetClipRect;
Begin
   If Not FBuffered Then
      Exit;
   If CGContextRef(Handle) = nil Then
      Exit;
   If CGContextRef(Handle) <> nil Then
      CGContextRelease(CGContextRef(Handle));

   If FBitmap <> nil Then
   Begin
      UpdateBitmap(FBitmap);
      Handle := FBitmap.Handle;
   End Else Begin
      Handle := THandle(CGBitmapContextCreate(FBufferBits, FWidth, FHeight, 8, FWidth * 4,
         CGColorSpaceCreateDeviceRGB, kCGImageAlphaPremultipliedLast));
   End;
End;

Procedure TD2CanvasDarwin.ApplyFill(ARect: TD2Rect; Const AOpacity: Single);
Begin
   If CGContextRef(Handle) = nil Then
      Exit;

   If (FFill.Style = d2BrushResource) And (FFill.Resource <> nil) And (FFill.Resource.Brush <> nil) Then
      FFill.Assign(FFill.Resource.Brush);

   With FFill Do
   Begin
      Case Style Of
         d2BrushSolid:
         Begin
            With CarbonColor(SolidColor, AOpacity) Do
               CGContextSetRGBFillColor(CGContextRef(Handle), r, g, b, a);
         End;
         d2BrushGradient:
         Begin
            fCallback.version := 0;
            fCallback.evaluate := @exLinearShadingValues;
            fCallback.releaseInfo := nil;
            varSOpacity := AOpacity;
            fFunc := CGFunctionCreate(FFill.Gradient, 1, @cnInputRange, 4, nil, fCallback);
            fColorSpace := CGColorSpaceCreateDeviceRGB();
            fShading := CGShadingCreateAxial(fColorSpace, CGPoint(d2Point(ARect.Left + Gradient.StartPosition.X * ARect.Right, ARect.Top + Gradient.StartPosition.Y * ARect.Bottom)),
               CGPoint(d2Point(ARect.Left + Gradient.StopPosition.X * ARect.Right, ARect.Top + Gradient.StopPosition.Y * ARect.Bottom)), fFunc, 1, 1);
         End;
         d2BrushResource:
         Begin
         End;
         d2BrushVisual:
         Begin
         End;
         d2BrushBitmap:
         Begin
            If (Bitmap.Bitmap <> nil) And (Bitmap.Bitmap.Width > 0) And (Bitmap.Bitmap.Height > 0) Then
            Begin
               UpdateBitmap(Bitmap.Bitmap);
               If (Bitmap.Bitmap.Handle <> 0) Then
               Begin
                  CGContextSetAlpha(CGContextRef(Handle), AOpacity);
                  fBmpRef := CGBitmapContextCreateImage(CGContextRef(Bitmap.Bitmap.Handle));
               End;
            End;
         End;
         Else CGContextSetRGBFillColor(CGContextRef(Handle), 0, 0, 0, 0);
      End;
   End;
End;

Procedure TD2CanvasDarwin.DeApplyFill(ARect: TD2Rect; Const AOpacity: Single);
Begin
   If CGContextRef(Handle) = nil Then
      Exit;

   With FFill Do
   Begin
      Case Style Of
         d2BrushSolid:
         Begin
         End;
         d2BrushGradient:
         Begin
            CGShadingRelease(fShading);
            CGColorSpaceRelease(fColorSpace);
            CGFunctionRelease(fFunc);
         End;
         d2BrushResource:
         Begin
         End;
         d2BrushVisual:
         Begin
         End;
         d2BrushBitmap:
         Begin
            CGContextSetAlpha(CGContextRef(Handle), 1);
            CFRelease(fBmpRef);
         End;
      End;
   End;
   fShading := nil;
   fBmpRef := nil;
End;

Procedure TD2CanvasDarwin.ApplyStroke(ARect: TD2Rect; Const AOpacity: Single);
Var
   i: Integer;
   xdash: Array Of Single;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;

   If (FStroke.Style = d2BrushResource) And (FStroke.Resource <> nil) And (FStroke.Resource.Brush <> nil) Then
      FStroke.Assign(FStroke.Resource.Brush);

   With FStroke Do
   Begin
      Case Style Of
         d2BrushSolid:
         Begin
            With CarbonColor(SolidColor, AOpacity) Do
               CGContextSetRGBStrokeColor(CGContextRef(Handle), r, g, b, a);
         End;
         d2BrushGradient:
         Begin
         End;
         d2BrushBitmap:
         Begin
         End;
         Else CGContextSetRGBStrokeColor(CGContextRef(Handle), 0, 0, 0, 0);
      End;
   End;
   Case StrokeCap Of
      d2CapFlat: CGContextSetLineCap(CGContextRef(Handle), kCGLineCapButt);
      d2CapRound: CGContextSetLineCap(CGContextRef(Handle), kCGLineCapRound);
   End;
   If Length(FDash) > 0 Then
   Begin
      SetLength(xdash, Length(FDash));
      For i := 0 To High(FDash) Do
      Begin
         xdash[i] := FDash[i] * StrokeThickness;
         If (StrokeCap = d2CapRound) Then
         Begin
            If odd(i) Then
               xdash[i] := (FDash[i] + 1) * StrokeThickness
            Else
               xdash[i] := (FDash[i] - 1) * StrokeThickness;
         End;
      End;
      CGContextSetLineDash(CGContextRef(Handle), FDashOffset, @xdash[0], Length(FDash));
   End Else
      CGContextSetLineDash(CGContextRef(Handle), 0, nil, 0);
   Case StrokeJoin Of
      d2JoinMiter: CGContextSetLineJoin(CGContextRef(Handle), kCGLineJoinMiter);
      d2JoinRound: CGContextSetLineJoin(CGContextRef(Handle), kCGLineJoinRound);
      d2JoinBevel: CGContextSetLineJoin(CGContextRef(Handle), kCGLineJoinBevel);
   End;
   CGContextSetLineWidth(CGContextRef(Handle), StrokeThickness);
End;

Procedure TD2CanvasDarwin.FontChanged(Sender: TObject);
Begin
End;

Procedure TD2CanvasDarwin.DrawLine(Const APt1, APt2: TD2Point; Const AOpacity: Single);
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FStroke.Style <> d2BrushNone Then
   Begin
      ApplyStroke(d2Rect(APt1.X, APt1.Y, APt2.X, APt2.Y), AOpacity);
      CGContextBeginPath(CGContextRef(Handle));
      CGContextMoveToPoint(CGContextRef(Handle), APt1.X, APt1.Y);
      CGContextAddLineToPoint(CGContextRef(Handle), APt2.X, APt2.Y);
      CGContextClosePath(CGContextRef(Handle));
      CGContextStrokePath(CGContextRef(Handle));
   End;
End;

Procedure TD2CanvasDarwin.DrawRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners;
   Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound);
Var
   xx1, xx2: Single;
   yy1, yy2: Single;
   R: TD2Rect;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FStroke.Style <> d2BrushNone Then
   Begin
      ApplyStroke(ARect, AOpacity);
      If (xRadius < Epsilon) And (yRadius < Epsilon) Then
      Begin
         CGContextStrokeRect(CGContextRef(Handle), GetMacRectFromRect(ARect));
      End Else Begin
         R := ARect;
         xx1 := xRadius;
         If d2RectWidth(R) - (xx1 * 2) < 0 Then
            xx1 := d2RectWidth(R) / 2;

         xx2 := xRadius * CurveKappaInv;

         yy1 := yRadius;
         If d2RectHeight(R) - (yy1 * 2) < 0 Then
            yy1 := d2RectHeight(R) / 2;

         yy2 := yRadius * CurveKappaInv;
         CGContextBeginPath(CGContextRef(Handle));
         CGContextMoveToPoint(CGContextRef(Handle), R.Left, R.Top + yy1);

         If d2CornerTopLeft In ACorners Then
         Begin
            Case ACornerType Of

               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Left + xx2, R.Top + yy1, R.Left + xx1, R.Top + yy2, R.Left + xx1, R.Top);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx2, R.Top + yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top + yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Left, R.Top + (yy2), R.Left + xx2, R.Top, R.Left + xx1, R.Top)
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Top);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top);
         End;

         CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Top);
         If d2CornerTopRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top + yy1);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Right - xx1, R.Top + yy2, R.Right - xx2, R.Top + yy1, R.Right, R.Top + yy1);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Top + yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx2, R.Top + yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top + yy1);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Right - xx2, R.Top, R.Right, R.Top + (yy2), R.Right, R.Top + yy1)
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top + yy1);
         End;
         CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Bottom - yy1);
         If d2CornerBottomRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Right - xx2, R.Bottom - yy1, R.Right - xx1, R.Bottom - yy2, R.Right - xx1, R.Bottom);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx2, R.Bottom - yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom - yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Right, R.Bottom - (yy2), R.Right - xx2, R.Bottom, R.Right - xx1, R.Bottom)
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Bottom);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom);
         End;

         CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Bottom);
         If d2CornerBottomLeft In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom - yy1);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Left + xx1, R.Bottom - yy2, R.Left + xx2, R.Bottom - yy1, R.Left, R.Bottom - yy1);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Bottom - yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx2, R.Bottom - yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom - yy1);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Left + xx2, R.Bottom, R.Left, R.Bottom - (yy2), R.Left, R.Bottom - yy1);
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom - yy1);
         End;

         CGContextClosePath(CGContextRef(Handle));
         CGContextStrokePath(CGContextRef(Handle));
      End;
   End;
End;

Procedure TD2CanvasDarwin.FillRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners;
   Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound);
Var
   xx1, xx2: Single;
   yy1, yy2: Single;
   R: TD2Rect;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FFill.Style <> d2BrushNone Then
   Begin
      CGContextSaveGState(CGContextRef(Handle));
      ApplyFill(ARect, AOpacity);

      CGContextBeginPath(CGContextRef(Handle));
      If (xRadius < Epsilon) And (yRadius < Epsilon) Then
      Begin
         CGContextAddRect(CGContextRef(Handle), GetMacRectFromRect(ARect));
      End Else Begin
         R := ARect;
         xx1 := xRadius;
         If d2RectWidth(R) - (xx1 * 2) < 0 Then
            xx1 := d2RectWidth(R) / 2;
         xx2 := xRadius * CurveKappaInv;
         yy1 := yRadius;
         If d2RectHeight(R) - (yy1 * 2) < 0 Then
            yy1 := d2RectHeight(R) / 2;
         yy2 := yRadius * CurveKappaInv;

         CGContextMoveToPoint(CGContextRef(Handle), R.Left, R.Top + yy1);
         If d2CornerTopLeft In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Left + xx2, R.Top + yy1, R.Left + xx1, R.Top + yy2, R.Left + xx1, R.Top);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx2, R.Top + yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top + yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Left, R.Top + (yy2), R.Left + xx2, R.Top, R.Left + xx1, R.Top)
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Top);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Top);
         End;
         CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Top);
         If d2CornerTopRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top + yy1);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Right - xx1, R.Top + yy2, R.Right - xx2, R.Top + yy1, R.Right, R.Top + yy1);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Top + yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx2, R.Top + yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top + yy1);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Right - xx2, R.Top, R.Right, R.Top + (yy2), R.Right, R.Top + yy1)
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Top + yy1);
         End;

         CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Bottom - yy1);
         If d2CornerBottomRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Right - xx2, R.Bottom - yy1, R.Right - xx1, R.Bottom - yy2, R.Right - xx1, R.Bottom);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx2, R.Bottom - yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom - yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Right, R.Bottom - (yy2), R.Right - xx2, R.Bottom, R.Right - xx1, R.Bottom)
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right, R.Bottom);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Right - xx1, R.Bottom);
         End;
         CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Bottom);
         If d2CornerBottomLeft In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom - yy1);
               d2CornerInnerRound: CGContextAddCurveToPoint(CGContextRef(Handle), R.Left + xx1, R.Bottom - yy2, R.Left + xx2, R.Bottom - yy1, R.Left, R.Bottom - yy1);
               d2CornerInnerLine:
               Begin
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx1, R.Bottom - yy2);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left + xx2, R.Bottom - yy1);
                  CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom - yy1);
               End;
               Else CGContextAddCurveToPoint(CGContextRef(Handle), R.Left + xx2, R.Bottom, R.Left, R.Bottom - (yy2), R.Left, R.Bottom - yy1)
            End;
         End Else Begin
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom);
            CGContextAddLineToPoint(CGContextRef(Handle), R.Left, R.Bottom - yy1);
         End;
      End;
      CGContextClosePath(CGContextRef(Handle));

      If (fBmpRef <> nil) And (FFill.Bitmap.Bitmap.Handle <> 0) Then
      Begin
         CGContextClip(CGContextRef(Handle));
         Case FFill.Bitmap.WrapMode Of
            d2WrapTile:
            Begin
               CGContextScaleCTM(CGContextRef(Handle), 1, -1);
               CarbonContextDrawTiledImage(CGContextRef(Handle), ARect, fBmpRef, FFill.Bitmap.Bitmap);
            End;
            d2WrapTileOriginal:
            Begin
               CGContextScaleCTM(CGContextRef(Handle), 1, -1);
               CGContextDrawImage(CGContextRef(Handle), GetMacRectFromRect(d2Rect(ARect.Left, ARect.Top, ARect.Left + FFill.Bitmap.Bitmap.Width, -ARect.Top - FFill.Bitmap.Bitmap.Height)), fBmpRef);
            End;
            d2WrapTileStretch:
            Begin
               CGContextScaleCTM(CGContextRef(Handle), 1, -1);
               CGContextDrawImage(CGContextRef(Handle), GetMacRectFromRect(d2Rect(ARect.Left, ARect.Top, ARect.Right, -ARect.Bottom)), fBmpRef);
            End;
         End;
      End Else
      If fShading <> nil Then
      Begin
         CGContextClip(CGContextRef(Handle));
         CGContextDrawShading(CGContextRef(Handle), fShading);
      End Else
         CGContextFillPath(CGContextRef(Handle));

      DeApplyFill(ARect, AOpacity);
      CGContextRestoreGState(CGContextRef(Handle));
   End;
End;

Procedure TD2CanvasDarwin.DrawEllipse(Const ARect: TD2Rect; Const AOpacity: Single);
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FStroke.Style <> d2BrushNone Then
   Begin
      ApplyStroke(ARect, AOpacity);
      CGContextStrokeEllipseInRect(CGContextRef(Handle), GetMacRectFromRect(ARect));
   End;
End;

Procedure TD2CanvasDarwin.FillEllipse(Const ARect: TD2Rect; Const AOpacity: Single);
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FFill.Style <> d2BrushNone Then
   Begin
      CGContextSaveGState(CGContextRef(Handle));
      ApplyFill(ARect, AOpacity);

      CGContextBeginPath(CGContextRef(Handle));
      CGContextAddEllipseInRect(CGContextRef(Handle), GetMacRectFromRect(ARect));
      CGContextClosePath(CGContextRef(Handle));

      If (fBmpRef <> nil) And (FFill.Bitmap.Bitmap.Handle <> 0) Then
      Begin
         CGContextClip(CGContextRef(Handle));
         Case FFill.Bitmap.WrapMode Of
            d2WrapTile:
            Begin
               CGContextScaleCTM(CGContextRef(Handle), 1, -1);
               CarbonContextDrawTiledImage(CGContextRef(Handle), ARect, fBmpRef, FFill.Bitmap.Bitmap);
            End;
            d2WrapTileOriginal:
            Begin
               CGContextScaleCTM(CGContextRef(Handle), 1, -1);
               CGContextDrawImage(CGContextRef(Handle), GetMacRectFromRect(d2Rect(ARect.Left, ARect.Top, ARect.Left + FFill.Bitmap.Bitmap.Width, -ARect.Top - FFill.Bitmap.Bitmap.Height)), fBmpRef);
            End;
            d2WrapTileStretch:
            Begin
               CGContextScaleCTM(CGContextRef(Handle), 1, -1);
               CGContextDrawImage(CGContextRef(Handle), GetMacRectFromRect(d2Rect(ARect.Left, ARect.Top, ARect.Right, -ARect.Bottom)), fBmpRef);
            End;
         End;
      End Else
      If fShading <> nil Then
      Begin
         CGContextClip(CGContextRef(Handle));
         CGContextDrawShading(CGContextRef(Handle), fShading);
      End Else
         CGContextFillPath(CGContextRef(Handle));

      DeApplyFill(ARect, AOpacity);
      CGContextRestoreGState(CGContextRef(Handle));
   End;
End;

Procedure TD2CanvasDarwin.DrawBitmap(Const ABitmap: TD2Bitmap; Const SrcRect, DstRect: TD2Rect; Const AOpacity: Single;
   Const HighSpeed: Boolean = False);
Var
   R, xSubR: CGRect;
   xImgRef, xTempImgRef: CGImageRef;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If ABitmap = nil Then
      Exit;

   UpdateBitmap(ABitmap);
   If (ABitmap.Handle <> 0) Then
   Begin
      R := GetMacRectFromRect(DstRect);
      If (SrcRect.Left = 0) And (SrcRect.Top = 0) And (SrcRect.Right = ABitmap.Width) And (SrcRect.Bottom = ABitmap.Height) Then
      Begin
         xImgRef := CGBitmapContextCreateImage(CGContextRef(ABitmap.Handle));
         If xImgRef <> nil Then
         Begin
            CGContextSaveGState(CGContextRef(Handle));
            CGContextSetAlpha(CGContextRef(Handle), AOpacity);

            If HighSpeed Then
               CGContextSetInterpolationQuality(CGContextRef(Handle), kCGInterpolationNone)
            Else
               CGContextSetInterpolationQuality(CGContextRef(Handle), kCGInterpolationDefault);

            R.origin.y := -DstRect.Bottom;
            CGContextScaleCTM(CGContextRef(Handle), 1, -1);

            CGContextDrawImage(CGContextRef(Handle), R, xImgRef);

            CGContextRestoreGState(CGContextRef(Handle));

            CGImageRelease(xImgRef);
         End;
      End Else Begin
         xSubR := GetMacRectFromRect(SrcRect);

         xImgRef := CGBitmapContextCreateImage(CGContextRef(ABitmap.Handle));
         If xImgRef <> nil Then
         Begin
            xTempImgRef := CGImageCreateWithImageInRect(xImgRef, xSubR);
            If xTempImgRef <> nil Then
            Begin
               CGContextSaveGState(CGContextRef(Handle));
               CGContextSetAlpha(CGContextRef(Handle), AOpacity);
               If HighSpeed Then
                  CGContextSetInterpolationQuality(CGContextRef(Handle), kCGInterpolationNone)
               Else
                  CGContextSetInterpolationQuality(CGContextRef(Handle), kCGInterpolationDefault);

               R.origin.y := -DstRect.Bottom;
               CGContextScaleCTM(CGContextRef(Handle), 1, -1);

               CGContextDrawImage(CGContextRef(Handle), R, xTempImgRef);
               CGImageRelease(xTempImgRef);
               CGContextRestoreGState(CGContextRef(Handle));
            End;
            CGImageRelease(xImgRef);
         End;
      End;
   End;
End;

Procedure TD2CanvasDarwin.FillPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single);
Var
   i: Integer;
   xW, xH: Single;
   xNewW, xNewH: Single;
   xB: TD2Rect;
   xCP1, xCP2: TD2Point;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FFill.Style = d2BrushNone Then
      Exit;
   If APath.IsEmpty Then
      Exit;

   xB := APath.GetBounds;
   xW := d2RectWidth(xB);
   xH := d2RectHeight(xB);
   xNewW := d2RectWidth(ARect);
   xNewH := d2RectHeight(ARect);

   ApplyFill(ARect, AOpacity);
   CGContextSaveGState(CGContextRef(Handle));
   i := 0;
   While i < Length(APath.PathData) Do
   Begin
      Case APath.PathData[i].Kind Of
         d2PathPointMoveTo:
         Begin
            CGContextMoveToPoint(CGContextRef(Handle), ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointLineTo:
         Begin
            CGContextAddLineToPoint(CGContextRef(Handle), ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointCurveTo:
         Begin
            xCP1 := d2Point(ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW, ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
            Inc(i);
            xCP2 := d2Point(ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW, ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
            Inc(i);
            CGContextAddCurveToPoint(CGContextRef(Handle), xCP1.X,
               xCP1.Y,
               xCP2.X,
               xCP2.Y,
               ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointClose:
         Begin
            CGContextClosePath(CGContextRef(Handle));
         End;
      End;
      Inc(i);
   End;

   If (fBmpRef <> nil) And (FFill.Bitmap.Bitmap.Handle <> 0) Then
   Begin
      CGContextClip(CGContextRef(Handle));
      Case FFill.Bitmap.WrapMode Of
         d2WrapTile:
         Begin
            CGContextScaleCTM(CGContextRef(Handle), 1, -1);
            CarbonContextDrawTiledImage(CGContextRef(Handle), ARect, fBmpRef, FFill.Bitmap.Bitmap);
         End;
         d2WrapTileOriginal:
         Begin
            CGContextScaleCTM(CGContextRef(Handle), 1, -1);
            CGContextDrawImage(CGContextRef(Handle), GetMacRectFromRect(d2Rect(ARect.Left, ARect.Top, ARect.Left + FFill.Bitmap.Bitmap.Width, -ARect.Top - FFill.Bitmap.Bitmap.Height)), fBmpRef);
         End;
         d2WrapTileStretch:
         Begin
            CGContextScaleCTM(CGContextRef(Handle), 1, -1);
            CGContextDrawImage(CGContextRef(Handle), GetMacRectFromRect(d2Rect(ARect.Left, ARect.Top, ARect.Right, -ARect.Bottom)), fBmpRef);
         End;
      End;
   End Else Begin
      If fShading <> nil Then
      Begin
         CGContextClip(CGContextRef(Handle));
         CGContextDrawShading(CGContextRef(Handle), fShading);
      End Else Begin
         CGContextFillPath(CGContextRef(Handle));
      End;
   End;
   DeApplyFill(ARect, AOpacity);
   CGContextRestoreGState(CGContextRef(Handle));
End;


Procedure TD2CanvasDarwin.UpdateBitmap(ABitmap: TD2Bitmap);
Var
   xClrSpace: CGColorSpaceRef;
Begin
   If ABitmap = nil Then
      Exit;

   If ABitmap.Handle = 0 Then
   Begin
      xClrSpace := CGColorSpaceCreateDeviceRGB;
      ABitmap.Handle := Cardinal(CGBitmapContextCreate(ABitmap.StartLine, ABitmap.Width, ABitmap.Height, 8, ABitmap.Width * 4, xClrSpace, kCGImageAlphaPremultipliedLast));
      CGColorSpaceRelease(xClrSpace);
   End;

   ABitmap.NeedUpdate := False;
   ABitmap.OnDestroyHandle := DoDestroyBitmap;
End;

Procedure TD2CanvasDarwin.DoDestroyBitmap(Sender: TObject);
Begin
   If (Sender = nil) Then
      exit;

   If (TD2Bitmap(Sender).Handle <> 0) Then
      CGContextRelease(CGContextRef(TD2Bitmap(Sender).Handle));

   TD2Bitmap(Sender).Handle := 0;
End;

Procedure TD2CanvasDarwin.DrawPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single);
Var
   i: Integer;
   xW, xH: Single;
   xNewW, xNewH: Single;
   xB: TD2Rect;
   xCP1, xCP2: TD2Point;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FStroke.Style = d2BrushNone Then
      Exit;
   If APath.IsEmpty Then
      Exit;
   ApplyStroke(ARect, AOpacity);
   xB := APath.GetBounds;
   xW := d2RectWidth(xB);
   xH := d2RectHeight(xB);
   xNewW := d2RectWidth(ARect);
   xNewH := d2RectHeight(ARect);

   CGContextSaveGState(CGContextRef(Handle));
   CGContextBeginPath(CGContextRef(Handle));
   i := 0;
   While i < Length(APath.PathData) Do
   Begin
      Case APath.PathData[i].Kind Of
         d2PathPointMoveTo:
         Begin
            CGContextMoveToPoint(CGContextRef(Handle), ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointLineTo:
         Begin
            CGContextAddLineToPoint(CGContextRef(Handle), ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointCurveTo:
         Begin
            xCP1 := d2Point(ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW, ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
            Inc(i);
            xCP2 := d2Point(ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW, ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
            Inc(i);

            CGContextAddCurveToPoint(CGContextRef(Handle), xCP1.X,
               xCP1.Y,
               xCP2.X,
               xCP2.Y,
               ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointClose:
         Begin
            CGContextClosePath(CGContextRef(Handle));
         End;
      End;

      Inc(i);
   End;
   CGContextStrokePath(CGContextRef(Handle));

   CGContextRestoreGState(CGContextRef(Handle));
End;


Function TD2CanvasDarwin.PtInPath(Const APoint: TD2Point; Const ARect: TD2Rect; Const APath: TD2PathData): Boolean;
Var
   i: Integer;
   xW, xH: Single;
   xNewW, xNewH: Single;
   xB: TD2Rect;
   xCP1, xCP2: TD2Point;
Begin
   Result := False;
   If CGContextRef(Handle) = nil Then
      Exit;
   If APath.IsEmpty Then
      Exit;
   xB := APath.GetBounds;
   xW := d2RectWidth(xB);
   xH := d2RectHeight(xB);
   xNewW := d2RectWidth(ARect);
   xNewH := d2RectHeight(ARect);

   CGContextSaveGState(CGContextRef(Handle));
   CGContextTranslateCTM(CGContextRef(Handle), ARect.Left, ARect.Top);
   CGContextBeginPath(CGContextRef(Handle));
   i := 0;
   While i < Length(APath.PathData) Do
   Begin
      Case APath.PathData[i].Kind Of
         d2PathPointMoveTo:
         Begin
            CGContextMoveToPoint(CGContextRef(Handle), ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointLineTo:
         Begin
            CGContextAddLineToPoint(CGContextRef(Handle), ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointCurveTo:
         Begin
            xCP1 := d2Point(ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW, ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
            Inc(i);
            xCP2 := d2Point(ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW, ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
            Inc(i);
            CGContextAddCurveToPoint(CGContextRef(Handle), xCP1.X,
               xCP1.Y,
               xCP2.X,
               xCP2.Y,
               ARect.Left + (APath.PathData[i].Point.X - xB.Left) / xW * xNewW,
               ARect.Top + (APath.PathData[i].Point.Y - xB.Top) / xH * xNewH);
         End;
         d2PathPointClose:
         Begin
            CGContextClosePath(CGContextRef(Handle));
         End;
      End;
      Inc(i);
   End;
   CGContextRestoreGState(CGContextRef(Handle));
   Result := CGContextPathContainsPoint(CGContextRef(Handle), CGPoint(APoint), kCGPathFillStroke) > 0;
End;

Procedure TD2CanvasDarwin.FillText(Const ARect, AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean;
   Const AOpacity: Single; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter);
Var
   S: String;
   i, lineWidth: Cardinal;
   CLeft, CRight, CTop, CBottom: Fixed;
   xAttValues: Array [0..10] Of ATSUAttributeValuePtr;
   xAtrTags: Array [0..10] Of ATSUAttributeTag;
   xTmpSizes: Array [0..10] Of ByteCount;
   xsoftBreaks: Array [0..100] Of UniCharArrayOffset;
   xStyles: ATSUStyle;
   xTextLayout: ATSUTextLayout;
   xtmpFont: ATSUFontID;
   xFontSize: Fixed;
   xFontItalic, xatsuBold: Boolean;
   xlineTrunc: ATSULineTruncation;
   xtextAlign: fract;
   xnumSoftBreaks: Integer;
   xcurrentStart, xcurrentEnd, xlayoutStart, xlayoutLength: UniCharArrayOffset;
   xHOffset, xVOffset: Single;
Begin
   If CGContextRef(Handle) = nil Then
      Exit;
   If FFill.Style = d2BrushNone Then
      Exit;
   If Length(AText) = 0 Then
      Exit;

   ApplyFill(ARect, AOpacity);

   ATSUCreateStyle(xStyles);

   S := Font.Family;
   If ATSUFindFontFromName(@S[1], Length(S), kFontFamilyName, kFontNoPlatform, kFontNoScript, kFontNoLanguage, xtmpFont) = noErr Then
   Begin
      xAtrTags[0] := kATSUFontTag;
      xTmpSizes[0] := sizeof(ATSUFontId);
      xAttValues[0] := @xtmpFont;
      ATSUSetAttributes(xStyles, 1, xAtrTags, xTmpSizes, xAttValues);
   End;

   xFontSize := Round(Font.Size * $FFFF);
   xatsuBold := Font.Style In [d2FontBold, d2FontBoldItalic];
   xFontItalic := Font.Style In [d2FontItalic, d2FontBoldItalic];

   xAtrTags[0] := kATSUSizeTag;
   xTmpSizes[0] := sizeof(Fixed);
   xAttValues[0] := @xFontSize;
   xAtrTags[1] := kATSUQDBoldFaceTag;
   xTmpSizes[1] := sizeof(Boolean);
   xAttValues[1] := @xatsuBold;
   xAtrTags[2] := kATSUQDItalicTag;
   xTmpSizes[2] := sizeof(Boolean);
   xAttValues[2] := @xFontItalic;
   ATSUSetAttributes(xStyles, 3, xAtrTags, xTmpSizes, xAttValues);

   xTextLayout := nil;
   ATSUCreateTextLayout(xTextLayout);

   xAtrTags[0] := kATSUCGContextTag;
   xTmpSizes[0] := SizeOf(CGContextRef);
   xAttValues[0] := @CGContextRef(Handle);

   xAtrTags[1] := kATSULineFlushFactorTag;
   xTmpSizes[1] := SizeOf(xtextAlign);
   If WordWrap Then
   Begin
      Case ATextAlign Of
         d2TextAlignCenter:
         Begin
            xtextAlign := kATSUCenterAlignment;
         End;
         d2TextAlignNear:
         Begin
            xtextAlign := kATSUStartAlignment;
         End;
         d2TextAlignFar:
         Begin
            xtextAlign := kATSUEndAlignment;
         End;
      End;
   End Else
      xtextAlign := kATSUStartAlignment;
   xAttValues[1] := @xtextAlign;

   If WordWrap Then
      lineWidth := Trunc(d2RectWidth(ARect) * $FFFF)
   Else
      lineWidth := 5000 * $FFFF;
   If lineWidth <= 0 Then
      lineWidth := 1;
   xAtrTags[2] := kATSULineWidthTag;
   xTmpSizes[2] := SizeOf(lineWidth);
   xAttValues[2] := @lineWidth;

   xlineTrunc := kATSUTruncateNone;
   xAtrTags[3] := kATSULineTruncationTag;
   xTmpSizes[3] := SizeOf(xlineTrunc);
   xAttValues[3] := @xlineTrunc;
   ATSUSetLayoutControls(xTextLayout, 4, xAtrTags, xTmpSizes, xAttValues);

   ATSUSetTextPointerLocation(xTextLayout,
      ConstUniCharArrayPtr(Pwidechar(AText)),
      kATSUFromTextBeginning,
      kATSUToTextEnd,
      Length(AText));
   ATSUSetRunStyle(xTextLayout,
      xStyles,
      kATSUFromTextBeginning,
      kATSUToTextEnd);
   ATSUSetTransientFontMatching(xTextLayout, True);

   ATSUBatchBreakLines(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, lineWidth, @xnumSoftBreaks);

   CGContextSaveGState(CGContextRef(Handle));

   CGContextClipToRect(CGContextRef(Handle), GetMacRectFromRect(d2Rect(ARect.Left - 1, ARect.Top - 1, ARect.Right + 1, ARect.Bottom + 3)));

   CGContextScaleCTM(CGContextRef(Handle), 1, -1);

   Case AVTextAlign Of
      d2TextAlignCenter:
         xVOffset := (d2RectHeight(ARect) - ((xnumSoftBreaks + 0.5) * Font.Size * 1.33)) / 2;
      d2TextAlignNear:
         xVOffset := d2RectHeight(ARect) - ((xnumSoftBreaks + 0.75) * Font.Size * 1.33);
      d2TextAlignFar:
         xVOffset := 0.25 * Font.Size * 1.33;
   End;

   If xnumSoftBreaks = 0 Then
   Begin
      If Not WordWrap Then
      Begin
         CLeft := 0;
         CTop := 0;
         Cright := 0;
         CBottom := 0;
         ATSUGetUnjustifiedBounds(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, CLeft, CRight, CTop, CBottom);
         Case ATextAlign Of
            d2TextAlignCenter:
               xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect)) / 2;
            d2TextAlignFar:
               xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect));
            Else xHOffset := 0;
         End;
      End Else
         xHOffset := 0;
      ATSUDrawText(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, (Trunc(ARect.Left + xHOffset) * $FFFF), (Trunc(-ARect.Bottom + xVOffset) * $FFFF));
   End Else Begin
      ATSUGetSoftLineBreaks(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, xnumSoftBreaks, @xsoftBreaks[0], @xnumSoftBreaks);
      ATSUGetTextLocation(xTextLayout, nil, nil, @xlayoutStart, @xlayoutLength, nil);
      xcurrentStart := xlayoutStart;
      For i := 0 To xnumSoftBreaks Do
      Begin
         If (xnumSoftBreaks > i) Then
            xcurrentEnd := xsoftBreaks[i]
         Else
            xcurrentEnd := xlayoutStart + xlayoutLength;
         If Not WordWrap Then
         Begin
            CLeft := 0;
            CTop := 0;
            Cright := 0;
            CBottom := 0;
            ATSUGetUnjustifiedBounds(xTextLayout, xcurrentStart, xcurrentEnd - xcurrentStart, CLeft, CRight, CTop, CBottom);
            Case ATextAlign Of
               d2TextAlignCenter:
                  xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect)) / 2;
               d2TextAlignFar:
                  xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect));
               Else xHOffset := 0;
            End;
         End Else
            xHOffset := 0;
         ATSUDrawText(xTextLayout, xcurrentStart, xcurrentEnd - xcurrentStart, Round((ARect.Left + xHOffset) * $FFFF), Round((-ARect.Bottom + xVOffset + ((xnumSoftBreaks - i) * Font.Size * 1.33)) * $FFFF));
         xcurrentStart := xcurrentEnd;
      End;
   End;

   CGContextRestoreGState(CGContextRef(Handle));

   ATSUDisposeTextLayout(xTextLayout);
   ATSUDisposeStyle(xStyles);
   DeApplyFill(ARect, AOpacity);
End;

Function TD2CanvasDarwin.TextToPath(Path: TD2PathData; Const ARect: TD2Rect; Const AText: WideString;
   Const WordWrap: Boolean; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter): Boolean;
Var
   i, j, lineWidth: Cardinal;
   S: String;
   xVOffset, xHOffset: Single;
   CLeft, CRight, CTop, CBottom: Fixed;
   xAttValues: Array [0..10] Of ATSUAttributeValuePtr;
   xAtrTags: Array [0..10] Of ATSUAttributeTag;
   xTmpSizes: Array [0..10] Of ByteCount;
   xsoftBreaks: Array [0..100] Of UniCharArrayOffset;
   xStyles: ATSUStyle;
   xTextLayout: ATSUTextLayout;
   xtmpFont: ATSUFontID;
   xFontSize: Fixed;
   xFontItalic, atsuBold: Boolean;
   xlineTrunc: ATSULineTruncation;
   xtextAlign: fract;
   xnumSoftBreaks: Integer;
   xcurrentStart, xcurrentEnd, xlayoutStart, xlayoutLength: UniCharArrayOffset;
   xStatus: OSStatus;
   xnumGlyphs: Cardinal;
   xglyphArray: TATSLayoutRecArray;
   xmoveToProc: ATSCubicMoveToUPP;
   xlineToProc: ATSCubicLineToUPP;
   xcurveToProc: ATSCubicCurveToUPP;
   xclosePathProc: ATSCubicClosePathUPP;
   xData: TText3DCallback;
   xSP: TD2Point;
Begin
   Result := False;
   If Length(AText) = 0 Then
      Exit;
   Path.Clear;

   ATSUCreateStyle(xStyles);

   S := Font.Family;
   If ATSUFindFontFromName(@S[1], Length(S), kFontFamilyName, kFontNoPlatform, kFontNoScript, kFontNoLanguage, xtmpFont) = noErr Then
   Begin
      xAtrTags[0] := kATSUFontTag;
      xTmpSizes[0] := sizeof(ATSUFontId);
      xAttValues[0] := @xtmpFont;
      ATSUSetAttributes(xStyles, 1, xAtrTags, xTmpSizes, xAttValues);
   End;

   xFontSize := Round(Font.Size * $FFFF);
   atsuBold := Font.Style In [d2FontBold, d2FontBoldItalic];
   xFontItalic := Font.Style In [d2FontItalic, d2FontBoldItalic];

   xAtrTags[0] := kATSUSizeTag;
   xTmpSizes[0] := sizeof(Fixed);
   xAttValues[0] := @xFontSize;
   xAtrTags[1] := kATSUQDBoldFaceTag;
   xTmpSizes[1] := sizeof(Boolean);
   xAttValues[1] := @atsuBold;
   xAtrTags[2] := kATSUQDItalicTag;
   xTmpSizes[2] := sizeof(Boolean);
   xAttValues[2] := @xFontItalic;
   ATSUSetAttributes(xStyles, 3, xAtrTags, xTmpSizes, xAttValues);

   xTextLayout := nil;
   ATSUCreateTextLayout(xTextLayout);

   xAtrTags[1] := kATSULineFlushFactorTag;
   xTmpSizes[1] := SizeOf(xtextAlign);
   If WordWrap Then
   Begin
      Case ATextAlign Of
         d2TextAlignCenter:
         Begin
            xtextAlign := kATSUCenterAlignment;
         End;
         d2TextAlignNear:
         Begin
            xtextAlign := kATSUStartAlignment;
         End;
         d2TextAlignFar:
         Begin
            xtextAlign := kATSUEndAlignment;
         End;
      End;
   End Else
      xtextAlign := kATSUStartAlignment;

   xAttValues[1] := @xtextAlign;

   If WordWrap Then
      lineWidth := Trunc(d2RectWidth(ARect) * $FFFF)
   Else
      lineWidth := 5000 * $FFFF;
   If lineWidth <= 0 Then
      lineWidth := 1;
   xAtrTags[2] := kATSULineWidthTag;
   xTmpSizes[2] := SizeOf(lineWidth);
   xAttValues[2] := @lineWidth;

   xlineTrunc := kATSUTruncateNone;
   xAtrTags[3] := kATSULineTruncationTag;
   xTmpSizes[3] := SizeOf(xlineTrunc);
   xAttValues[3] := @xlineTrunc;
   ATSUSetLayoutControls(xTextLayout, 4, xAtrTags, xTmpSizes, xAttValues);

   ATSUSetTextPointerLocation(xTextLayout,
      ConstUniCharArrayPtr(Pwidechar(AText)),
      kATSUFromTextBeginning,
      kATSUToTextEnd,
      Length(AText));
   ATSUSetRunStyle(xTextLayout,
      xStyles,
      kATSUFromTextBeginning,
      kATSUToTextEnd);
   ATSUSetTransientFontMatching(xTextLayout, True);

   ATSUBatchBreakLines(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, lineWidth, @xnumSoftBreaks);

   xmoveToProc := NewATSCubicMoveToUPP(@exCubicMoveToProc);
   xlineToProc := NewATSCubicLineToUPP(@exCubicLineToProc);
   xcurveToProc := NewATSCubicCurveToUPP(@exCubicCurveToProc);
   xclosePathProc := NewATSCubicClosePathUPP(@exCubicClosePathProc);

   Case AVTextAlign Of
      d2TextAlignCenter: xVOffset := (d2RectHeight(ARect) - (((xnumSoftBreaks - 0.5) * Font.Size * 1.33))) / 2;
      d2TextAlignNear: xVOffset := (0.75 * Font.Size * 1.33);
      d2TextAlignFar: xVOffset := d2RectHeight(ARect) - (Font.Size * 1.33 * (xnumSoftBreaks + 0.25));
   End;

   If xnumSoftBreaks = 0 Then
   Begin
      ATSUGetUnjustifiedBounds(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, CLeft, CRight, CTop, CBottom);

      Case ATextAlign Of
         d2TextAlignCenter: xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect)) / 2;
         d2TextAlignFar: xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect));
         Else xHOffset := 0;
      End;

      GetGlyphIDsAndPositions(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, xGlyphArray, xnumGlyphs);
      For i := 0 To xnumGlyphs - 1 Do
      Begin
         xSP := d2Point(0, 0);
         xData.Path := Path;
         xData.SP := @xSP;
         xData.GlyphLayouts := xGlyphArray[i];
         xData.Height := (Font.Size * 1.33);
         xData.XOffset := ARect.Left + xHOffset;
         xData.YOffset := xVOffset;
         ATSUGlyphGetCubicPaths(xStyles, xGlyphArray[i].glyphID, xmoveToProc, xlineToProc, xcurveToProc, xclosePathProc, @xData, xStatus);
      End;
   End Else Begin
      ATSUGetSoftLineBreaks(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, xnumSoftBreaks, @xsoftBreaks[0], @xnumSoftBreaks);
      ATSUGetTextLocation(xTextLayout, nil, nil, @xlayoutStart, @xlayoutLength, nil);
      xcurrentStart := xlayoutStart;
      For i := 0 To xnumSoftBreaks Do
      Begin
         If (xnumSoftBreaks > i) Then
            xcurrentEnd := xsoftBreaks[i]
         Else
            xcurrentEnd := xlayoutStart + xlayoutLength;

         ATSUGetUnjustifiedBounds(xTextLayout, xcurrentStart, xcurrentEnd - xcurrentStart, CLeft, CRight, CTop, CBottom);
         Case ATextAlign Of
            d2TextAlignCenter: xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect)) / 2;
            d2TextAlignFar: xHOffset := -(((CRight - CLeft) / $FFFF) - d2RectWidth(ARect));
            Else xHOffset := 0;
         End;
         GetGlyphIDsAndPositions(xTextLayout, xcurrentStart, xcurrentEnd - xcurrentStart, xGlyphArray, xnumGlyphs);
         For j := 0 To xnumGlyphs - 1 Do
         Begin
            xSP := d2Point(0, 0);
            xData.Path := Path;
            xData.SP := @xSP;
            xData.GlyphLayouts := xGlyphArray[j];
            xData.Height := (Font.Size * 1.33);

            If i > 0 Then
               xData.YOffset := i * xData.Height
            Else
               xData.YOffset := 0;

            xData.YOffset := xData.YOffset + xVOffset;
            xData.XOffset := xHOffset;
            ATSUGlyphGetCubicPaths(xStyles, xGlyphArray[j].glyphID, xmoveToProc, xlineToProc, xcurveToProc, xclosePathProc, @xData, xStatus);
         End;

         xcurrentStart := xcurrentEnd;
      End;
      ATSUGetUnjustifiedBounds(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, CLeft, CRight, CTop, CBottom);
   End;

   Result := True;

   DisposeATSCubicMoveToUPP(xmoveToProc);
   DisposeATSCubicLineToUPP(xlineToProc);
   DisposeATSCubicCurveToUPP(xcurveToProc);
   DisposeATSCubicClosePathUPP(xclosePathProc);
   ATSUDisposeTextLayout(xTextLayout);
   ATSUDisposeStyle(xStyles);
End;


Procedure TD2CanvasDarwin.MeasureText(Var ARect: TD2Rect; AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean;
   Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign);
Var
   S: String;
   curR, TextR: TD2Rect;
   xVOffset: Single;
   xAttValues: Array [0..10] Of ATSUAttributeValuePtr;
   xAtrTags: Array [0..10] Of ATSUAttributeTag;
   xTmpSizes: Array [0..10] Of ByteCount;
   xsoftBreaks: Array [0..100] Of UniCharArrayOffset;
   xStyles: ATSUStyle;
   xTextLayout: ATSUTextLayout;
   xtmpFont: ATSUFontID;
   xFontSize: Fixed;
   xFontItalic, xatsuBold: Boolean;
   xlineTrunc: ATSULineTruncation;
   i, lineWidth: Cardinal;
   xtextAlign: fract;
   CLeft, CRight, CTop, CBottom: Fixed;
   xnumSoftBreaks: Integer;
   xcurrentStart, xcurrentEnd, xlayoutStart, xlayoutLength: UniCharArrayOffset;
Begin
   If Length(AText) = 0 Then
   Begin
      ARect.Right := ARect.Left;
      Exit;
   End;

   ATSUCreateStyle(xStyles);

   S := Font.Family;
   If ATSUFindFontFromName(@S[1], Length(S), kFontFamilyName, kFontNoPlatform, kFontNoScript, kFontNoLanguage, xtmpFont) = noErr Then
   Begin
      xAtrTags[0] := kATSUFontTag;
      xTmpSizes[0] := sizeof(ATSUFontId);
      xAttValues[0] := @xtmpFont;
      ATSUSetAttributes(xStyles, 1, xAtrTags, xTmpSizes, xAttValues);
   End;

   xFontSize := Round(Font.Size * $FFFF);
   xatsuBold := Font.Style In [d2FontBold, d2FontBoldItalic];
   xFontItalic := Font.Style In [d2FontItalic, d2FontBoldItalic];
   xAtrTags[0] := kATSUSizeTag;
   xTmpSizes[0] := sizeof(Fixed);
   xAttValues[0] := @xFontSize;
   xAtrTags[1] := kATSUQDBoldfaceTag;
   xTmpSizes[1] := sizeof(Boolean);
   xAttValues[1] := @xatsuBold;
   xAtrTags[2] := kATSUQDItalicTag;
   xTmpSizes[2] := sizeof(Boolean);
   xAttValues[2] := @xFontItalic;
   ATSUSetAttributes(xStyles, 3, xAtrTags, xTmpSizes, xAttValues);

   xTextLayout := nil;
   ATSUCreateTextLayout(xTextLayout);

   xAtrTags[0] := kATSUCGContextTag;
   xTmpSizes[0] := SizeOf(CGContextRef);
   xAttValues[0] := @CGContextRef(Handle);

   xAtrTags[1] := kATSULineFlushFactorTag;
   xTmpSizes[1] := SizeOf(xtextAlign);

   If WordWrap Then
   Begin
      Case ATextAlign Of
         d2TextAlignCenter:
         Begin
            xtextAlign := kATSUCenterAlignment;
         End;
         d2TextAlignNear:
         Begin
            xtextAlign := kATSUStartAlignment;
         End;
         d2TextAlignFar:
         Begin
            xtextAlign := kATSUEndAlignment;
         End;
      End;
   End Else Begin
      xtextAlign := kATSUStartAlignment;
   End;

   xAttValues[1] := @xtextAlign;

   If WordWrap Then
      lineWidth := Trunc(d2RectWidth(ARect) * $FFFF)
   Else
      lineWidth := 5000 * $FFFF;
   If lineWidth <= 0 Then
      lineWidth := 1;
   xAtrTags[2] := kATSULineWidthTag;
   xTmpSizes[2] := SizeOf(lineWidth);
   xAttValues[2] := @lineWidth;

   xlineTrunc := kATSUTruncateNone;
   xAtrTags[3] := kATSULineTruncationTag;
   xTmpSizes[3] := SizeOf(xlineTrunc);
   xAttValues[3] := @xlineTrunc;
   ATSUSetLayoutControls(xTextLayout, 4, xAtrTags, xTmpSizes, xAttValues);

   ATSUSetTextPointerLocation(xTextLayout,
      ConstUniCharArrayPtr(Pwidechar(AText)),
      kATSUFromTextBeginning,
      kATSUToTextEnd,
      Length(AText));
   ATSUSetRunStyle(xTextLayout,
      xStyles,
      kATSUFromTextBeginning,
      kATSUToTextEnd);
   ATSUSetTransientFontMatching(xTextLayout, True);

   ATSUBatchBreakLines(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, lineWidth, @xnumSoftBreaks);

   Case AVTextAlign Of
      d2TextAlignCenter:
         xVOffset := (d2RectHeight(ARect) - ((xnumSoftBreaks + 0.5) * Font.Size * 1.33)) / 2;
      d2TextAlignNear:
         xVOffset := d2RectHeight(ARect) - ((xnumSoftBreaks + 1) * Font.Size * 1.33);
      d2TextAlignFar:
         xVOffset := 0;
   End;

   If xnumSoftBreaks = 0 Then
   Begin
      ATSUGetUnjustifiedBounds(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, CLeft, CRight, CTop, CBottom);
      curR := d2Rect(CLeft / $FFFF, CBottom / $FFFF, CRight / $FFFF, CTop / $FFFF);
      curR.Bottom := curR.Top + (Font.Size * 1.33);
      d2RectCenter(curR, curR);
      textR := curR;
   End Else Begin
      ATSUGetSoftLineBreaks(xTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, xnumSoftBreaks, @xsoftBreaks[0], @xnumSoftBreaks);
      ATSUGetTextLocation(xTextLayout, nil, nil, @xlayoutStart, @xlayoutLength, nil);
      xcurrentStart := xlayoutStart;
      For i := 0 To xnumSoftBreaks Do
      Begin
         If (xnumSoftBreaks > i) Then
            xcurrentEnd := xsoftBreaks[i]
         Else
            xcurrentEnd := xlayoutStart + xlayoutLength;

         ATSUGetUnjustifiedBounds(xTextLayout, xcurrentStart, xcurrentEnd - xcurrentStart, CLeft, CRight, CTop, CBottom);
         curR := d2Rect(CLeft / $FFFF, CBottom / $FFFF, CRight / $FFFF, CTop / $FFFF);
         curR.Bottom := curR.Top + (Font.Size * 1.33);

         d2OffsetRect(CurR, 0, i * Font.Size * 1.33);
         If i = 0 Then
            TextR := curR
         Else
            TextR := d2UnionRect(TextR, curR);
         xcurrentStart := xcurrentEnd;
      End;
   End;

   Case ATextAlign Of
      d2TextAlignCenter:
      Begin
         d2OffsetRect(TextR, -TextR.Left, 0);
         d2OffsetRect(TextR, Trunc((d2RectWidth(ARect) - d2RectWidth(TextR)) / 2), 0);
         d2OffsetRect(TextR, ARect.Left, 0);
      End;
      d2TextAlignNear:
      Begin
         d2OffsetRect(TextR, -TextR.Left, 0);
         d2OffsetRect(TextR, ARect.Left, 0);
      End;
      d2TextAlignFar:
      Begin
         d2OffsetRect(TextR, -TextR.Left, 0);
         d2OffsetRect(TextR, Trunc((d2RectWidth(ARect) - d2RectWidth(TextR))), 0);
         d2OffsetRect(TextR, ARect.Left, 0);
      End;
   End;
   Case AVTextAlign Of
      d2TextAlignCenter:
      Begin
         d2OffsetRect(TextR, 0, -TextR.Top);
         d2OffsetRect(TextR, 0, Trunc((d2RectHeight(ARect) - d2RectHeight(TextR)) / 2));
         d2OffsetRect(TextR, 0, ARect.Top);
      End;
      d2TextAlignNear:
      Begin
         d2OffsetRect(TextR, 0, -TextR.Top);
         d2OffsetRect(TextR, 0, ARect.Top);
      End;
      d2TextAlignFar:
      Begin
         d2OffsetRect(TextR, 0, -TextR.Top);
         d2OffsetRect(TextR, 0, Trunc((d2RectHeight(ARect) - d2RectHeight(TextR))));
         d2OffsetRect(TextR, 0, ARect.Top);
      End;
   End;

   ARect := TextR;

   ATSUDisposeTextLayout(xTextLayout);
   ATSUDisposeStyle(xStyles);
End;
