cocoa: implemented GDI region functions

git-svn-id: trunk@27245 -
This commit is contained in:
dmitry 2010-09-01 13:31:20 +00:00
parent c239bbb27d
commit 4adbe49f92
8 changed files with 549 additions and 27 deletions

1
.gitattributes vendored
View File

@ -4742,6 +4742,7 @@ lcl/interfaces/carbon/pascocoa/foundation/NSZone.inc svneol=native#text/plain
lcl/interfaces/carbon/pascocoa/foundation/foundation.pas svneol=native#text/plain
lcl/interfaces/cocoa/Makefile svneol=native#text/plain
lcl/interfaces/cocoa/Makefile.fpc svneol=native#text/plain
lcl/interfaces/cocoa/cocoagdiobjects.pas svneol=native#text/plain
lcl/interfaces/cocoa/cocoaint.pas svneol=native#text/plain
lcl/interfaces/cocoa/cocoaobject.inc svneol=native#text/plain
lcl/interfaces/cocoa/cocoaprivate.pp svneol=native#text/plain

View File

@ -0,0 +1,413 @@
unit CocoaGDIObjects;
//todo: Remove MacOSAll unit to prevent Carbon framework linking.
//todo: Remove HIShape usage used in TCocoaRegion.
interface
{$mode objfpc}{$H+}
{$modeswitch objectivec1}
uses
MacOSAll, // for CGContextRef
CocoaAll, CocoaUtils,
Classes, Types;
type
{ TCocoaGDIObject }
TCocoaGDIObject = class(TObject);
TCocoaRegionType = (crt_Empty, crt_Rectangle, crt_Complex);
TCocoaCombine = (cc_And, cc_Xor, cc_Or, cc_Diff, cc_Copy);
{ TCocoaRegion }
//todo: Remove HIShape usage. HIShape is legacy
TCocoaRegion = class(TCocoaGDIObject)
private
FShape: HIShapeRef;
public
constructor Create;
constructor Create(const X1, Y1, X2, Y2: Integer);
constructor Create(Points: PPoint; NumPts: Integer; isAlter: Boolean);
destructor Destroy; override;
procedure Apply(cg: CGContextRef);
function GetBounds: TRect;
function GetType: TCocoaRegionType;
function ContainsPoint(const P: TPoint): Boolean;
procedure SetShape(AShape: HIShapeRef);
function CombineWith(ARegion: TCocoaRegion; CombineMode: TCocoaCombine): Boolean;
public
property Shape: HIShapeRef read FShape write SetShape;
end;
{ TCocoaBrush }
TCocoaBrush = class(TCocoaGDIObject)
end;
{ TCocoaPen }
TCocoaPen = class(TCocoaGDIObject);
{ TCocoaContext }
TCocoaContext = class(TObject)
public
ctx : NSGraphicsContext;
PenPos : TPoint;
function InitDraw(width, height: Integer): Boolean;
procedure MoveTo(x,y: Integer);
procedure LineTo(x,y: Integer);
function CGContext: CGContextRef; virtual;
end;
implementation
{ TCocoaContext }
function TCocoaContext.CGContext:CGContextRef;
begin
Result:=CGContextRef(ctx.graphicsPort);
end;
function TCocoaContext.InitDraw(width,height:Integer): Boolean;
var
cg : CGContextRef;
begin
cg:=CGContext;
Result:=Assigned(cg);
if not Result then Exit;
CGContextTranslateCTM(cg, 0, height);
CGContextScaleCTM(cg, 1, -1);
PenPos.x:=0;
PenPos.y:=0;
end;
procedure TCocoaContext.MoveTo(x,y:Integer);
begin
PenPos.x:=x;
PenPos.y:=y;
end;
procedure TCocoaContext.LineTo(x,y:Integer);
var
cg : CGContextRef;
p : array [0..1] of CGPoint;
deltaX, deltaY, absDeltaX, absDeltaY: Integer;
clipDeltaX, clipDeltaY: Float32;
tx,ty:Float32;
begin
cg:=CGContext;
if not Assigned(cg) then Exit;
deltaX := X - PenPos.x;
deltaY := Y - PenPos.y;
if (deltaX=0) and (deltaY=0) then Exit;
absDeltaX := Abs(deltaX);
absDeltaY := Abs(deltaY);
if (absDeltaX<=1) and (absDeltaY<=1) then
begin
// special case for 1-pixel lines
tx := PenPos.x + 0.55;
ty := PenPos.y + 0.55;
end
else
begin
// exclude the last pixel from the line
if absDeltaX > absDeltaY then
begin
if deltaX > 0 then clipDeltaX := -1.0 else clipDeltaX := 1.0;
clipDeltaY := clipDeltaX * deltaY / deltaX;
end
else
begin
if deltaY > 0 then clipDeltaY := -1.0 else clipDeltaY := 1.0;
clipDeltaX := clipDeltaY * deltaX / deltaY;
end;
tx := X + clipDeltaX + 0.5;
ty := Y + clipDeltaY + 0.5;
end;
p[0].x:=PenPos.X+0.5;
p[0].y:=PenPos.Y+0.5;
p[1].x:=tx;
p[1].y:=ty;
CGContextAddLines(cg, @p, 2);
CGContextStrokePath(cg);
PenPos.x := X;
PenPos.y := Y;
end;
{ TCocoaRegion }
{------------------------------------------------------------------------------
Method: TCocoaRegion.Create
Creates a new empty Cocoa region
------------------------------------------------------------------------------}
constructor TCocoaRegion.Create;
begin
inherited Create;
FShape := HIShapeCreateEmpty;
end;
{------------------------------------------------------------------------------
Method: TCocoaRegion.Create
Params: X1, Y1, X2, Y2 - Region bounding rectangle
Creates a new rectangular Cocoa region
------------------------------------------------------------------------------}
constructor TCocoaRegion.Create(const X1, Y1, X2, Y2: Integer);
begin
inherited Create;
FShape := HIShapeCreateWithRect(GetCGRect(X1, Y1, X2, Y2));
end;
{------------------------------------------------------------------------------
Method: TCocoaRegion.Create
Params: Points - Pointer to array of polygon points
NumPts - Number of points passed
FillMode - Filling mode
Creates a new polygonal Cocoa region from the specified points
------------------------------------------------------------------------------}
constructor TCocoaRegion.Create(Points: PPoint; NumPts: Integer; isAlter: Boolean);
var
Bounds: TRect;
Context: CGContextRef;
W, H: Integer;
Data: Pointer;
PData: PByte;
P: PPoint;
I: Integer;
X, Y, SX: Integer;
LC, C: Byte;
//Line: String;
function GetPolygonBounds: TRect;
var
I: Integer;
begin
P := Points;
Result := Classes.Rect(P^.X, P^.Y, P^.X, P^.Y);
for I := 1 to NumPts - 1 do
begin
Inc(P);
if P^.X < Result.Left then Result.Left := P^.X;
if P^.X > Result.Right then Result.Right := P^.X;
if P^.Y < Result.Top then Result.Top := P^.Y;
if P^.Y > Result.Bottom then Result.Bottom := P^.Y;
end;
end;
procedure AddPart(X1, X2, Y: Integer);
var
R: HIShapeRef;
begin
//DebugLn('AddPart:' + DbgS(X1) + ' - ' + DbgS(X2) + ', ' + DbgS(Y));
R := HIShapeCreateWithRect(GetCGRect(X1, Y, X2, Y + 1));
HIShapeUnion(FShape, R, FShape);
CFRelease(R);
end;
begin
inherited Create;
(*
The passed polygon is drawed into grayscale context, the region is constructed
per rows from rectangles of drawed polygon parts.
*)
FShape := HIShapeCreateMutable;
if (NumPts <= 2) or (Points = nil) then Exit;
Bounds := GetPolygonBounds;
W := Bounds.Right - Bounds.Left + 2;
H := Bounds.Bottom - Bounds.Top + 2;
if (W <= 0) or (H <= 0) then Exit;
System.GetMem(Data, W * H);
System.FillChar(Data^, W * H, 0); // clear bitmap context data to black
try
Context := CGBitmapContextCreate(Data, W, H, 8, W, CGColorSpaceCreateDeviceGray,
kCGImageAlphaNone);
try
CGContextSetShouldAntialias(Context, 0); // disable anti-aliasing
CGContextSetGrayFillColor(Context, 1.0, 1.0); // draw white polygon
P := Points;
CGContextBeginPath(Context);
CGContextMoveToPoint(Context, P^.X, P^.Y);
for I := 1 to NumPts - 1 do
begin
Inc(P);
CGContextAddLineToPoint(Context, P^.X, P^.Y);
end;
CGContextClosePath(Context);
if isAlter then
CGContextEOFillPath(Context)
else
CGContextFillPath(Context);
//SetLength(Line, W);
PData := Data;
for Y := 0 to Pred(H) do
begin
LC := 0; // edge is black
for X := 0 to Pred(W) do
begin
C := PData^;
//Line[X + 1] := Chr(Ord('0') + C div 255);
if (C = $FF) and (LC = 0) then
SX := X; // start of painted row part
if (C = 0) and (LC = $FF) then
// end of painted row part (SX, X)
AddPart(SX, X, Pred(H) - Y);
LC := C;
Inc(PData);
end;
//DebugLn(DbgS(Pred(H) - Y) + ':' + Line);
end;
finally
CGContextRelease(Context);
end;
finally
System.FreeMem(Data);
end;
end;
{------------------------------------------------------------------------------
Method: TCocoaRegion.Destroy
Destroys Cocoa region
------------------------------------------------------------------------------}
destructor TCocoaRegion.Destroy;
begin
CFRelease(FShape);
inherited Destroy;
end;
{------------------------------------------------------------------------------
Method: TCocoaRegion.Apply
Params: ADC - Context to apply to
Applies region to the specified context
Note: Clipping region is only reducing
------------------------------------------------------------------------------}
procedure TCocoaRegion.Apply(cg: CGContextRef);
begin
if not Assigned(cg) then Exit;
if HIShapeIsEmpty(FShape) or (HIShapeReplacePathInCGContext(FShape, cg)<>noErr) then
Exit;
CGContextClip(cg);
end;
{------------------------------------------------------------------------------
Method: TCocoaRegion.GetBounds
Returns: The bounding box of Cocoa region
------------------------------------------------------------------------------}
function TCocoaRegion.GetBounds: TRect;
var
R: HIRect;
begin
if HIShapeGetBounds(FShape, R) = nil then begin
System.FillChar(Result, sizeof(Result), 0);
Exit;
end;
Result := CGRectToRect(R);
end;
{------------------------------------------------------------------------------
Method: TCocoaRegion.GetType
Returns: The type of Cocoa region
------------------------------------------------------------------------------}
function TCocoaRegion.GetType: TCocoaRegionType;
begin
if not Assigned(FShape) or HIShapeIsEmpty(FShape) then
Result := crt_Empty
else if HIShapeIsRectangular(FShape) then
Result := crt_Rectangle
else
Result := crt_Complex;
end;
{------------------------------------------------------------------------------
Method: TCocoaRegion.ContainsPoint
Params: P - Point
Returns: If the specified point lies in Cocoa region
------------------------------------------------------------------------------}
function TCocoaRegion.ContainsPoint(const P: TPoint): Boolean;
var
cp : CGPoint;
begin
cp.x:=P.x+0.5;
cp.y:=P.y+0.5;
Result := HIShapeContainsPoint(FShape, cp);
end;
procedure TCocoaRegion.SetShape(AShape: HIShapeRef);
begin
if Assigned(FShape) then CFRelease(FShape);
FShape := AShape;
end;
function TCocoaRegion.CombineWith(ARegion: TCocoaRegion; CombineMode: TCocoaCombine): Boolean;
var
sh1, sh2: HIShapeRef;
const
MinCoord=-35000;
MaxSize=65000;
begin
Result:=Assigned(ARegion);
if not Assigned(ARegion) then Exit;
if (CombineMode in [cc_AND, cc_OR, cc_XOR]) and HIShapeIsEmpty(FShape) then
CombineMode := cc_COPY;
case CombineMode of
cc_AND: Shape:=HIShapeCreateIntersection(FShape, ARegion.Shape);
cc_XOR:
begin
sh1 := HIShapeCreateUnion(FShape, ARegion.Shape);
sh2 := HIShapeCreateIntersection(FShape, ARegion.Shape);
Shape := HIShapeCreateDifference(sh1, sh2);
CFRelease(sh1); CFRelease(sh2);
end;
cc_OR: Shape:=HIShapeCreateUnion(FShape, ARegion.Shape);
cc_DIFF:
begin
if HIShapeIsEmpty(FShape) then
{HIShapeCreateDifference doesn't work properly if original shape is empty}
{to simulate "emptieness" very big shape is created }
Shape:=HIShapeCreateWithRect(GetCGRect(MinCoord,MinCoord,MaxSize,MaxSize)); // create clip nothing.
Shape:=HIShapeCreateDifference(FShape, ARegion.Shape);
end;
cc_COPY: Shape:=HIShapeCreateCopy(ARegion.Shape);
else
Result := false;
end;
end;
end.

View File

@ -37,7 +37,7 @@ uses
// interfacebase
InterfaceBase, GraphType,
// private
CocoaAll, CocoaPrivate, CocoaUtils,
CocoaAll, CocoaPrivate, CocoaUtils, CocoaGDIObjects,
// LCL
LCLStrConsts, LMessages, LCLMessageGlue, LCLProc, LCLIntf, LCLType,
CocoaWSFactory;
@ -106,6 +106,13 @@ var
implementation
function CheckDC(dc: HDC): TCocoaContext;
begin
//todo: a better check!
Result:=TCocoaContext(dc);
end;
// the implementation of the winapi compatibility methods
{$I cocoawinapi.inc}
// the implementation of the extra LCL interface methods

View File

@ -43,7 +43,7 @@ type
procedure MouseUp(x,y: Integer); virtual; abstract;
procedure MouseClick(ClickCount: Integer); virtual; abstract;
procedure MouseMove(x,y: Integer); virtual; abstract;
procedure Draw(ctx: NSGraphicsContext; r: NSRect); virtual; abstract;
procedure Draw(ctx: NSGraphicsContext; const bounds, dirty: NSRect); virtual; abstract;
end;
{ TWindowCallback }
@ -316,7 +316,7 @@ end;
procedure TCocoaCustomControl.drawRect(dirtyRect:NSRect);
begin
inherited drawRect(dirtyRect);
callback.Draw(NSGraphicsContext.currentContext, dirtyRect);
callback.Draw(NSGraphicsContext.currentContext, bounds, dirtyRect);
end;
end.

View File

@ -9,18 +9,11 @@ uses
MacOSAll, CocoaAll,
Types, LCLType;
type
{ TCocoaContext }
TCocoaContext = class(TObject)
public
ctx : NSGraphicsContext;
end;
function GetNSPoint(x,y: single): NSPoint; inline;
function GetCGRect(x1, y1, x2, y2: Integer): CGRect;
function CGRectToRect(const c: CGRect): TRect;
function GetNSRect(x, y, width, height: Integer): NSRect; inline;
function RectToNSRect(const r: TRect): NSRect;
procedure NSRectToRect(const ns: NSRect; var r: TRect);
@ -166,6 +159,24 @@ begin
Result.size.height:=height;
end;
function GetCGRect(x1, y1, x2, y2: Integer): CGRect;
begin
Result.origin.x:=x1;
Result.origin.y:=y1;
Result.size.width:=x2-x1;
Result.size.height:=y2-y1;
end;
function CGRectToRect(const c:CGRect):TRect;
begin
with Result do begin
Left:=round(c.origin.x);
Top:=round(c.origin.y);
Right:=round(c.origin.x+c.size.width);
Bottom:=round(c.origin.y+c.size.height);
end;
end;
function RectToNSRect(const r: TRect): NSRect;
begin
Result:=GetNSRect(r.Left,r.Top,r.Right-r.Left,r.Bottom-r.Top);

View File

@ -73,4 +73,89 @@ begin
{todo: GetNSViewFrame(NSView(Handle), ARect)};
end;
function TCocoaWidgetSet.LineTo(DC: HDC; X, Y: Integer): Boolean;
var
ctx : TCocoaContext;
begin
ctx:=CheckDC(DC);
Result:=Assigned(ctx);
if not Result then Exit;
ctx.MoveTo(x,y);
end;
function TCocoaWidgetSet.MoveToEx(DC: HDC; X, Y: Integer; OldPoint: PPoint): Boolean;
var
ctx : TCocoaContext;
begin
ctx:=CheckDC(DC);
Result:=Assigned(ctx);
if not Result then Exit;
if Assigned(OldPoint) then OldPoint^:=ctx.PenPos;
ctx.LineTo(x,y);
end;
{-------------------------- REGION ROUTINES -----------------------------------}
{------------------------------------------------------------------------------
Method: CreatePolygonRgn
Params: Points - Pointer to array of polygon points
NumPts - Number of points passed
FillMode - Filling mode
Returns: The new polygonal region
Creates a new polygonal region from the specified points
------------------------------------------------------------------------------}
function TCocoaWidgetSet.CreatePolygonRgn(Points: PPoint; NumPts: Integer;
FillMode: integer): HRGN;
begin
{$IFDEF VerboseWinAPI}
DebugLn('TCarbonWidgetSet.CreatePolygonRgn NumPts: ' + DbgS(NumPts) +
' FillMode: ' + DbgS(FillMode));
{$ENDIF}
Result := HRGN(TCocoaRegion.Create(Points, NumPts, FillMode=ALTERNATE));
end;
function CocoaCombineMode(fnCombineMode: Integer): TCocoaCombine;
begin
case fnCombineMode of
RGN_AND: Result:=cc_And;
RGN_OR: Result:=cc_Or;
RGN_XOR: Result:=cc_Xor;
RGN_DIFF: Result:=cc_Diff;
else
Result:=cc_Copy;
end;
end;
function TCocoaWidgetSet.CombineRgn(Dest, Src1, Src2: HRGN;
fnCombineMode: Longint): Longint;
begin
Result := LCLType.Error;
if (Dest = 0) or (Src1 = 0) or (fnCombineMode<RGN_AND) or (fnCombineMode>RGN_COPY) then Exit;
if (fnCombineMode <> RGN_COPY) and (Src2 = 0) then Exit;
TCocoaRegion(Dest).CombineWith(TCocoaRegion(Src1), cc_Copy);
if fnCombineMode <> RGN_COPY then
TCocoaRegion(Dest). CombineWith(TCocoaRegion(Src2), CocoaCombineMode(fnCombineMode));
end;
function TCocoaWidgetSet.CreateRectRgn(X1, Y1, X2, Y2: Integer): HRGN;
begin
{$IFDEF VerboseWinAPI}
DebugLn('TCarbonWidgetSet.CreateRectRgn R: ' + DbgS(Classes.Rect(X1, Y1, X2, Y2)));
{$ENDIF}
Result := HRGN(TCocoaRegion.Create(X1, Y1, X2, Y2));
end;
//todo:
//function TCocoaWidgetSet.CreateEllipticRgn(p1, p2, p3, p4: Integer): HRGN;
//begin
//end;
//##apiwiz##eps## // Do not remove, no wizard declaration after this line

View File

@ -46,10 +46,10 @@ function ClipboardGetFormats(ClipboardType: TClipboardType;
function ClipboardGetOwnerShip(ClipboardType: TClipboardType;
OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
Formats: PClipboardFormat): boolean; override;
function ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat; override;
function ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat; override;}
function CombineRgn(Dest, Src1, Src2: HRGN; fnCombineMode: Longint): Longint; override;
function CreateBitmap(Width, Height: Integer; Planes, BitCount: Longint; BitmapBits: Pointer): HBITMAP; override;
{function CreateBitmap(Width, Height: Integer; Planes, BitCount: Longint; BitmapBits: Pointer): HBITMAP; override;
function CreateBrushIndirect(const LogBrush: TLogBrush): HBRUSH; override;
function CreateCaret(Handle : HWND; Bitmap : hBitmap; Width, Height : Integer) : Boolean; override;
function CreateCompatibleBitmap(DC: HDC; Width, Height: Integer): HBITMAP; override;
@ -58,10 +58,10 @@ function CreateEllipticRgn(p1, p2, p3, p4: Integer): HRGN; override;
function CreateFontIndirect(const LogFont: TLogFont): HFONT; override;
function CreateFontIndirectEx(const LogFont: TLogFont; const LongFontName: string): HFONT; override;
function CreateIconIndirect(IconInfo: PIconInfo): HICON; override;
function CreatePenIndirect(const LogPen: TLogPen): HPEN; override;
function CreatePenIndirect(const LogPen: TLogPen): HPEN; override;}
function CreatePolygonRgn(Points: PPoint; NumPts: Integer; FillMode: integer): HRGN; override;
function CreateRectRgn(X1, Y1, X2, Y2: Integer): HRGN; override;
{
procedure DeleteCriticalSection(var CritSection: TCriticalSection); override;
function DeleteDC(hDC: HDC): Boolean; override;
function DeleteObject(GDIObject: HGDIOBJ): Boolean; override;
@ -135,13 +135,13 @@ function IntersectClipRect(dc: hdc; Left, Top, Right, Bottom: Integer): Integer;
function IsWindowEnabled(Handle: HWND): boolean; override;
function IsWindowVisible(Handle: HWND): boolean; override;
procedure LeaveCriticalSection(var CritSection: TCriticalSection); override;
procedure LeaveCriticalSection(var CritSection: TCriticalSection); override;}
function LineTo(DC: HDC; X, Y: Integer): Boolean; override;
function MessageBox(hWnd: HWND; lpText, lpCaption: PChar; uType: Cardinal): integer; override;
{function MessageBox(hWnd: HWND; lpText, lpCaption: PChar; uType: Cardinal): integer; override;}
function MoveToEx(DC: HDC; X, Y: Integer; OldPoint: PPoint): Boolean; override;
function PeekMessage(var lpMsg : TMsg; Handle : HWND; wMsgFilterMin, wMsgFilterMax,wRemoveMsg : UINT): Boolean; override;
{function PeekMessage(var lpMsg : TMsg; Handle : HWND; wMsgFilterMin, wMsgFilterMax,wRemoveMsg : UINT): Boolean; override;
function PolyBezier(DC: HDC; Points: PPoint; NumPts: Integer; Filled, Continuous: boolean): boolean; override;
function Polygon(DC: HDC; Points: PPoint; NumPts: Integer; Winding: boolean): boolean; override;
function Polyline(DC: HDC; Points: PPoint; NumPts: Integer): boolean; override;

View File

@ -10,7 +10,7 @@ uses
Classes,
Controls,
WSControls, LCLType,
CocoaPrivate, CocoaUtils, LCLMessageGlue;
CocoaPrivate, CocoaGDIObjects, CocoaUtils, LCLMessageGlue;
type
@ -26,7 +26,7 @@ type
procedure MouseUp(x,y: Integer); override;
procedure MouseClick(clickCount: Integer); override;
procedure MouseMove(x,y: Integer); override;
procedure Draw(ControlContext: NSGraphicsContext; dirty: NSRect); override;
procedure Draw(ControlContext: NSGraphicsContext; const bounds, dirty: NSRect); override;
end;
{ TCocoaWSWinControl }
@ -82,7 +82,6 @@ constructor TLCLCommonCallback.Create(AOwner: NSObject; ATarget: TControl);
begin
inherited Create(AOwner);
Target:=ATarget;
Context:=TCocoaContext.Create;
end;
destructor TLCLCommonCallback.Destroy;
@ -111,13 +110,19 @@ begin
LCLSendMouseMoveMsg(Target, x,y, []);
end;
procedure TLCLCommonCallback.Draw(ControlContext: NSGraphicsContext; dirty:NSRect);
procedure TLCLCommonCallback.Draw(ControlContext: NSGraphicsContext;
const bounds, dirty:NSRect);
var
struct : TPaintStruct;
begin
FillChar(struct, SizeOf(TPaintStruct), 0);
struct.hdc := HDC(Context);
LCLSendPaintMsg(Target, HDC(Context), @struct);
if not Assigned(Context) then Context:=TCocoaContext.Create;
Context.ctx:=ControlContext;
if Context.InitDraw(Round(bounds.size.width), Round(bounds.size.height)) then begin
FillChar(struct, SizeOf(TPaintStruct), 0);
struct.hdc := HDC(Context);
LCLSendPaintMsg(Target, HDC(Context), @struct);
end;
end;
{ TCocoaWSWinControl }