|
@@ -0,0 +1,2174 @@
|
|
|
+{
|
|
|
+ This file is part of the Fresnel Library.
|
|
|
+ Copyright (c) 2025 by the FPC & Lazarus teams.
|
|
|
+
|
|
|
+ Pas2js Fresnel interface - Webassembly rendering API, shared between worker and main page
|
|
|
+
|
|
|
+ See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
|
+ for details about the copyright.
|
|
|
+
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
+
|
|
|
+ **********************************************************************}
|
|
|
+{$mode objfpc}
|
|
|
+{$h+}
|
|
|
+{$modeswitch externalclass}
|
|
|
+{$modeswitch advancedrecords}
|
|
|
+
|
|
|
+{$DEFINE IMAGE_USEOSC}
|
|
|
+
|
|
|
+unit fresnel.shared.pas2js;
|
|
|
+
|
|
|
+// APIs and functionality that is common to Web & WebWorkers
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+// Define this to disable API Logging altogether
|
|
|
+{$DEFINE NOLOGAPICALLS}
|
|
|
+
|
|
|
+uses
|
|
|
+ SysUtils, Types,
|
|
|
+ wasienv, JS, WebOrWorker, webassembly,
|
|
|
+ fresnel.wasm.shared,
|
|
|
+ importextensionex.pas2js.wasmapi;
|
|
|
+
|
|
|
+type
|
|
|
+ TWasmFresnelSharedApi = class;
|
|
|
+
|
|
|
+ TWasmPointer = LongInt;
|
|
|
+
|
|
|
+ TTimerTickCallback = function (aTimerID : TTimerID; UserData : TWasmPointer) : Boolean;
|
|
|
+ TAnimationFrameCallback = procedure;
|
|
|
+ TUserMediaCallback = procedure (aUTF16Size : Integer; aUserData : TWasmPointer);
|
|
|
+ TUserMediaFrameCallback = procedure (aTimeStamp : TFresnelFloat; aVideoID : TVideoElementID; aImageBitmapID : Integer);
|
|
|
+ TProcessMessagesCallback = procedure (aCurrentTicks, aPreviousTicks : NativeInt);
|
|
|
+
|
|
|
+ TKeyKind = record
|
|
|
+ isSpecial : Boolean;
|
|
|
+ KeyCode : LongInt;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TJSFresnelFloat = class external name 'Number'
|
|
|
+ function toFixed(aDigits : Integer) : string;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { TFresnelHelper }
|
|
|
+
|
|
|
+ TFresnelHelper = class
|
|
|
+ public
|
|
|
+ class function FresnelColorToHTMLColor(aColor : TCanvasColor) : string; static;
|
|
|
+ class function FresnelColorToHTMLColor(aRed, aGreen, aBlue, aAlpha: TCanvasColorComponent): string; static;
|
|
|
+ class function RGBAToHTMLColor(aRed, aGreen, aBlue, aAlpha: Integer): string; static;
|
|
|
+
|
|
|
+ class function EncodeKeyboardShiftState(keyEvent: TJSObject): LongInt; static;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { TOffscreenCanvasReference }
|
|
|
+
|
|
|
+ TOffscreenCanvasReference = class (TObject)
|
|
|
+ public
|
|
|
+ API : TWasmFresnelSharedApi;
|
|
|
+ OffscreenCanvasID : TOffscreenCanvasID;
|
|
|
+ CanvasContext : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ Canvas : TJSHTMLOffscreenCanvas;
|
|
|
+ Scale : TFresnelFloat;
|
|
|
+
|
|
|
+ FontOffsets : TJSMap;
|
|
|
+ FontOffset : TFresnelFloat;
|
|
|
+
|
|
|
+ constructor Create(const aID : TOffscreenCanvasID; aAPI : TWasmFresnelSharedApi; aWidth, aHeight: LongInt; aScale : TFresnelFloat);
|
|
|
+ procedure Resize(aWidth, aHeight: Longint; aScale: TFresnelFloat);
|
|
|
+ procedure RemoveCanvas;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { TWindowEvent }
|
|
|
+
|
|
|
+ TWindowEvent = class (TJSObject)
|
|
|
+ public
|
|
|
+ WindowID : TWindowCanvasID;
|
|
|
+ Msg : TWindowMessageID;
|
|
|
+ Param0 : TWindowMessageParam;
|
|
|
+ Param1 : TWindowMessageParam;
|
|
|
+ Param2 : TWindowMessageParam;
|
|
|
+ Param3 : TWindowMessageParam;
|
|
|
+ NextEvent : TWindowEvent;
|
|
|
+
|
|
|
+ class function Create(const aWindowID : TWindowCanvasID; aMsg : TWindowMessageID) : TWindowEvent; static;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { TWasmFresnelSharedApi }
|
|
|
+
|
|
|
+ TDebugAPI = (daText,daClipRect);
|
|
|
+ TDebugAPIs = Set of TDebugAPI;
|
|
|
+
|
|
|
+ TWasmFresnelSharedApi = class (TImportExtensionEx)
|
|
|
+ private
|
|
|
+ FCreateDefaultCanvas: Boolean;
|
|
|
+ FDefaultCanvas : TOffscreenCanvasReference;
|
|
|
+
|
|
|
+ FDebugAPIs: TDebugAPIs;
|
|
|
+ FLogAPICalls : Boolean;
|
|
|
+
|
|
|
+ FEventQueueHead : TWindowEvent;
|
|
|
+ FEventQueueTail : TWindowEvent;
|
|
|
+ FEventCount : Integer;
|
|
|
+ FUseWordColors: Boolean;
|
|
|
+ class var vEventRecycler : TWindowEvent;
|
|
|
+
|
|
|
+ class var vLastOffscreenCanvasID : LongInt;
|
|
|
+ class var vLastImageBitmapID : LongInt;
|
|
|
+
|
|
|
+ protected
|
|
|
+ // reference to the import object, currently used to whitelist calls
|
|
|
+ FImportObject : TJSObject;
|
|
|
+
|
|
|
+ FOffscreenCanvases : TJSMap;
|
|
|
+ FGradients : TJSMap;
|
|
|
+ FImageBitmaps : TJSMap;
|
|
|
+
|
|
|
+ FEnumeratedUserMedia : String; // in IniFile format
|
|
|
+
|
|
|
+ FProcessMessagesCallback : JSValue;
|
|
|
+ FLastTick : NativeInt;
|
|
|
+
|
|
|
+ procedure LogCall(const Msg{%H-} : String);
|
|
|
+ procedure LogCall(Const Fmt{%H-} : String; const Args{%H-} : Array of const);
|
|
|
+
|
|
|
+ // call of one of the FImportObject functions
|
|
|
+ function ExecuteFunctionCall(const funcName: string; const args: TJSValueDynArray): TCanvasError;
|
|
|
+ // call main_thread_wake, only do this from the main thread
|
|
|
+ procedure MainThreadWake;
|
|
|
+
|
|
|
+
|
|
|
+ function StoreImageBitmap(const aImageBitmap : TJSImageBitmap) : Integer;
|
|
|
+ function DeleteImageBitmap(const aID : Integer) : Boolean;
|
|
|
+ function GetImageBitmap(const aID : Integer) : TJSImageBitmap;
|
|
|
+
|
|
|
+ procedure SetCreateDefaultCanvas(AValue: Boolean);
|
|
|
+
|
|
|
+ property EventCount : Integer read FEventCount;
|
|
|
+ procedure EnqueueEvent(aEvent : TWindowEvent); virtual;
|
|
|
+ function DequeueEvent : TWindowEvent;
|
|
|
+ class function NewEvent(aWindowID : TWindowCanvasID; aMessageID : TWindowMessageID) : TWindowEvent; static;
|
|
|
+ class procedure RecycleEvent(aEvent : TWindowEvent); static;
|
|
|
+
|
|
|
+ procedure ProcessMessages;
|
|
|
+
|
|
|
+ public
|
|
|
+ constructor Create(aEnv : TPas2JSWASIEnvironment); override;
|
|
|
+
|
|
|
+ property LogAPICalls : Boolean read FLogAPICalls write FLogAPICalls;
|
|
|
+
|
|
|
+ procedure FillImportObject(aObject : TJSObject); override;
|
|
|
+ function ImportName : String; override;
|
|
|
+
|
|
|
+ property DebugAPIs : TDebugAPIs read FDebugAPIs write FDebugAPIs;
|
|
|
+
|
|
|
+ property DefaultCanvas : TOffscreenCanvasReference read FDefaultCanvas;
|
|
|
+ property CreateDefaultCanvas : Boolean read FCreateDefaultCanvas Write SetCreateDefaultCanvas;
|
|
|
+ // Get info
|
|
|
+
|
|
|
+ function GetOffscreenCanvasRef(const aID: TOffscreenCanvasID): TOffscreenCanvasReference;
|
|
|
+ function GetOffscreenContext2D(const aID : TOffscreenCanvasID) : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ // Menu
|
|
|
+
|
|
|
+ function HandleMenuClick(aMenuID : TMenuID; aData : TWasmPointer) : Boolean; virtual; abstract;
|
|
|
+
|
|
|
+ // Debug
|
|
|
+
|
|
|
+ procedure DrawBaseLine(C: TJSOffscreenCanvasRenderingContext2D; S: String; X, Y: Double);
|
|
|
+ procedure DrawClipRect(aRef: TOffscreenCanvasReference; aX, aY, aWidth, aHeight: double);
|
|
|
+
|
|
|
+ // OffscreenCanvas
|
|
|
+
|
|
|
+ function AllocateOffscreenCanvas(aSizeX, aSizeY : LongInt; aScale : TFresnelFloat; aBitmap : TWasmPointer; aID: TWasmPointer): TCanvasError;
|
|
|
+ function DeAllocateOffscreenCanvas(const aID: TOffscreenCanvasID): TCanvasError;
|
|
|
+ function ResizeOffscreenCanvas(const aID: TOffscreenCanvasID; aSizeX, aSizeY : Longint; aScale : TFresnelFloat): TCanvasError;
|
|
|
+
|
|
|
+ function MoveTo(const aID : TOffscreenCanvasID; aX, aY : TFresnelFloat): TCanvasError;
|
|
|
+ function LineTo(const aID : TOffscreenCanvasID; aX, aY : TFresnelFloat): TCanvasError;
|
|
|
+ function Stroke(const aID : TOffscreenCanvasID): TCanvasError;
|
|
|
+ function BeginPath(const aID : TOffscreenCanvasID): TCanvasError;
|
|
|
+ function Arc(const aID : TOffscreenCanvasID; aX, aY, aRadiusX, aRadiusY, aStartAngle, aEndAngle, aRotate : TFresnelFloat; aFlags : LongInt): TCanvasError;
|
|
|
+ function FillRect(const aID : TOffscreenCanvasID; X, Y, Width, Height : TFresnelFloat): TCanvasError;
|
|
|
+ function StrokeRect(const aID : TOffscreenCanvasID; X, Y, Width, Height : TFresnelFloat ): TCanvasError;
|
|
|
+ function ClearRect(const aID : TOffscreenCanvasID; X, Y, Width, Height : TFresnelFloat ): TCanvasError;
|
|
|
+ function RoundRect(const aID : TOffscreenCanvasID; Flags : LongInt; Data : PFresnelFloat) : TCanvasError;
|
|
|
+ function StrokeText(const aID : TOffscreenCanvasID; X, Y : TFresnelFloat; aText : TWasmPointer; aTextLen : LongInt ): TCanvasError;
|
|
|
+ function FillText(const aID : TOffscreenCanvasID; aX, aY : TFresnelFloat; aText : TWasmPointer; aTextLen : LongInt; aOpacity : TFresnelFloat): TCanvasError;
|
|
|
+ function SetFillStyle(const aID : TOffscreenCanvasID; aRed,aGreen,aBlue,aAlpha: TCanvasColorComponent): TCanvasError;
|
|
|
+ function SetFillStyleString(const aID : TOffscreenCanvasID; aTextPtr : TWasmPointer; aTextLen : LongInt): TCanvasError;
|
|
|
+ function SetLinearGradientFillStyle(const aID : TOffscreenCanvasID; aStartX, aStartY, aEndX, aEndY : TFresnelFloat; aColorPointCount : LongInt; aColorPoints : TWasmPointer) : TCanvasError;
|
|
|
+ function SetImageFillStyle(const aID : TOffscreenCanvasID; Flags : LongInt; aImageWidth, aImageHeight: LongInt; aImageData: TWasmPointer) : TCanvasError;
|
|
|
+ function SetLineCap(const aID : TOffscreenCanvasID; aCap: TCanvasLinecap): TCanvasError;
|
|
|
+ function SetLineJoin(const aID : TOffscreenCanvasID; aJoin: TCanvasLineJoin): TCanvasError;
|
|
|
+ function SetLineMiterLimit(const aID : TOffscreenCanvasID; aWidth: TCanvasLineMiterLimit): TCanvasError;
|
|
|
+ function SetLineDash(const aID : TOffscreenCanvasID; aOffset : TFresnelFloat; aPatternCount : LongInt; aPattern : PFresnelFloat): TCanvasError;
|
|
|
+ function SetLineWidth(const aID : TOffscreenCanvasID; aWidth: TCanvasLineWidth): TCanvasError;
|
|
|
+ function SetTextBaseLine(const aID : TOffscreenCanvasID; aBaseLine: TCanvasTextBaseLine): TCanvasError;
|
|
|
+ function SetStrokeStyle(const aID : TOffscreenCanvasID; aRed,aGreen,aBlue,aAlpha: TCanvasColorComponent): TCanvasError;
|
|
|
+ function DrawImage(const aID : TOffscreenCanvasID; aX, aY, aWidth, aHeight: TFresnelFloat; aImageWidth, aImageHeight: LongInt; aImageData: TWasmPointer) : TCanvasError;
|
|
|
+ function DrawImageEx(const aID : TOffscreenCanvasID; DrawData : PFresnelFloat; aImageData: TWasmPointer): TCanvasError;
|
|
|
+
|
|
|
+ function DrawImageObject(const aID : TOffscreenCanvasID; DrawData : PFresnelFloat; aObject: TJSObject; aOpacity : TFresnelFloat): TCanvasError;
|
|
|
+ function DrawImageFromCanvas(const aID : TOffscreenCanvasID; DrawData : PFresnelFloat; const aSource: TOffscreenCanvasID; aOpacity : TFresnelFloat): TCanvasError;
|
|
|
+ function DrawImageFromImageBitmap(const aID : TOffscreenCanvasID; DrawData : PFresnelFloat; aImageBitmapID : Integer; aOpacity : TFresnelFloat): TCanvasError;
|
|
|
+ function ReleaseImageBitmap(aImageBitmapID : Integer): TCanvasError;
|
|
|
+ function GetImageBitmapSize(aImageBitmapID : Integer; aSizePtr : TWasmPointer): TCanvasError;
|
|
|
+
|
|
|
+ function SetFont(const aID : TOffscreenCanvasID; aFontName : TWasmPointer; aFontNameLen : integer) : TCanvasError;
|
|
|
+ function MeasureText(const aID : TOffscreenCanvasID; aText : TWasmPointer; aTextLen : integer; aMeasureData : TWasmPointer) : TCanvasError;
|
|
|
+ function SetTextShadowParams (const aID : TOffscreenCanvasID; aOffsetX, aOffsetY, aRadius : TFresnelFloat; aRed,aGreen,aBlue,aAlpha : TCanvasColorComponent): TCanvasError;
|
|
|
+
|
|
|
+ function CreatePath2D(aFlags : LongInt; aPathCount : LongInt; aPath : PFresnelFloat) : TJSPath2D;
|
|
|
+ function DrawPath(const aID : TOffscreenCanvasID; aFlags : LongInt; aPathCount : LongInt; aPath : PFresnelFloat) : TCanvasError;
|
|
|
+ function PointInPath(const aID : TOffscreenCanvasID; aX,aY : TFresnelFloat; aPathCount : Integer; aPath : PFresnelFloat; aRes : TWasmPointer): TCanvasError;
|
|
|
+
|
|
|
+ function SetTransform(const aID : TOffscreenCanvasID; m11,m12,m21,m22,m31,m32 : TFresnelFloat) : TCanvasError;
|
|
|
+
|
|
|
+ function SaveState(const aID : TOffscreenCanvasID) : TCanvasError;
|
|
|
+ function RestoreState(const aID : TOffscreenCanvasID) : TCanvasError;
|
|
|
+ function RestoreAndSaveState(const aID : TOffscreenCanvasID) : TCanvasError;
|
|
|
+ function ClipAddRect(const aID : TOffscreenCanvasID; aX,aY,aWidth,aHeight: TFresnelFloat): TCanvasError;
|
|
|
+ function ClipAddPolygon(const aID : TOffscreenCanvasID;
|
|
|
+ APolygonData : TWasmPointer; APolygonCount : Integer;
|
|
|
+ AClipQuadData : TWasmPointer; ANbClipQuads : Integer): TCanvasError;
|
|
|
+
|
|
|
+ // Timer
|
|
|
+
|
|
|
+ function AllocateTimer(ainterval : LongInt; userdata: TWasmPointer) : TTimerID;
|
|
|
+ procedure DeallocateTimer(timerid: TTimerID);
|
|
|
+
|
|
|
+ // Events
|
|
|
+
|
|
|
+ function GetEvent(aID: TWasmPointer; aMsg: TWasmPointer; Data : TWasmPointer): TCanvasError;
|
|
|
+ function GetEventCount(aCount: TWasmPointer): TCanvasError;
|
|
|
+
|
|
|
+ // Debug
|
|
|
+
|
|
|
+ procedure ConsoleLog(aText : TWasmPointer; aTextLen : integer);
|
|
|
+
|
|
|
+ // UserMedia
|
|
|
+
|
|
|
+ function GetEnumeratedUserMedia(aUTF16SizePtr, aDataUTF16 : TWasmPointer) : TCanvasError;
|
|
|
+ // Use word-sized colors
|
|
|
+ property UseWordColors : Boolean Read FUseWordColors Write FUseWordColors;
|
|
|
+ end;
|
|
|
+
|
|
|
+// --------------------------------------------------------------------
|
|
|
+// --------------------------------------------------------------------
|
|
|
+// --------------------------------------------------------------------
|
|
|
+implementation
|
|
|
+// --------------------------------------------------------------------
|
|
|
+// --------------------------------------------------------------------
|
|
|
+// --------------------------------------------------------------------
|
|
|
+
|
|
|
+{ TFresnelHelper }
|
|
|
+
|
|
|
+// RGBAToHTMLMColor
|
|
|
+//
|
|
|
+class function TFresnelHelper.RGBAToHTMLColor(aRed, aGreen, aBlue, aAlpha: Integer): string;
|
|
|
+begin
|
|
|
+ Result := '#' + TJSNumber(JSValue((aRed shl 16) or (aGreen shl 8) or aBlue)).toString(16).padStart(6, '0');
|
|
|
+ if aAlpha < 255 then
|
|
|
+ Result += TJSNumber(JSValue(aAlpha)).toString(16).padStart(2, '0');
|
|
|
+end;
|
|
|
+
|
|
|
+// EncodeKeyboardShiftState
|
|
|
+//
|
|
|
+class function TFresnelHelper.EncodeKeyboardShiftState(keyEvent: TJSObject): LongInt;
|
|
|
+begin
|
|
|
+ Result := 0;
|
|
|
+ if keyEvent['shiftKey'] then Result += WASM_KEYSTATE_SHIFT;
|
|
|
+ if keyEvent['ctrlKey'] then Result += WASM_KEYSTATE_CTRL;
|
|
|
+ if keyEvent['altKey'] then Result += WASM_KEYSTATE_ALT;
|
|
|
+ if keyEvent['metaKey'] then Result += WASM_KEYSTATE_META;
|
|
|
+end;
|
|
|
+
|
|
|
+// FresnelColorToHTMLColor (components)
|
|
|
+//
|
|
|
+class function TFresnelHelper.FresnelColorToHTMLColor(aRed,aGreen,aBlue,aAlpha: TCanvasColorComponent): string;
|
|
|
+begin
|
|
|
+ Result:='rgb('+IntTostr(aRed shr 8)+' '+IntToStr(aGreen shr 8)+' '+inttoStr(aBlue shr 8);
|
|
|
+ if aAlpha<>$FFFF then
|
|
|
+ Result:=Result+' / '+floatToStr(aAlpha/$FFFF);
|
|
|
+ Result:=Result+')';
|
|
|
+end;
|
|
|
+
|
|
|
+// FresnelColorToHTMLColor (TCanvasColor)
|
|
|
+//
|
|
|
+class function TFresnelHelper.FresnelColorToHTMLColor(aColor: TCanvasColor): string;
|
|
|
+const
|
|
|
+ cHex = '0123456789ABCDEF';
|
|
|
+var
|
|
|
+ I : Integer;
|
|
|
+begin
|
|
|
+ Result:='#';
|
|
|
+ aColor:=aColor shr 8;
|
|
|
+ for I:=1 to 6 do
|
|
|
+ begin
|
|
|
+ Result:=Result+cHex[(aColor and $F)+1];
|
|
|
+ aColor:=aColor shr 4;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TWindowEvent }
|
|
|
+
|
|
|
+class function TWindowEvent.Create(const aWindowID: TWindowCanvasID; aMsg: TWindowMessageID): TWindowEvent;
|
|
|
+begin
|
|
|
+ Result := TWasmFresnelSharedApi.NewEvent(aWindowID, aMsg);
|
|
|
+end;
|
|
|
+
|
|
|
+{ TOffscreenCanvasReference }
|
|
|
+
|
|
|
+// Create
|
|
|
+//
|
|
|
+constructor TOffscreenCanvasReference.Create(
|
|
|
+ const aID: TOffscreenCanvasID;
|
|
|
+ aAPI: TWasmFresnelSharedApi;
|
|
|
+ aWidth, aHeight: LongInt;
|
|
|
+ aScale : TFresnelFloat
|
|
|
+ );
|
|
|
+begin
|
|
|
+ API := aAPI;
|
|
|
+ OffscreenCanvasID := aID;
|
|
|
+ Canvas := TJSHTMLOffscreenCanvas.New(Round(aWidth * aScale), Round(aHeight * aScale));
|
|
|
+ CanvasContext := Canvas.getContextAs2DContext('2d');
|
|
|
+ Scale := aScale;
|
|
|
+ FontOffsets := TJSMap.new;
|
|
|
+end;
|
|
|
+
|
|
|
+// Resize
|
|
|
+//
|
|
|
+procedure TOffscreenCanvasReference.Resize(aWidth, aHeight: Longint; aScale: TFresnelFloat);
|
|
|
+begin
|
|
|
+ Canvas.width := Round(aWidth * aScale);
|
|
|
+ Canvas.height := Round(aHeight * aScale);
|
|
|
+ Scale := aScale;
|
|
|
+end;
|
|
|
+
|
|
|
+// RemoveCanvas
|
|
|
+//
|
|
|
+procedure TOffscreenCanvasReference.RemoveCanvas;
|
|
|
+begin
|
|
|
+ // Offscreen canvas are GC'ed
|
|
|
+ Canvas := nil;
|
|
|
+ CanvasContext := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TWasmFresnelSharedApi }
|
|
|
+
|
|
|
+// TWasmFresnelSharedApi
|
|
|
+//
|
|
|
+constructor TWasmFresnelSharedApi.Create(aEnv: TPas2JSWASIEnvironment);
|
|
|
+begin
|
|
|
+ inherited Create(aEnv);
|
|
|
+
|
|
|
+ FOffscreenCanvases := TJSMap.New();
|
|
|
+ FGradients := TJSMap.New();
|
|
|
+ FImageBitmaps := TJSMap.New();
|
|
|
+
|
|
|
+ FLogAPICalls := True;
|
|
|
+end;
|
|
|
+
|
|
|
+// LogCall (str)
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.LogCall(const Msg: String);
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ Writeln(Msg);
|
|
|
+ {$ENDIF}
|
|
|
+end;
|
|
|
+
|
|
|
+// LogCall (fmt, ...)
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.LogCall(const Fmt: String; const Args: array of const);
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ Writeln(Format(Fmt,Args));
|
|
|
+ {$ENDIF}
|
|
|
+end;
|
|
|
+
|
|
|
+// ExecuteFunctionCall
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.ExecuteFunctionCall(const funcName: string; const args: TJSValueDynArray): TCanvasError;
|
|
|
+var
|
|
|
+ callFunction : TJSFunction;
|
|
|
+ resultValue : JSValue;
|
|
|
+begin
|
|
|
+ callFunction := TJSFunction(FImportObject[funcName]);
|
|
|
+
|
|
|
+ if JSValue(callFunction) then
|
|
|
+ begin
|
|
|
+ resultValue := callFunction.apply(TJSObject(Self), args);
|
|
|
+ if isNumber(resultvalue) then
|
|
|
+ result:=Integer(resultValue)
|
|
|
+ else
|
|
|
+ result:=0;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ raise Exception.CreateFmt('Unsupported function "%s"', [ funcName ]);
|
|
|
+end;
|
|
|
+
|
|
|
+// MainThreadWake
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.MainThreadWake;
|
|
|
+var
|
|
|
+ callback : JSValue;
|
|
|
+begin
|
|
|
+ // if we reach here, we're in the appropriate thread, wake up the wasm
|
|
|
+ callback := InstanceExports['__fresnel_main_thread_wake'];
|
|
|
+ if callback then
|
|
|
+ TProcedure(callback)();
|
|
|
+end;
|
|
|
+
|
|
|
+// GetOffscreenCanvasRef
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.GetOffscreenCanvasRef(const aID: TOffscreenCanvasID): TOffscreenCanvasReference;
|
|
|
+var
|
|
|
+ jsRef : JSValue;
|
|
|
+ resultRef : TOffscreenCanvasReference absolute jsRef;
|
|
|
+begin
|
|
|
+ jsRef := FOffscreenCanvases.get(aID);
|
|
|
+ if jsRef then
|
|
|
+ Result := resultRef
|
|
|
+ else
|
|
|
+ Result := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+// GetOffscreenContext2D
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.GetOffscreenContext2D(const aID: TOffscreenCanvasID): TJSOffscreenCanvasRenderingContext2D;
|
|
|
+var
|
|
|
+ Ref : TOffscreenCanvasReference;
|
|
|
+begin
|
|
|
+ Ref := GetOffscreenCanvasRef(aID);
|
|
|
+ if Assigned(Ref) then
|
|
|
+ Result := Ref.CanvasContext
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ Console.Warn('Fresnel: Unknown canvas : ', aID.ToString);
|
|
|
+ Result := nil;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+// StoreImageBitmap
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.StoreImageBitmap(const aImageBitmap: TJSImageBitmap): Integer;
|
|
|
+begin
|
|
|
+ Inc(vLastImageBitmapID);
|
|
|
+ Result := vLastImageBitmapID;
|
|
|
+ FImageBitmaps.&set(Result, aImageBitmap);
|
|
|
+end;
|
|
|
+
|
|
|
+// DeleteImageBitmap
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.DeleteImageBitmap(const aID: Integer) : Boolean;
|
|
|
+var
|
|
|
+ lImageBitmap : TJSImageBitmap;
|
|
|
+begin
|
|
|
+ lImageBitmap := TJSImageBitmap(FImageBitmaps.get(aID));
|
|
|
+ if JSValue(lImageBitmap) then
|
|
|
+ begin
|
|
|
+ FImageBitmaps.delete(aID);
|
|
|
+ lImageBitmap.close();
|
|
|
+ Result := True;
|
|
|
+ end else Result := False;
|
|
|
+end;
|
|
|
+
|
|
|
+// GetImageBitmap
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.GetImageBitmap(const aID: Integer): TJSImageBitmap;
|
|
|
+begin
|
|
|
+ Result := TJSImageBitmap(FImageBitmaps.get(aID));
|
|
|
+ if not JSValue(Result) then
|
|
|
+ Result := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetCreateDefaultCanvas
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.SetCreateDefaultCanvas(AValue: Boolean);
|
|
|
+var
|
|
|
+ defaultCanvasID : TOffscreenCanvasID;
|
|
|
+begin
|
|
|
+ if FCreateDefaultCanvas = AValue then Exit;
|
|
|
+
|
|
|
+ FCreateDefaultCanvas := AValue;
|
|
|
+ if FCreateDefaultCanvas and (DefaultCanvas = nil) then
|
|
|
+ begin
|
|
|
+ // TODO: merge with AllocateOffscreenCanvas
|
|
|
+ Inc(vLastOffscreenCanvasID);
|
|
|
+ defaultCanvasID := vLastOffscreenCanvasID;
|
|
|
+ FDefaultCanvas := TOffscreenCanvasReference.Create(defaultCanvasID, Self, 128, 64, 1);
|
|
|
+ FOffscreenCanvases.&set(defaultCanvasID, FDefaultCanvas);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+// EnqueueEvent
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.EnqueueEvent(aEvent: TWindowEvent);
|
|
|
+begin
|
|
|
+ Inc(FEventCount);
|
|
|
+ if FEventQueueHead = nil then
|
|
|
+ begin
|
|
|
+ FEventQueueHead := aEvent;
|
|
|
+ FEventQueueTail := aEvent;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ FEventQueueTail.NextEvent := aEvent;
|
|
|
+ FEventQueueTail := aEvent;
|
|
|
+ end;
|
|
|
+ ProcessMessages;
|
|
|
+end;
|
|
|
+
|
|
|
+// DequeueEvent
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.DequeueEvent: TWindowEvent;
|
|
|
+begin
|
|
|
+ if FEventQueueHead = nil then Exit(nil);
|
|
|
+
|
|
|
+ Dec(FEventCount);
|
|
|
+ Result := FEventQueueHead;
|
|
|
+ FEventQueueHead := Result.NextEvent;
|
|
|
+ Result.NextEvent := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+// NewEvent
|
|
|
+//
|
|
|
+class function TWasmFresnelSharedApi.NewEvent(aWindowID: TWindowCanvasID; aMessageID: TWindowMessageID): TWindowEvent;
|
|
|
+begin
|
|
|
+ if vEventRecycler = nil then
|
|
|
+ begin
|
|
|
+ Result := TWindowEvent(TJSObject.new);
|
|
|
+ Result.NextEvent := nil;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ Result := vEventRecycler;
|
|
|
+ vEventRecycler := Result.NextEvent;
|
|
|
+ Result.NextEvent := nil;
|
|
|
+ end;
|
|
|
+ Result.WindowID := aWindowID;
|
|
|
+ Result.Msg := aMessageID;
|
|
|
+end;
|
|
|
+
|
|
|
+// RecycleEvent
|
|
|
+//
|
|
|
+class procedure TWasmFresnelSharedApi.RecycleEvent(aEvent: TWindowEvent);
|
|
|
+begin
|
|
|
+ aEvent.NextEvent := vEventRecycler;
|
|
|
+ vEventRecycler := aEvent;
|
|
|
+ aEvent.Param0 := 0;
|
|
|
+ aEvent.Param1 := 0;
|
|
|
+ aEvent.Param2 := 0;
|
|
|
+ aEvent.Param3 := 0;
|
|
|
+end;
|
|
|
+
|
|
|
+// ProcessMessages
|
|
|
+//
|
|
|
+
|
|
|
+procedure TWasmFresnelSharedApi.ProcessMessages;
|
|
|
+
|
|
|
+ procedure Prepare;
|
|
|
+ begin
|
|
|
+ if not Assigned(InstanceExports) then
|
|
|
+ Console.Error('No instance exports !')
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ FProcessMessagesCallback := InstanceExports['__fresnel_process_message'];
|
|
|
+ if not FProcessMessagesCallback then
|
|
|
+ Console.Error('No processmessages callback !');
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+var
|
|
|
+ newTick : NativeInt;
|
|
|
+begin
|
|
|
+ if not FProcessMessagesCallback then
|
|
|
+ Prepare;
|
|
|
+
|
|
|
+ newTick := TJSDate.now;
|
|
|
+ TProcessMessagesCallback(FProcessMessagesCallback)(FLastTick, newTick);
|
|
|
+ FLastTick := newTick;
|
|
|
+end;
|
|
|
+
|
|
|
+// FillImportObject
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.FillImportObject(aObject: TJSObject);
|
|
|
+begin
|
|
|
+ FImportObject := aObject;
|
|
|
+
|
|
|
+ // Canvas
|
|
|
+
|
|
|
+ aObject['canvas_allocate_offscreen'] := @AllocateOffscreenCanvas;
|
|
|
+ aObject['canvas_deallocate_offscreen'] := @DeAllocateOffscreenCanvas;
|
|
|
+ aObject['canvas_resize_offscreen'] := @ResizeOffscreenCanvas;
|
|
|
+
|
|
|
+ aObject['canvas_moveto'] := @Moveto;
|
|
|
+ aObject['canvas_lineto'] := @LineTo;
|
|
|
+ aObject['canvas_stroke'] := @Stroke;
|
|
|
+ aObject['canvas_beginpath'] := @BeginPath;
|
|
|
+ aObject['canvas_arc'] := @Arc;
|
|
|
+ aObject['canvas_fillrect'] := @fillrect;
|
|
|
+ aObject['canvas_strokerect'] := @strokerect;
|
|
|
+ aObject['canvas_clearrect'] := @ClearRect;
|
|
|
+ aObject['canvas_stroketext'] := @StrokeText;
|
|
|
+ aObject['canvas_filltext'] := @FillText;
|
|
|
+ aObject['canvas_set_fillstyle'] := @SetFillStyle;
|
|
|
+ aObject['canvas_set_fillstyle_string'] := @SetFillStyleString;
|
|
|
+ aObject['canvas_linear_gradient_fillstyle'] := @SetLinearGradientFillStyle;
|
|
|
+ aObject['canvas_image_fillstyle'] := @SetImageFillStyle;
|
|
|
+ aObject['canvas_set_strokestyle'] := @SetStrokeStyle;
|
|
|
+ aObject['canvas_set_linewidth'] := @SetLineWidth;
|
|
|
+ aObject['canvas_set_linecap'] := @SetLineCap;
|
|
|
+ aObject['canvas_set_linejoin'] := @SetLineJoin;
|
|
|
+ aObject['canvas_set_linemiterlimit'] := @SetLineMiterLimit;
|
|
|
+ aObject['canvas_set_linedash'] := @SetLineDash;
|
|
|
+ aObject['canvas_set_textbaseline'] := @SetTextBaseLine;
|
|
|
+ aObject['canvas_draw_image'] := @DrawImage;
|
|
|
+ aObject['canvas_draw_image_ex'] := @DrawImageEx;
|
|
|
+ aObject['canvas_draw_image_from_canvas'] := @DrawImageFromCanvas;
|
|
|
+ aObject['canvas_draw_imagebitmap'] := @DrawImageFromImageBitmap;
|
|
|
+ aObject['canvas_release_imagebitmap'] := @ReleaseImageBitmap;
|
|
|
+ aObject['canvas_imagebitmap_getsize'] := @GetImageBitmapSize;
|
|
|
+ aObject['canvas_set_font'] := @SetFont;
|
|
|
+ aObject['canvas_measure_text'] := @MeasureText;
|
|
|
+ aObject['canvas_set_textshadow_params'] := @SetTextShadowParams;
|
|
|
+ aObject['canvas_roundrect'] := @RoundRect;
|
|
|
+ aObject['canvas_draw_path'] := @DrawPath;
|
|
|
+ aObject['canvas_point_in_path'] := @PointInPath;
|
|
|
+ aObject['canvas_set_transform'] := @SetTransForm;
|
|
|
+ aObject['canvas_save_state'] := @SaveState;
|
|
|
+ aObject['canvas_restore_state'] := @RestoreState;
|
|
|
+ aObject['canvas_restore_and_save_state'] := @RestoreAndSaveState;
|
|
|
+ aObject['canvas_clip_add_rect'] := @ClipAddRect;
|
|
|
+ aObject['canvas_clip_add_polygon'] := @ClipAddPolygon;
|
|
|
+
|
|
|
+ // Timer
|
|
|
+ aObject['timer_allocate'] := @AllocateTimer;
|
|
|
+ aObject['timer_deallocate'] := @DeAllocateTimer;
|
|
|
+
|
|
|
+ // Event
|
|
|
+ aObject['event_get'] := @GetEvent;
|
|
|
+ aObject['event_count'] := @GetEventCount;
|
|
|
+
|
|
|
+ // Debug
|
|
|
+ aObject['console_log'] := @ConsoleLog;
|
|
|
+end;
|
|
|
+
|
|
|
+// ImportName
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.ImportName: String;
|
|
|
+begin
|
|
|
+ Result:='fresnel_api';
|
|
|
+end;
|
|
|
+
|
|
|
+// DrawBaseLine
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.DrawBaseLine(C: TJSOffscreenCanvasRenderingContext2D; S: String; X, Y: Double);
|
|
|
+var
|
|
|
+ M : TJSTextMetrics;
|
|
|
+ Style : JSValue;
|
|
|
+begin
|
|
|
+ if daText in DebugApis then
|
|
|
+ begin
|
|
|
+ M:=C.measureText(S);
|
|
|
+ Style:=C.StrokeStyle;
|
|
|
+ C.StrokeStyle:='rgb(255 0 0 /1)';
|
|
|
+ C.beginPath;
|
|
|
+ C.moveTo(X,Y);
|
|
|
+ C.lineTo(X+M.width,y);
|
|
|
+ C.stroke;
|
|
|
+ C.StrokeStyle:=Style;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+// DrawClipRect
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.DrawClipRect(aRef: TOffscreenCanvasReference; aX, aY, aWidth, aHeight: double);
|
|
|
+var
|
|
|
+ context : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ context := aRef.CanvasContext;
|
|
|
+ context.save;
|
|
|
+ context.strokeStyle := 'rgb(255 0 0)';
|
|
|
+ context.lineWidth := 1;
|
|
|
+ context.strokeRect(aX-1, aY-1, aWidth+2, aHeight+2);
|
|
|
+ context.restore;
|
|
|
+end;
|
|
|
+
|
|
|
+// AllocateOffscreenCanvas
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.AllocateOffscreenCanvas(
|
|
|
+ aSizeX, aSizeY: LongInt;
|
|
|
+ aScale : TFresnelFloat;
|
|
|
+ aBitmap: TWasmPointer;
|
|
|
+ aID: TWasmPointer
|
|
|
+ ) : TCanvasError;
|
|
|
+var
|
|
|
+ canvasRef : TOffscreenCanvasReference;
|
|
|
+
|
|
|
+ procedure PutBitmapOnCanvas;
|
|
|
+ var
|
|
|
+ dataArray : TJSUint8ClampedArray;
|
|
|
+ imgData : TJSImageData;
|
|
|
+ imgCanvas : TJSHTMLOffscreenCanvas;
|
|
|
+ scaledSizeX, scaledSizeY : Integer;
|
|
|
+ begin
|
|
|
+ dataArray := TJSUint8ClampedArray.New(MemoryDataView.buffer, aBitmap, aSizeX*aSizeY*4);
|
|
|
+ imgData := TJSImageData.new(TJSUint8ClampedArray(dataArray.slice), aSizeX, aSizeY);
|
|
|
+
|
|
|
+ scaledSizeX := canvasRef.Canvas.width;
|
|
|
+ scaledSizeY := canvasRef.Canvas.height;
|
|
|
+ if (scaledSizeX <> aSizeX) or (scaledSizeY <> aSizeY) then
|
|
|
+ begin
|
|
|
+ imgCanvas := TJSHTMLOffscreenCanvas.New(scaledSizeX, scaledSizeY);
|
|
|
+ imgCanvas.getContextAs2DContext('2d').putImageData(imgData, 0, 0);
|
|
|
+ canvasRef.CanvasContext.drawImage(imgCanvas, 0, 0, scaledSizeX, scaledSizeY);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ canvasRef.CanvasContext.putImageData(imgData, 0, 0);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+var
|
|
|
+ canvasID : TOffscreenCanvasID;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.AllocateOffScreenCanvas(%d,%d,[%x])', [aSizeX, aSizeY, aBitMap]);
|
|
|
+ {$ENDIF}
|
|
|
+
|
|
|
+ WebAssemblyMemory := Env.Memory.buffer;
|
|
|
+
|
|
|
+ Inc(vLastOffscreenCanvasID);
|
|
|
+ canvasID := vLastOffscreenCanvasID;
|
|
|
+
|
|
|
+ canvasRef := TOffscreenCanvasReference.Create(canvasID, Self, aSizeX, aSizeY, aScale);
|
|
|
+ FOffscreenCanvases.&set(canvasID, canvasRef);
|
|
|
+ MemoryDataView.setUint32(aID, canvasID, env.IsLittleEndian);
|
|
|
+
|
|
|
+ if aBitmap <> 0 then
|
|
|
+ PutBitmapOnCanvas;
|
|
|
+
|
|
|
+ canvasRef.CanvasContext.scale(aScale, aScale);
|
|
|
+
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+
|
|
|
+ WebAssemblyMemory := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+// DeAllocateOffscreenCanvas
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.DeAllocateOffscreenCanvas(const aID: TOffscreenCanvasID): TCanvasError;
|
|
|
+var
|
|
|
+ lCanvasRef : TOffscreenCanvasReference;
|
|
|
+begin
|
|
|
+ lCanvasRef := GetOffscreenCanvasRef(aID);
|
|
|
+ if lCanvasRef = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ FOffscreenCanvases.delete(aID);
|
|
|
+ lCanvasRef.Free;
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// ResizeOffscreenCanvas
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.ResizeOffscreenCanvas(const aID: TOffscreenCanvasID; aSizeX, aSizeY: Longint; aScale: TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ lCanvasRef : TOffscreenCanvasReference;
|
|
|
+begin
|
|
|
+ lCanvasRef := GetOffscreenCanvasRef(aID);
|
|
|
+ if lCanvasRef = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ lCanvasRef.Resize(aSizeX, aSizeY, aScale);
|
|
|
+ // resizing canvas resets states
|
|
|
+ lCanvasRef.CanvasContext.scale(aScale, aScale);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// MoveTo
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.MoveTo(const aID: TOffscreenCanvasID; aX, aY: TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.MoveTo(%d,%g,%g)',[aID,aX,aY]);
|
|
|
+ {$ENDIF}
|
|
|
+ Result := ECANVAS_NOCANVAS;
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if Assigned(C) then
|
|
|
+ begin
|
|
|
+ C.moveTo(aX, aY);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+// LineTo
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.LineTo(const aID: TOffscreenCanvasID; aX, aY: TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.LineTo(%d,%g,%g)',[aID,aX,aY]);
|
|
|
+ {$ENDIF}
|
|
|
+ Result:=ECANVAS_NOCANVAS;
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if Assigned(C) then
|
|
|
+ begin
|
|
|
+ C.lineto(aX, aY);
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+// Strokg
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.Stroke(const aID: TOffscreenCanvasID): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.Stroke(%d)',[aID]);
|
|
|
+ {$ENDIF}
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(C) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ C.Stroke;
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// BeginPath
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.BeginPath(const aID: TOffscreenCanvasID): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ If LogAPICalls then
|
|
|
+ LogCall('Canvas.BeginPath(%d)',[aID]);
|
|
|
+ {$ENDIF}
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(C) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ C.beginPath;
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// Arc
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.Arc(
|
|
|
+ const aID: TOffscreenCanvasID;
|
|
|
+ aX, aY, aRadiusX, aRadiusY, aStartAngle, aEndAngle, aRotate: TFresnelFloat;
|
|
|
+ aFlags: LongInt
|
|
|
+ ): TCanvasError;
|
|
|
+var
|
|
|
+ lCtx : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.Arc(%d,%g,%g,%g,%g,%g,%g)',[aID,X,Y,RadiusX,RadiusY,StartAngle,EndAngle]);
|
|
|
+ {$ENDIF}
|
|
|
+ lCtx := GetOffscreenContext2D(aID);
|
|
|
+ if lCtx = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ lCtx.beginPath;
|
|
|
+ // Arc is about 4x faster than Ellipse on Chromium as of march 2025
|
|
|
+ if aRadiusX = aRadiusY then
|
|
|
+ lCtx.Arc(aX, aY, aRadiusX, aStartangle, aEndAngle)
|
|
|
+ else
|
|
|
+ lCtx.Ellipse(aX, aY, aRadiusX, aRadiusY, aRotate, aStartangle, aEndAngle);
|
|
|
+ if (aFlags and ARC_FILL) <> 0 then
|
|
|
+ lCtx.fill()
|
|
|
+ else lCtx.stroke();
|
|
|
+
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// FillRect
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.FillRect(const aID: TOffscreenCanvasID; X, Y, Width,
|
|
|
+ Height: TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.FillRect(%d,%g,%g,%g,%g)',[aID,X,Y,Width,Height]);
|
|
|
+ {$ENDIF}
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(C) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ C.FillRect(x, y, width, height);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// StrokeRect
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.StrokeRect(const aID: TOffscreenCanvasID;
|
|
|
+ X, Y, Width, Height: TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.StrokeRect(%d,%g,%g,%g,%g)',[aID,X,Y,Width,Height]);
|
|
|
+ {$ENDIF}
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(C) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ C.StrokeRect(X,Y,Width,Height);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// ClearRect
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.ClearRect(const aID: TOffscreenCanvasID; X, Y, Width,
|
|
|
+ Height: TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.ClearRect(%d,%g,%g,%g,%g)',[aID,X,Y,Width,Height]);
|
|
|
+ {$ENDIF}
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(C) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ C.ClearRect(X,Y,Width,Height);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// RoundRect
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.RoundRect(const aID: TOffscreenCanvasID;
|
|
|
+ Flags: LongInt; Data: PFresnelFloat): TCanvasError;
|
|
|
+Var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ V : TJSDataView;
|
|
|
+ X,Y,W,H : TFresnelFloat;
|
|
|
+ Radii : TJSArray;
|
|
|
+ Fill : Boolean;
|
|
|
+
|
|
|
+ function GetElement(aOffset : LongInt) : TFresnelFloat;
|
|
|
+ begin
|
|
|
+ Result:=V.getFloat32(Data+(aOffset*SizeFloat32),Env.IsLittleEndian);
|
|
|
+ end;
|
|
|
+
|
|
|
+ procedure AddRadius(aRX,aRY : Double);
|
|
|
+ begin
|
|
|
+ Radii.Push(New(['x',aRX,'y',aRY]))
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ If LogAPICalls then
|
|
|
+ LogCall('Canvas.RoundRect(%d,%d,[%d])',[aID,Flags,Data]);
|
|
|
+ {$ENDIF}
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(C) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ V:=MemoryDataView;
|
|
|
+ X:=GetElement(ROUNDRECT_BOXTOPLEFTX);
|
|
|
+ Y:=GetElement(ROUNDRECT_BOXTOPLEFTY);
|
|
|
+ W:=GetElement(ROUNDRECT_BOXBOTTOMRIGHTX)-X;
|
|
|
+ H:=GetElement(ROUNDRECT_BOXBOTTOMRIGHTY)-Y;
|
|
|
+ Fill:=(Flags and ROUNDRECT_FLAG_FILL)<>0;
|
|
|
+ Radii:=TJSArray.New;
|
|
|
+ AddRadius(GetElement(ROUNDRECT_RADIITOPLEFTX),GetElement(ROUNDRECT_RADIITOPLEFTY));
|
|
|
+ AddRadius(GetElement(ROUNDRECT_RADIITOPRIGHTX),GetElement(ROUNDRECT_RADIITOPRIGHTY));
|
|
|
+ AddRadius(GetElement(ROUNDRECT_RADIIBOTTOMRIGHTX),GetElement(ROUNDRECT_RADIIBOTTOMRIGHTY));
|
|
|
+ AddRadius(GetElement(ROUNDRECT_RADIIBOTTOMLEFTX),GetElement(ROUNDRECT_RADIIBOTTOMLEFTY));
|
|
|
+ C.BeginPath;
|
|
|
+ C.roundRect(X,Y,W,H,Radii);
|
|
|
+ if Fill then
|
|
|
+ C.fill;
|
|
|
+ C.stroke();
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// StrokeText
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.StrokeText(const aID: TOffscreenCanvasID; X,
|
|
|
+ Y: TFresnelFloat; aText: TWasmPointer; aTextLen: LongInt): TCanvasError;
|
|
|
+var
|
|
|
+ C : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ S : String;
|
|
|
+begin
|
|
|
+ S := GetUTF16FromMem(aText, aTextLen);
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.StrokeText(%d,(%g,%g),''%s'')',[aID,X,Y,S]);
|
|
|
+ {$ENDIF}
|
|
|
+ C := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(C) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ C.StrokeText(S,X,Y);
|
|
|
+ if daText in DebugApis then
|
|
|
+ DrawBaseLine(C,S,X,Y);
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// FillText
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.FillText(
|
|
|
+ const aID: TOffscreenCanvasID;
|
|
|
+ aX, aY: TFresnelFloat;
|
|
|
+ aText: TWasmPointer; aTextLen: LongInt;
|
|
|
+ aOpacity : TFresnelFloat
|
|
|
+ ): TCanvasError;
|
|
|
+var
|
|
|
+ lCanvas : TOffscreenCanvasReference;
|
|
|
+ lContext : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ lText : String;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.FillText(%d,(%g,%g),''%s'')', [ aID, aX, Y, lText ]);
|
|
|
+ {$ENDIF}
|
|
|
+ lCanvas := GetOffscreenCanvasRef(aID);
|
|
|
+ if lCanvas = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ lContext := lCanvas.CanvasContext;
|
|
|
+
|
|
|
+ lText := GetUTF16FromMem(aText, aTextLen);
|
|
|
+ if aOpacity <> 1 then
|
|
|
+ lContext.globalAlpha := aOpacity;
|
|
|
+
|
|
|
+ aY += lCanvas.FontOffset;
|
|
|
+
|
|
|
+ lContext.FillText(lText, aX, aY);
|
|
|
+ if daText in DebugApis then
|
|
|
+ DrawBaseLine(lContext, lText, aX, aY);
|
|
|
+ if aOpacity <> 1 then
|
|
|
+ lContext.globalAlpha := 1;
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetFillStyle
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetFillStyle(const aID : TOffscreenCanvasID; aRed, aGreen, aBlue, aAlpha: TCanvasColorComponent): TCanvasError;
|
|
|
+var
|
|
|
+ lContext : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetFillStyle(%d,%d,%d,%d,%d)',[aID,aRed,aGreen,aBlue,aAlpha]);
|
|
|
+ {$ENDIF}
|
|
|
+ lContext := GetOffscreenContext2D(aID);
|
|
|
+ if lContext = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ if UseWordColors then
|
|
|
+ lContext.fillStyle := TFresnelHelper.FresnelColorToHTMLColor(aRed, aGreen, aBlue, aAlpha)
|
|
|
+ else
|
|
|
+ lContext.fillStyle := TFresnelHelper.RGBAToHTMLColor(aRed, aGreen, aBlue, aAlpha);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetFillStyleString
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetFillStyleString(
|
|
|
+ const aID: TOffscreenCanvasID;
|
|
|
+ aTextPtr: TWasmPointer; aTextLen: LongInt
|
|
|
+ ): TCanvasError;
|
|
|
+var
|
|
|
+ lCanvasRef : TOffscreenCanvasReference;
|
|
|
+ lContext : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ lStyle : String;
|
|
|
+
|
|
|
+ procedure SetGradientFillStyle;
|
|
|
+ const
|
|
|
+ cMAX_GRADIENTS = 32; // completely arbitrary max number of gradients to keep around
|
|
|
+ var
|
|
|
+ gradient : JSValue;
|
|
|
+ parameters : TJSValueDynArray;
|
|
|
+ p, i : Integer;
|
|
|
+ begin
|
|
|
+ gradient := FGradients.get(lStyle);
|
|
|
+ if not gradient then
|
|
|
+ begin
|
|
|
+ if FGradients.size > cMAX_GRADIENTS then
|
|
|
+ FGradients.clear;
|
|
|
+ p := TJSString(lStyle).indexOf(' ');
|
|
|
+ if TJSString(lStyle).startsWith('gradient.linear') then
|
|
|
+ begin
|
|
|
+ parameters := TJSValueDynArray(TJSJSON.parse('[' + TJSString(lStyle).slice(p+1) + ']'));
|
|
|
+ //gradient := TJSFunction(@lContext.createLinearGradient).apply(...);
|
|
|
+ gradient := TJSFunction(TJSObject(lContext)['createLinearGradient']).apply(
|
|
|
+ lContext, TJSValueDynArray(parameters[0]));
|
|
|
+ for i := 1 to High(parameters) do
|
|
|
+ begin
|
|
|
+ TJSFunction(TJSObject(gradient)['addColorStop']).apply(TJSObject(gradient), TJSValueDynArray(parameters[i]));
|
|
|
+ end;
|
|
|
+ FGradients.&set(lStyle, gradient);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ lContext.fillStyle := gradient;
|
|
|
+ end;
|
|
|
+
|
|
|
+ procedure SetBitmapFillStyle;
|
|
|
+ var
|
|
|
+ parameters : TJSValueDynArray;
|
|
|
+ p : Integer;
|
|
|
+ bitmapID : TOffscreenCanvasID;
|
|
|
+ pattern : TJSCanvasPattern;
|
|
|
+ bitmapRef : TOffscreenCanvasReference;
|
|
|
+ wrapMode : Integer;
|
|
|
+ tempCanvas: TJSHTMLOffscreenCanvas;
|
|
|
+ tempCtx: TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ begin
|
|
|
+ p := TJSString(lStyle).indexOf(' ');
|
|
|
+ parameters := TJSValueDynArray(TJSJSON.parse('[' + TJSString(lStyle).slice(p+1) + ']'));
|
|
|
+ bitmapID := Integer(parameters[0]);
|
|
|
+ wrapMode := Integer(parameters[1]);
|
|
|
+ bitmapRef := GetOffscreenCanvasRef(bitmapID);
|
|
|
+
|
|
|
+ case wrapMode of
|
|
|
+ 0: // Tile
|
|
|
+ pattern := lContext.createPattern(bitmapRef.Canvas, 'repeat');
|
|
|
+
|
|
|
+ 1: // TileOriginal
|
|
|
+ begin
|
|
|
+ lContext.save;
|
|
|
+ // Reset any transform that might affect the pattern scaling
|
|
|
+ lContext.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
+ pattern := lContext.createPattern(bitmapRef.Canvas, 'repeat');
|
|
|
+ lContext.restore;
|
|
|
+ end;
|
|
|
+
|
|
|
+ 2: // TileStretch
|
|
|
+ begin
|
|
|
+ tempCanvas := TJSHTMLOffscreenCanvas.new(lContext.canvas.width, lContext.canvas.height);
|
|
|
+ tempCtx := TJSOffscreenCanvasRenderingContext2D(tempCanvas.getContext('2d'));
|
|
|
+ tempCtx.drawImage(bitmapRef.Canvas, 0, 0, tempCanvas.width, tempCanvas.height);
|
|
|
+ pattern := lContext.createPattern(tempCanvas, 'repeat');
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ lContext.fillStyle := pattern;
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ lStyle := GetUTF16FromMem(aTextPtr, aTextLen);
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetFillStyle(%d,"%s")',[aID,lStyle]);
|
|
|
+ {$ENDIF}
|
|
|
+
|
|
|
+ lCanvasRef := GetOffscreenCanvasRef(aID);
|
|
|
+ if lCanvasRef = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ lContext := lCanvasRef.CanvasContext;
|
|
|
+
|
|
|
+ if TJSString(lStyle).startsWith('gradient') then
|
|
|
+ SetGradientFillStyle
|
|
|
+ else if TJSString(lStyle).startsWith('bitmap') then
|
|
|
+ SetBitmapFillStyle
|
|
|
+ else lContext.fillStyle := GetUTF16FromMem(aTextPtr, aTextLen);
|
|
|
+
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetLinearGradientFillStyle
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetLinearGradientFillStyle(
|
|
|
+ const aID: TOffscreenCanvasID;
|
|
|
+ aStartX, aStartY, aEndX, aEndY: TFresnelFloat;
|
|
|
+ aColorPointCount: LongInt; aColorPoints: TWasmPointer
|
|
|
+ ) : TCanvasError;
|
|
|
+var
|
|
|
+ i,P : LongInt;
|
|
|
+ Red,Green,Blue,Alpha: LongInt;
|
|
|
+ offset : double;
|
|
|
+ lGradient : TJSCanvasGradient;
|
|
|
+ lContext : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ lDataView : TJSDataView;
|
|
|
+ lColor : String;
|
|
|
+
|
|
|
+ function GetLongInt: LongInt;
|
|
|
+ begin
|
|
|
+ Result := lDataView.getInt32(P, Env.IsLittleEndian);
|
|
|
+ Inc(P, SizeInt32);
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetLinearGradientFillStyle(%d,(%g,%g),(%g,%g),%d,[%x])',[aID,aStartX, aStartY, aEndX, aEndY, aColorPointCount,aColorPoints]);
|
|
|
+ {$ENDIF}
|
|
|
+ lContext := GetOffscreenContext2D(aID);
|
|
|
+ if lContext = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ lGradient := lContext.createLinearGradient(aStartX, aStartY, aEndX, aEndY);
|
|
|
+ lDataView := MemoryDataView;
|
|
|
+ P := aColorPoints;
|
|
|
+ for i := 0 to aColorPointCount-1 do
|
|
|
+ begin
|
|
|
+ Red := GetLongInt;
|
|
|
+ Green := GetLongInt;
|
|
|
+ Blue := GetLongInt;
|
|
|
+ Alpha := GetLongInt;
|
|
|
+ offset := GetLongInt/10000;
|
|
|
+ lColor := TFresnelHelper.FresnelColorToHTMLColor(Red, Green, Blue, Alpha);
|
|
|
+ lGradient.addColorStop(offset, lColor);
|
|
|
+ end;
|
|
|
+ lContext.fillStyleAsGradient := lGradient;
|
|
|
+ Exit(ECANVAS_SUCCESS);
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.SetImageFillStyle(const aID: TOffscreenCanvasID; Flags: LongInt;
|
|
|
+ aImageWidth, aImageHeight: LongInt; aImageData: TWasmPointer): TCanvasError;
|
|
|
+var
|
|
|
+ OSC : TJSHTMLOffscreenCanvas;
|
|
|
+ ImgData : TJSImageData;
|
|
|
+// OSCImgBitmap : TJSImageBitmap;
|
|
|
+ Canv,Canv2 : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ D : TJSUint8ClampedArray;
|
|
|
+ V : TJSDataView;
|
|
|
+ S : String;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetImageFillStyle(%d,%d,(%d,%d),[%x])',[aID,flags,aImageWidth,aImageHeight,aImageData]);
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if Not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ V:=MemoryDataView;
|
|
|
+ D:=TJSUint8ClampedArray.New(V.Buffer,aImageData,aImageWidth*aImageWidth*4);
|
|
|
+ ImgData:=TJSImageData.new(TJSUint8ClampedArray(D.slice), aImageWidth, aImageWidth);
|
|
|
+ OSC:=TJSHTMLOffscreenCanvas.New(aImageWidth,aImageHeight);
|
|
|
+ Canv2:=OSC.getContextAs2DContext('2d');
|
|
|
+ Canv2.ClearRect(0,0,aImageWidth,aImageHeight);
|
|
|
+ Canv2.putImageData(ImgData,0,0);
|
|
|
+ Case flags and 3 of
|
|
|
+ IMAGEFILLSTYLE_NOREPEAT : s:='no-repeat';
|
|
|
+ IMAGEFILLSTYLE_REPEAT : s:='repeat';
|
|
|
+ IMAGEFILLSTYLE_REPEATX : s:='repeat-x';
|
|
|
+ IMAGEFILLSTYLE_REPEATY : s:='repeat-y';
|
|
|
+ end;
|
|
|
+ Canv.fillStyleAsPattern:=Canv.createPattern(OSC,S);
|
|
|
+end;
|
|
|
+
|
|
|
+// SetLineCap
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetLineCap(const aID: TOffscreenCanvasID; aCap : TCanvasLinecap): TCanvasError;
|
|
|
+var
|
|
|
+ Canv : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ S : String;
|
|
|
+begin
|
|
|
+ S:=LineCapToString(aCap);
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetLineCap(%d,%s)',[aID,S]);
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Canv.lineCap:=S;
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.SetLineJoin(const aID: TOffscreenCanvasID; aJoin : TCanvasLineJoin): TCanvasError;
|
|
|
+
|
|
|
+var
|
|
|
+ Canv:TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ S : String;
|
|
|
+
|
|
|
+begin
|
|
|
+ S:=LineJoinToString(aJoin);
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ If LogAPICalls then
|
|
|
+ begin
|
|
|
+ LogCall('Canvas.SetLineJoin(%d,%s)',[aID,S]);
|
|
|
+ end;
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if Not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Canv.lineJoin:=S;
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.SetLineMiterLimit(const aID: TOffscreenCanvasID; aWidth : TCanvasLineMiterLimit): TCanvasError;
|
|
|
+
|
|
|
+var
|
|
|
+ Canv:TJSOffscreenCanvasRenderingContext2D;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetLineMiterLimit(%d,%d)',[aID,aWidth]);
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if Not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Canv.miterLimit:=aWidth;
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+ Writeln('Canvas.SetLineMiterLimit not implemented');
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.SetLineDash(const aID: TOffscreenCanvasID; aOffset: TFresnelFloat;
|
|
|
+ aPatternCount: LongInt; aPattern: PFresnelFloat): TCanvasError;
|
|
|
+
|
|
|
+var
|
|
|
+ Dashes : TJSArray;
|
|
|
+ V : TJSDataView;
|
|
|
+ I : Integer;
|
|
|
+ P : TWasmPointer;
|
|
|
+ Canv : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ If LogAPICalls then
|
|
|
+ begin
|
|
|
+ LogCall('Canvas.SetLineDash(%d,%g,%d,[%x])',[aID,aOffset,aPatternCount,aPattern]);
|
|
|
+ end;
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if Not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Dashes:=TJSArray.New;
|
|
|
+ if aPatternCount>0 then
|
|
|
+ begin
|
|
|
+ V:=MemoryDataView;
|
|
|
+ P:=aPattern;
|
|
|
+ for I:=0 to APatternCount-1 do
|
|
|
+ begin
|
|
|
+ Dashes.Push(v.GetFloat32(P,env.IsLittleEndian));
|
|
|
+ Inc(P,SizeFloat32);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ Canv.lineDashOffset:=aOffset;
|
|
|
+ Canv.setLineDash(Dashes);
|
|
|
+end;
|
|
|
+
|
|
|
+// SetLineWidth
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetLineWidth(const aID : TOffscreenCanvasID; aWidth : TCanvasLineWidth): TCanvasError;
|
|
|
+var
|
|
|
+ Canv : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetLineWidth(%d,%g)',[aID,aWidth]);
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Canv.LineWidth:=aWidth;
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetTextBaseLine
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetTextBaseLine(const aID: TOffscreenCanvasID; aBaseLine: TCanvasTextBaseLine): TCanvasError;
|
|
|
+var
|
|
|
+ Ref :TOffscreenCanvasReference;
|
|
|
+ S : String;
|
|
|
+begin
|
|
|
+ S := TextBaseLineToString(aBaseLine);
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetTextBaseLine(%d,%s)',[aID,S]);
|
|
|
+ {$ENDIF}
|
|
|
+ Ref := GetOffscreenCanvasRef(aID);
|
|
|
+ if not Assigned(Ref) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Ref.CanvasContext.TextBaseLine := S;
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetStrokeStyle
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetStrokeStyle(const aID: TOffscreenCanvasID; aRed, aGreen, aBlue, aAlpha: TCanvasColorComponent): TCanvasError;
|
|
|
+var
|
|
|
+ Ref : TOffscreenCanvasReference;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetStrokeStyle(%d,%d,%d,%d,%d)',[aID,aRed,aGreen,aBlue,aAlpha]);
|
|
|
+ {$ENDIF}
|
|
|
+ Ref := GetOffscreenCanvasRef(aID);
|
|
|
+ if not Assigned(Ref) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ if UseWordColors then
|
|
|
+ Ref.CanvasContext.StrokeStyle := TFresnelHelper.FresnelColorToHTMLColor(aRed, aGreen, aBlue, aAlpha)
|
|
|
+ else
|
|
|
+ Ref.CanvasContext.StrokeStyle := TFresnelHelper.RGBAToHTMLColor(aRed, aGreen, aBlue, aAlpha);
|
|
|
+
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.DrawImage(const aID: TOffscreenCanvasID; aX, aY,
|
|
|
+ aWidth, aHeight: TFresnelFloat; aImageWidth, aImageHeight: LongInt;
|
|
|
+ aImageData: TWasmPointer): TCanvasError;
|
|
|
+
|
|
|
+var
|
|
|
+ V : TJSDataView;
|
|
|
+ D : TJSUint8ClampedArray;
|
|
|
+ ImgData : TJSImageData;
|
|
|
+ Canv : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+
|
|
|
+{$IFDEF IMAGE_USEOSC}
|
|
|
+ Canv2 : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ OSC : TJSHTMLOffscreenCanvas;
|
|
|
+{$ENDIF}
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ If LogAPICalls then
|
|
|
+ begin
|
|
|
+ LogCall('Canvas.DrawImage(%d,(%g,%g),(%gx%g),(%dx%d)',[aID,aX,aY,aWidth,aHeight,aImageWidth,aImageHeight]);
|
|
|
+ end;
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if Not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ V:=MemoryDataView;
|
|
|
+ D:=TJSUint8ClampedArray.New(V.Buffer,aImageData,aImageWidth*aImageWidth*4);
|
|
|
+ ImgData:=TJSImageData.new(TJSUint8ClampedArray(D.slice), aImageWidth, aImageWidth);
|
|
|
+{$IFDEF IMAGE_USEOSC}
|
|
|
+ OSC := TJSHTMLOffscreenCanvas.New(aImageWidth,aImageHeight);
|
|
|
+ Canv2:=OSC.getContextAs2DContext('2d');
|
|
|
+ Canv2.ClearRect(0,0,aImageWidth,aImageHeight);
|
|
|
+ Canv2.putImageData(ImgData,0,0);
|
|
|
+ Canv.drawImage(OSC,aX,aY,aWidth,aHeight);
|
|
|
+{$ELSE}
|
|
|
+Window.createImageBitmap(ImgData)._then(
|
|
|
+ function (res : jsvalue) : JSValue
|
|
|
+ var
|
|
|
+ ImgBitmap : TJSImageBitmap absolute res;
|
|
|
+ begin
|
|
|
+ Canv.drawImage(ImgBitmap,aX,aY,aWidth,aHeight);
|
|
|
+ end);
|
|
|
+{$ENDIF}
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.DrawImageEx(const aID: TOffscreenCanvasID; DrawData: PFresnelFloat; aImageData: TWasmPointer): TCanvasError;
|
|
|
+
|
|
|
+var
|
|
|
+ V : TJSDataView;
|
|
|
+ D : TJSUint8ClampedArray;
|
|
|
+ ImgData : TJSImageData;
|
|
|
+ Canv : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ aSrcX,aSrcY,aSrcWidth,aSrcHeight,aDestX,aDestY,aDestWidth,aDestHeight :Double;
|
|
|
+ aImageWidth,aImageHeight : LongInt;
|
|
|
+
|
|
|
+{$IFDEF IMAGE_USEOSC}
|
|
|
+ Canv2 : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ OSC : TJSHTMLOffscreenCanvas;
|
|
|
+{$ENDIF}
|
|
|
+
|
|
|
+ Function GetD(aIdx : Integer) : LongInt;
|
|
|
+ begin
|
|
|
+ Result:=Round(V.getFloat32(DrawData+aIdx*SizeFloat32,Env.IsLittleEndian));
|
|
|
+ end;
|
|
|
+
|
|
|
+ Function GetS(aIdx : Integer) : Double;
|
|
|
+ begin
|
|
|
+ Result:=v.getFloat32(DrawData+aIdx*SizeFloat32,Env.IsLittleEndian);
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ If LogAPICalls then
|
|
|
+ LogCall('Canvas.DrawImageEx(%d,[%x],[%x])',[aID,DrawData,aImageData]);
|
|
|
+ {$ENDIF}
|
|
|
+
|
|
|
+ Canv := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ V:=MemoryDataView;
|
|
|
+ aDestX:=GetS(DRAWIMAGE_DESTX);
|
|
|
+ aDestY:=GetS(DRAWIMAGE_DESTY);
|
|
|
+ aDestWidth:=GetS(DRAWIMAGE_DESTWIDTH);
|
|
|
+ aDestHeight:=GetS(DRAWIMAGE_DESTHEIGHT);
|
|
|
+ aSrcX:=GetS(DRAWIMAGE_SRCX);
|
|
|
+ aSrcY:=GetS(DRAWIMAGE_SRCY);
|
|
|
+ aSrcWidth:=GetS(DRAWIMAGE_SRCWIDTH);
|
|
|
+ aSrcHeight:=GetS(DRAWIMAGE_SRCHEIGHT);
|
|
|
+ aImageWidth:=GetD(DRAWIMAGE_IMAGEWIDTH);
|
|
|
+ aImageHeight:=GetD(DRAWIMAGE_IMAGEHEIGHT);
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ If LogAPICalls then
|
|
|
+ begin
|
|
|
+ LogCall('Canvas.DrawImage(%d,[(%g,%g) - (%gx%g)],[(%g,%g) - (%gx%g)],[%dx%d])',[aID,aSrcX,aSrcY,aSrcWidth,aSrcHeight,aDestX,aDestY,aDestWidth,aDestHeight,aImageWidth,aImageWidth]);
|
|
|
+ end;
|
|
|
+ {$ENDIF}
|
|
|
+ D:=TJSUint8ClampedArray.New(V.Buffer,aImageData,aImageWidth*aImageWidth*4);
|
|
|
+ ImgData:=TJSImageData.new(TJSUint8ClampedArray(D.slice), aImageWidth, aImageWidth);
|
|
|
+
|
|
|
+{$IFDEF IMAGE_USEOSC}
|
|
|
+ OSC:=TJSHTMLOffscreenCanvas.New(aImageWidth,aImageHeight);
|
|
|
+ Canv2:=OSC.getContextAs2DContext('2d');
|
|
|
+ Canv2.ClearRect(0,0,aImageWidth,aImageHeight);
|
|
|
+ Canv2.putImageData(ImgData,0,0);
|
|
|
+ Canv.drawImage(OSC,aSrcX,aSrcY,aSrcWidth,aSrcHeight,aDestX,aDestY,aDestWidth,aDestHeight);
|
|
|
+{$ELSE}
|
|
|
+Window.createImageBitmap(ImgData)._then(
|
|
|
+ function (res : jsvalue) : JSValue
|
|
|
+ var
|
|
|
+ ImgBitmap : TJSImageBitmap absolute res;
|
|
|
+ begin
|
|
|
+ Canv.drawImage(ImgBitmap,aX,aY,aWidth,aHeight);
|
|
|
+ end);
|
|
|
+{$ENDIF}
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// DrawImageObject
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.DrawImageObject(const aID: TOffscreenCanvasID; DrawData : PFresnelFloat; aObject: TJSObject; aOpacity : TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ V : TJSDataView;
|
|
|
+ Canv : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ aSrcX,aSrcY,aSrcWidth,aSrcHeight,aDestX,aDestY,aDestWidth,aDestHeight :Double;
|
|
|
+
|
|
|
+
|
|
|
+ function GetS(aIdx : Integer) : Double;
|
|
|
+ begin
|
|
|
+ Result:=v.getFloat32(DrawData+aIdx*SizeFloat32,Env.IsLittleEndian);
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ Canv := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ V:=MemoryDataView;
|
|
|
+ aDestX:=GetS(DRAWIMAGE_DESTX);
|
|
|
+ aDestY:=GetS(DRAWIMAGE_DESTY);
|
|
|
+ aDestWidth:=GetS(DRAWIMAGE_DESTWIDTH);
|
|
|
+ aDestHeight:=GetS(DRAWIMAGE_DESTHEIGHT);
|
|
|
+ aSrcX:=GetS(DRAWIMAGE_SRCX);
|
|
|
+ aSrcY:=GetS(DRAWIMAGE_SRCY);
|
|
|
+ aSrcWidth:=GetS(DRAWIMAGE_SRCWIDTH);
|
|
|
+ aSrcHeight:=GetS(DRAWIMAGE_SRCHEIGHT);
|
|
|
+
|
|
|
+ if aOpacity < 1 then
|
|
|
+ Canv.GlobalAlpha := aOpacity;
|
|
|
+ Canv.drawImage(aObject, aSrcX, aSrcY, aSrcWidth, aSrcHeight, aDestX, aDestY, aDestWidth, aDestHeight);
|
|
|
+ if aOpacity < 1 then
|
|
|
+ Canv.GlobalAlpha := 1;
|
|
|
+
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// DrawImageFromCanvas
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.DrawImageFromCanvas(
|
|
|
+ const aID: TOffscreenCanvasID; DrawData : PFresnelFloat;
|
|
|
+ const aSource: TOffscreenCanvasID; aOpacity : TFresnelFloat
|
|
|
+ ): TCanvasError;
|
|
|
+var
|
|
|
+ LSourceCanvas : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.DrawImageFromCanvas(%d,[%x],[%x])', [ aID, DrawData, aImageData ]);
|
|
|
+ {$ENDIF}
|
|
|
+
|
|
|
+ LSourceCanvas := GetOffscreenContext2D(aSource);
|
|
|
+ if not Assigned(LSourceCanvas) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ Result := DrawImageObject(aID, DrawData, LSourceCanvas.canvas, aOpacity);
|
|
|
+end;
|
|
|
+
|
|
|
+// DrawImageFromImageBitmap
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.DrawImageFromImageBitmap(const aID: TOffscreenCanvasID; DrawData : PFresnelFloat; aImageBitmapID : Integer; aOpacity : TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ imageBitmap : TJSImageBitmap;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.DrawImageFromImageBitmap(%d,[%x],%d)', [ aID, DrawData, aImageBitmapID ]);
|
|
|
+ {$ENDIF}
|
|
|
+ imageBitmap := GetImageBitmap(aImageBitmapID);
|
|
|
+ if imageBitmap <> nil then
|
|
|
+ Result := DrawImageObject(aID, DrawData, imageBitmap, aOpacity)
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ writeln('no ImageBitmap of id ', aImageBitmapID);
|
|
|
+ Result := ECANVAS_INVALIDPARAM;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+// ReleaseImageBitmap
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.ReleaseImageBitmap(aImageBitmapID: Integer): TCanvasError;
|
|
|
+begin
|
|
|
+ if DeleteImageBitmap(aImageBitmapID) then
|
|
|
+ Result := ECANVAS_SUCCESS
|
|
|
+ else Result := ECANVAS_NOIMAGEBITMAP;
|
|
|
+end;
|
|
|
+
|
|
|
+// GetImageBitmapSize
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.GetImageBitmapSize(aImageBitmapID: Integer; aSizePtr: TWasmPointer): TCanvasError;
|
|
|
+var
|
|
|
+ imageBitmap : TJSImageBitmap;
|
|
|
+ lView : TJSDataView;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.GetImageBitmapSize(%d,[%x])', [ aImageBitmapID, aSizeFPtr ]);
|
|
|
+ {$ENDIF}
|
|
|
+ imageBitmap := GetImageBitmap(aImageBitmapID);
|
|
|
+ if imageBitmap <> nil then
|
|
|
+ begin
|
|
|
+ lView := MemoryDataView;
|
|
|
+ lView.setInt32(aSizePtr, imageBitmap.width, Env.IsLittleEndian);
|
|
|
+ lView.setInt32(aSizePtr + SizeInt32, imageBitmap.height, Env.IsLittleEndian);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+ end
|
|
|
+ else Result := ECANVAS_NOIMAGEBITMAP;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetFont
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetFont(const aID: TOffscreenCanvasID;
|
|
|
+ aFontName: TWasmPointer; aFontNameLen: integer): TCanvasError;
|
|
|
+var
|
|
|
+ lFontName : String;
|
|
|
+ lPreviousFontName, lNewFontName : String;
|
|
|
+ lCanvas : TOffscreenCanvasReference;
|
|
|
+ lContext : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ lMetrics : TJSTextMetrics;
|
|
|
+begin
|
|
|
+ lFontName := GetUTF16FromMem(aFontName, aFontNameLen);
|
|
|
+
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetFont(%d,"%s")',[aID,lFontName]);
|
|
|
+ {$ENDIF}
|
|
|
+
|
|
|
+ lCanvas := GetOffscreenCanvasRef(aID);
|
|
|
+ if not Assigned(lCanvas) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ lContext := lCanvas.CanvasContext;
|
|
|
+
|
|
|
+ // Note: the browser will normalize the font name
|
|
|
+
|
|
|
+ lPreviousFontName := lContext.font;
|
|
|
+ lContext.font := lFontName;
|
|
|
+ lNewFontName:= lContext.font;
|
|
|
+
|
|
|
+ if lNewFontName <> lPreviousFontName then
|
|
|
+ begin
|
|
|
+ if lCanvas.FontOffsets.has(lNewFontName) then
|
|
|
+ begin
|
|
|
+ lCanvas.FontOffset := TFresnelFloat(lCanvas.FontOffsets.get(lNewFontName));
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ lMetrics := lContext.measureText('x');
|
|
|
+ lCanvas.FontOffset := (lMetrics.fontBoundingBoxAscent - lMetrics.hangingBaseline) * 0.5;
|
|
|
+ lCanvas.FontOffsets.&set(lNewFontName, lCanvas.FontOffset);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ lContext.TextBaseLine := 'top';
|
|
|
+
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// MeasureText
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.MeasureText(const aID: TOffscreenCanvasID;
|
|
|
+ aText: TWasmPointer; aTextLen: integer; aMeasureData: TWasmPointer
|
|
|
+ ): TCanvasError;
|
|
|
+var
|
|
|
+ S : String;
|
|
|
+ lOffscreenRef : TOffscreenCanvasReference;
|
|
|
+ lMetrics : TJSTextMetrics;
|
|
|
+ lView : TJSDataView;
|
|
|
+ H,Asc,Desc : Double;
|
|
|
+ scaleFactor : Double;
|
|
|
+begin
|
|
|
+ S := GetUTF16FromMem(aText, aTextLen);
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.MeasureText(%d,"%s")',[aID,S]);
|
|
|
+{$ENDIF}
|
|
|
+ lOffscreenRef := GetOffscreenCanvasRef(aID);
|
|
|
+ if lOffscreenRef = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ lMetrics := lOffscreenRef.CanvasContext.measureText(S);
|
|
|
+ Asc := lMetrics.fontBoundingBoxAscent;
|
|
|
+ Desc := lMetrics.fontBoundingBoxDescent;
|
|
|
+ H := Asc + Desc;
|
|
|
+
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.MeasureText(%d,"%s") : [W: %g, H: %g, Asc: %g, Desc: %g]',[aID,S,W,H,Asc,Desc]);
|
|
|
+ {$ENDIF}
|
|
|
+
|
|
|
+ scaleFactor := 1;// / lOffscreenRef.Scale;
|
|
|
+
|
|
|
+ lView := MemoryDataView;
|
|
|
+ lView.setFloat32(
|
|
|
+ aMeasureData + WASMMEASURE_WIDTH*SizeFloat32,
|
|
|
+ lMetrics.width * scaleFactor, env.IsLittleEndian
|
|
|
+ );
|
|
|
+ lView.setFloat32(
|
|
|
+ aMeasureData + WASMMEASURE_HEIGHT*SizeFloat32,
|
|
|
+ H*scaleFactor, env.IsLittleEndian
|
|
|
+ );
|
|
|
+ lView.setFloat32(
|
|
|
+ aMeasureData + WASMMEASURE_ASCENDER*SizeFloat32,
|
|
|
+ Asc*scaleFactor, env.IsLittleEndian
|
|
|
+ );
|
|
|
+ lView.setFloat32(
|
|
|
+ aMeasureData + WASMMEASURE_DESCENDER*SizeFloat32,
|
|
|
+ Desc*scaleFactor, env.IsLittleEndian
|
|
|
+ );
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.SetTextShadowParams(const aID: TOffscreenCanvasID;
|
|
|
+ aOffsetX, aOffsetY, aRadius: TFresnelFloat; aRed, aGreen, aBlue,
|
|
|
+ aAlpha: TCanvasColorComponent): TCanvasError;
|
|
|
+
|
|
|
+var
|
|
|
+ Canv : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetTextShadowParams(%d,(%g,%g),%g,"%s")',[aID,aOffsetX,aOffsetY,aRadius,TFresnelHelper.FresnelColorToHTMLColor(aRed,aGreen,aBlue,aAlpha)]);
|
|
|
+ {$ENDIF}
|
|
|
+ Canv:=GetOffscreenContext2D(aID);
|
|
|
+ if Not Assigned(Canv) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Canv.shadowOffsetX:=aOffsetX;
|
|
|
+ Canv.shadowOffsetY:=aOffsetY;
|
|
|
+ Canv.shadowBlur:=aRadius;
|
|
|
+ Canv.shadowColor:=TFresnelHelper.FresnelColorToHTMLColor(aRed,aGreen,aBlue,aAlpha);
|
|
|
+ Result:=ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// CreatePath2D
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.CreatePath2D(aFlags : LongInt; aPathCount : LongInt; aPath : PFresnelFloat) : TJSPath2D;
|
|
|
+var
|
|
|
+ LView : TJSDataView;
|
|
|
+ LPtr, LPtrTail : TWasmPointer;
|
|
|
+ aType : TFresnelFloat;
|
|
|
+ X, Y, X1, Y1, X2, Y2 : TFresnelFloat;
|
|
|
+
|
|
|
+ procedure GetTriple;
|
|
|
+ begin
|
|
|
+ aType := LView.getFloat32(LPtr, env.IsLittleEndian);
|
|
|
+ X := LView.getFloat32(LPtr + SizeFloat32, env.IsLittleEndian);
|
|
|
+ Y := LView.getFloat32(LPtr + 2*SizeFloat32, env.IsLittleEndian);
|
|
|
+ Inc(LPtr, 3*SizeFloat32);
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result := TJSPath2D.New;
|
|
|
+
|
|
|
+ LView := MemoryDataView;
|
|
|
+ LPtr := aPath;
|
|
|
+ LPtrTail := LPtr + aPathCount * (3 * SizeFloat32);
|
|
|
+ aType := 0;
|
|
|
+
|
|
|
+ while LPtr <= LPtrTail do
|
|
|
+ begin
|
|
|
+ GetTriple;
|
|
|
+ if aType = DRAWPATH_TYPEMOVETO then
|
|
|
+ Result.MoveTo(X,Y)
|
|
|
+ else if aType = DRAWPATH_TYPELINETO then
|
|
|
+ Result.LineTo(X,Y)
|
|
|
+ else if aType = DRAWPATH_TYPECURVETO then
|
|
|
+ begin
|
|
|
+ X1:=X;
|
|
|
+ Y1:=Y;
|
|
|
+ GetTriple;
|
|
|
+ if aType <> DRAWPATH_TYPECURVETO then
|
|
|
+ begin
|
|
|
+ Console.Error('Invalid path data 2, expected CURVETO (',DRAWPATH_TYPECURVETO,'), got: ',aType);
|
|
|
+ exit;
|
|
|
+ end;
|
|
|
+ X2:=X;
|
|
|
+ Y2:=Y;
|
|
|
+ GetTriple;
|
|
|
+ if aType <> DRAWPATH_TYPECURVETO then
|
|
|
+ begin
|
|
|
+ Console.Error('Invalid path data 3, expected CURVETO (',DRAWPATH_TYPECURVETO,'), got: ',aType);
|
|
|
+ exit;
|
|
|
+ end;
|
|
|
+ Result.bezierCurveTo(X1, Y1, X2, Y2, X, Y);
|
|
|
+ end
|
|
|
+ else if aType = DRAWPATH_TYPECLOSE then
|
|
|
+ begin
|
|
|
+ Result.ClosePath;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ if (aType <> DRAWPATH_TYPECLOSE) and ((aFlags and DRAWPATH_CLOSEPATH) <> 0) then
|
|
|
+ Result.closePath;
|
|
|
+end;
|
|
|
+
|
|
|
+// DrawPath
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.DrawPath(const aID: TOffscreenCanvasID;
|
|
|
+ aFlags: LongInt; aPathCount: LongInt; aPath: PFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ LCanvas : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ LPath2D : TJSPath2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.DrawPath(%d,%d,%d,[%x])',[aID,aFlags,aPathCount,aPath]);
|
|
|
+ {$ENDIF}
|
|
|
+ LCanvas := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(LCanvas) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ if aPathCount = 0 then
|
|
|
+ Exit(ECANVAS_INVALIDPATH);
|
|
|
+
|
|
|
+ LPath2D := CreatePath2D(aFlags, aPathCount, aPath);
|
|
|
+
|
|
|
+ if (aFlags and DRAWPATH_FILLPATH) <> 0 then
|
|
|
+ LCanvas.Fill(LPath2D);
|
|
|
+ if (aFlags and DRAWPATH_STROKEPATH) <> 0 then
|
|
|
+ LCanvas.Stroke(LPath2D);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// PointInPath
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.PointInPath(const aID: TOffscreenCanvasID; aX,
|
|
|
+ aY: TFresnelFloat; aPathCount: Integer; aPath: PFresnelFloat;
|
|
|
+ aRes: TWasmPointer): TCanvasError;
|
|
|
+var
|
|
|
+ LCanvas : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ LPath2D : TJSPath2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.PointInPath(%d,%d,%d,[%x])', [ aID, aFlags, aPathCount, aPath ]);
|
|
|
+ {$ENDIF}
|
|
|
+ LCanvas := GetOffscreenContext2D(aID);
|
|
|
+ if not Assigned(LCanvas) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ if aPathCount = 0 then
|
|
|
+ Exit(ECANVAS_INVALIDPATH);
|
|
|
+
|
|
|
+ LPath2D := CreatePath2D(DRAWPATH_TYPECLOSE, aPathCount, aPath);
|
|
|
+
|
|
|
+ MemoryDataView.setInt8(aRes, Ord(LCanvas.isPointInPath(LPath2D, aX, aY)));
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// SetTransform
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SetTransform(
|
|
|
+ const aID: TOffscreenCanvasID;
|
|
|
+ m11, m12, m21, m22, m31, m32: TFresnelFloat
|
|
|
+ ): TCanvasError;
|
|
|
+var
|
|
|
+ lOffscreenRef : TOffscreenCanvasReference;
|
|
|
+ s : Double;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SetTransform(%d,%d,[%g,%g,%g,%g,%g,%g])',[aID,Flags,m11,m12,m21,m22,m31,m32]);
|
|
|
+ {$ENDIF}
|
|
|
+ lOffscreenRef := GetOffscreenCanvasRef(aID);
|
|
|
+ if lOffscreenRef = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ s := lOffscreenRef.Scale;
|
|
|
+ lOffscreenRef.CanvasContext.setTransform(
|
|
|
+ s * m11, s * m12,
|
|
|
+ s * m21, s * m22,
|
|
|
+ s * m31, s * m32
|
|
|
+ );
|
|
|
+
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// SaveState
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.SaveState(const aID: TOffscreenCanvasID): TCanvasError;
|
|
|
+var
|
|
|
+ Ref : TOffscreenCanvasReference;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.SaveState(%d)',[aID]);
|
|
|
+ {$ENDIF}
|
|
|
+ Ref := GetOffscreenCanvasRef(aID);
|
|
|
+ if not Assigned(Ref) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Ref.CanvasContext.Save;
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// RestoreState
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.RestoreState(const aID: TOffscreenCanvasID): TCanvasError;
|
|
|
+var
|
|
|
+ Ref : TOffscreenCanvasReference;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.RestoreState(%d)',[aID]);
|
|
|
+ {$ENDIF}
|
|
|
+ Ref := GetOffscreenCanvasRef(aID);
|
|
|
+ if not Assigned(Ref) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Ref.CanvasContext.Restore;
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// RestoreAndSaveState
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.RestoreAndSaveState(const aID : TOffscreenCanvasID) : TCanvasError;
|
|
|
+var
|
|
|
+ Ref : TOffscreenCanvasReference;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.RestoreState(%d)',[aID]);
|
|
|
+ {$ENDIF}
|
|
|
+ Ref := GetOffscreenCanvasRef(aID);
|
|
|
+ if not Assigned(Ref) then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+ Ref.CanvasContext.Restore;
|
|
|
+ Ref.CanvasContext.Save;
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// ClipAddRect
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.ClipAddRect(const aID : TOffscreenCanvasID; aX,aY,aWidth,aHeight: TFresnelFloat): TCanvasError;
|
|
|
+var
|
|
|
+ lCanvasRef : TOffscreenCanvasReference;
|
|
|
+ lPath2D : TJSPath2D;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.ClipAddRect(%d,(%g,%g)-(%gx%g))',[aID,aX,aY,aWidth,aHeight]);
|
|
|
+ {$ENDIF}
|
|
|
+ lCanvasRef := GetOffscreenCanvasRef(aID);
|
|
|
+ if lCanvasRef = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ if daClipRect in DebugAPIs then
|
|
|
+ DrawClipRect(lCanvasRef, aX, aY, aWidth, aHeight);
|
|
|
+
|
|
|
+ lPath2D := TJSPath2D.New;
|
|
|
+ lPath2D.rect(aX, aY, aWidth, aHeight);
|
|
|
+ lCanvasRef.CanvasContext.clip(lPath2D);
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// ClipAddRectAndExcludes
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.ClipAddPolygon(
|
|
|
+ const aID: TOffscreenCanvasID; APolygonData: TWasmPointer;
|
|
|
+ APolygonCount: Integer; AClipQuadData: TWasmPointer; ANbClipQuads: Integer
|
|
|
+ ): TCanvasError;
|
|
|
+var
|
|
|
+ lCanvasRef : TOffscreenCanvasReference;
|
|
|
+ lContext : TJSOffscreenCanvasRenderingContext2D;
|
|
|
+ lPath2D : TJSPath2D;
|
|
|
+ i : Integer;
|
|
|
+ p : LongInt;
|
|
|
+ lDataView : TJSDataView;
|
|
|
+ lOldStrokeStyle : JSValue;
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('Canvas.ClipAddRect(%d,(%g,%g)-(%gx%g))',[aID,aX,aY,aWidth,aHeight]);
|
|
|
+ {$ENDIF}
|
|
|
+ lCanvasRef := GetOffscreenCanvasRef(aID);
|
|
|
+ if lCanvasRef = nil then
|
|
|
+ Exit(ECANVAS_NOCANVAS);
|
|
|
+
|
|
|
+ lContext := lCanvasRef.CanvasContext;
|
|
|
+
|
|
|
+ lPath2D := TJSPath2D.New;
|
|
|
+
|
|
|
+ lDataView := MemoryDataView;
|
|
|
+
|
|
|
+ if APolygonCount > 0 then
|
|
|
+ begin
|
|
|
+ p := APolygonData;
|
|
|
+ lPath2D.moveTo(lDataView.getFloat32(p, Env.IsLittleEndian), lDataView.getFloat32(p+SizeFloat32, Env.IsLittleEndian));
|
|
|
+ Inc(p, 2*SizeFloat32);
|
|
|
+ for i := 1 to APolygonCount-1 do
|
|
|
+ begin
|
|
|
+ lPath2D.lineTo(lDataView.getFloat32(p, Env.IsLittleEndian), lDataView.getFloat32(p+SizeFloat32, Env.IsLittleEndian));
|
|
|
+ Inc(p, 2*SizeFloat32);
|
|
|
+ end;
|
|
|
+ lPath2D.closePath();
|
|
|
+ end;
|
|
|
+
|
|
|
+ p := aClipQuadData;
|
|
|
+ for i := 0 to aNbClipQuads-1 do
|
|
|
+ begin
|
|
|
+ lPath2D.moveTo(lDataView.getFloat32(p, Env.IsLittleEndian), lDataView.getFloat32(p+SizeFloat32, Env.IsLittleEndian));
|
|
|
+ Inc(p, 2*SizeFloat32);
|
|
|
+ lPath2D.lineTo(lDataView.getFloat32(p, Env.IsLittleEndian), lDataView.getFloat32(p+SizeFloat32, Env.IsLittleEndian));
|
|
|
+ Inc(p, 2*SizeFloat32);
|
|
|
+ lPath2D.lineTo(lDataView.getFloat32(p, Env.IsLittleEndian), lDataView.getFloat32(p+SizeFloat32, Env.IsLittleEndian));
|
|
|
+ Inc(p, 2*SizeFloat32);
|
|
|
+ lPath2D.lineTo(lDataView.getFloat32(p, Env.IsLittleEndian), lDataView.getFloat32(p+SizeFloat32, Env.IsLittleEndian));
|
|
|
+ Inc(p, 2*SizeFloat32);
|
|
|
+ lPath2D.closePath();
|
|
|
+ end;
|
|
|
+
|
|
|
+ if daClipRect in DebugAPIs then
|
|
|
+ begin
|
|
|
+ lOldStrokeStyle := lContext.strokeStyle;
|
|
|
+ lContext.strokeStyle := 'red';
|
|
|
+ lContext.stroke(lPath2D);
|
|
|
+ lContext.strokeStyle := lOldStrokeStyle;
|
|
|
+ end;
|
|
|
+
|
|
|
+ lContext.clip(lPath2D);
|
|
|
+
|
|
|
+ Result := ECANVAS_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmFresnelSharedApi.AllocateTimer(ainterval: LongInt; userdata: TWasmPointer): TTimerID;
|
|
|
+var
|
|
|
+ aTimerID : TTimerID;
|
|
|
+ CallBack : JSValue;
|
|
|
+
|
|
|
+ procedure HandleTimer;
|
|
|
+ var
|
|
|
+ Continue : Boolean;
|
|
|
+ begin
|
|
|
+ // The instance/timer could have disappeared
|
|
|
+ Callback:=InstanceExports['__fresnel_timer_tick'];
|
|
|
+ //Writeln(Format('FresnelAPi.TimerTick(%d)',[aTimerID]));
|
|
|
+ Continue:=Assigned(Callback);
|
|
|
+ if Continue then
|
|
|
+ Continue:=TTimerTickCallback(CallBack)(aTimerID,userData)
|
|
|
+ else
|
|
|
+ Console.Error('No more tick callback !');
|
|
|
+ if not Continue then
|
|
|
+ begin
|
|
|
+ //Writeln(Format('FresnelAPi.TimerTick(%d), return value false, deactivate',[aTimerID]));
|
|
|
+ DeAllocateTimer(aTimerID);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('FresnelApi.AllocateTimer(%d,[%x])',[aInterval,UserData]);
|
|
|
+ {$ENDIF}
|
|
|
+ Callback:=InstanceExports['__fresnel_timer_tick'];
|
|
|
+ if not Assigned(Callback) then
|
|
|
+ Exit(0);
|
|
|
+ aTimerID := Self_.setInterval(@HandleTimer,aInterval);
|
|
|
+ Result := aTimerID;
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('FresnelApi.AllocateTimer(%d,[%x] => %d)',[aInterval,UserData]);
|
|
|
+ {$ENDIF}
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TWasmFresnelSharedApi.DeallocateTimer(timerid: TTimerID);
|
|
|
+begin
|
|
|
+ {$IFNDEF NOLOGAPICALLS}
|
|
|
+ if LogAPICalls then
|
|
|
+ LogCall('FresnelApi.DeAllocateTimer(%d)',[TimerID]);
|
|
|
+ {$ENDIF}
|
|
|
+ Self_.clearInterval(TimerID);
|
|
|
+end;
|
|
|
+
|
|
|
+// GetEvent
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.GetEvent(aID: TWasmPointer; aMsg: TWasmPointer; Data: TWasmPointer): TCanvasError;
|
|
|
+var
|
|
|
+ view : TJSDataView;
|
|
|
+ evt : TWindowEvent;
|
|
|
+begin
|
|
|
+ evt := DequeueEvent;
|
|
|
+ if evt = nil then
|
|
|
+ Exit(EWASMEVENT_NOEVENT);
|
|
|
+
|
|
|
+ view := MemoryDataView;
|
|
|
+ view.setInt32(aID, evt.WindowID, env.IsLittleEndian);
|
|
|
+ view.setInt32(aMsg, evt.Msg, env.IsLittleEndian);
|
|
|
+
|
|
|
+ view.setInt32(Data, evt.param0, env.IsLittleEndian);
|
|
|
+ view.setInt32(Data + SizeInt32, evt.param1, env.IsLittleEndian);
|
|
|
+ view.setInt32(Data + 2*SizeInt32, evt.param2, env.IsLittleEndian);
|
|
|
+ view.setInt32(Data + 3*SizeInt32, evt.param3, env.IsLittleEndian);
|
|
|
+
|
|
|
+ RecycleEvent(evt);
|
|
|
+
|
|
|
+ Result := EWASMEVENT_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// GetEventCount
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.GetEventCount(aCount: TWasmPointer): TCanvasError;
|
|
|
+var
|
|
|
+ view : TJSDataView;
|
|
|
+begin
|
|
|
+ view := MemoryDataView;
|
|
|
+ view.setint32(aCount, EventCount, env.IsLittleEndian);
|
|
|
+ Result := EWASMEVENT_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+// ConsoleLog
|
|
|
+//
|
|
|
+procedure TWasmFresnelSharedApi.ConsoleLog(aText: TWasmPointer; aTextLen: integer);
|
|
|
+begin
|
|
|
+ WebAssemblyMemory := Env.Memory.buffer;
|
|
|
+ Console.log(GetUTF16FromMem(aText, aTextLen));
|
|
|
+ WebAssemblyMemory := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+// GetEnumeratedUserMedia
|
|
|
+//
|
|
|
+function TWasmFresnelSharedApi.GetEnumeratedUserMedia(aUTF16SizePtr, aDataUTF16: TWasmPointer): TCanvasError;
|
|
|
+var
|
|
|
+ view : TJSDataView;
|
|
|
+ allocatedSize : Integer;
|
|
|
+ requiredSize : Integer;
|
|
|
+begin
|
|
|
+ requiredSize := Length(FEnumeratedUserMedia);
|
|
|
+
|
|
|
+ view := MemoryDataView;
|
|
|
+ allocatedSize := view.getInt32(aUTF16SizePtr, env.IsLittleEndian);
|
|
|
+
|
|
|
+ view.setInt32(aUTF16SizePtr, requiredSize, env.IsLittleEndian);
|
|
|
+
|
|
|
+ if allocatedSize < requiredSize then
|
|
|
+ Exit(EWASMEVENT_BUFFER_SIZE);
|
|
|
+
|
|
|
+ SetUTF16ToMem(aDataUTF16, FEnumeratedUserMedia);
|
|
|
+ Result := EWASMEVENT_SUCCESS;
|
|
|
+end;
|
|
|
+
|
|
|
+end.
|
|
|
+
|