From d10e008328f10cdc25bf2a0a459c31914f318fe0 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Thu, 19 Dec 2024 07:52:44 -0700 Subject: [PATCH 01/23] init commit --- core/frontend/src/Viewport.ts | 2 ++ core/frontend/src/render/webgl/Target.ts | 16 ++++++++++++++-- .../src/frontend/SaveImageTool.ts | 4 +++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index 066f9c15d583..fc0cb1278a7b 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -3048,6 +3048,7 @@ export class ScreenViewport extends Viewport { private readonly _backStack: ViewPose[] = []; private _currentBaseline?: ViewPose; private _lastPose?: ViewPose; // the pose the last time this view was rendered + // probably the webgl canvas private _webglCanvas?: HTMLCanvasElement; private _logo!: HTMLImageElement; private readonly _decorationCache = new DecorationsCache(); @@ -3057,6 +3058,7 @@ export class ScreenViewport extends Viewport { /** The div created to hold all viewport elements. */ public readonly vpDiv: HTMLDivElement; /** The canvas to display the view contents. */ + // probable the 2d canvas on top public readonly canvas: HTMLCanvasElement; /** The HTMLDivElement used for HTML decorations. May be referenced from the DOM by class "overlay-decorators". * @internal diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index d22e9b623a7b..971ad0cfb349 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -64,6 +64,7 @@ import { FrameStatsCollector } from "../FrameStats"; import { ActiveSpatialClassifier } from "../../SpatialClassifiersState"; import { AnimationNodeId } from "../../common/internal/render/AnimationNodeId"; import { _implementationProhibited } from "../../common/internal/Symbols"; +import { IModelApp } from "../../IModelApp"; function swapImageByte(image: ImageBuffer, i0: number, i1: number) { const tmp = image.data[i0]; @@ -1177,12 +1178,23 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo return image; } - public copyImageToCanvas(): HTMLCanvasElement { + public copyImageToCanvas(combineCanvas?: boolean): HTMLCanvasElement { const image = this.readImageBuffer(); const canvas = undefined !== image ? imageBufferToCanvas(image, false) : undefined; const retCanvas = undefined !== canvas ? canvas : document.createElement("canvas"); const pixelRatio = this.devicePixelRatio; retCanvas.getContext("2d")!.scale(pixelRatio, pixelRatio); + + if (combineCanvas) { + const vp = IModelApp.viewManager.selectedView; + if (vp) { + const twoDCanvas = vp.canvas; + const ctx = retCanvas.getContext("2d")!; + ctx.drawImage(twoDCanvas, 0, 0); + } + } + + return retCanvas; } @@ -1513,7 +1525,7 @@ export class OnScreenTarget extends Target { } public override readImageToCanvas(): HTMLCanvasElement { - return this._usingWebGLCanvas ? this.copyImageToCanvas() : this._2dCanvas.canvas; + return this._usingWebGLCanvas ? this.copyImageToCanvas(true) : this._2dCanvas.canvas; } } diff --git a/test-apps/display-test-app/src/frontend/SaveImageTool.ts b/test-apps/display-test-app/src/frontend/SaveImageTool.ts index e2546b326957..2c01bcf2ec76 100644 --- a/test-apps/display-test-app/src/frontend/SaveImageTool.ts +++ b/test-apps/display-test-app/src/frontend/SaveImageTool.ts @@ -40,7 +40,9 @@ export class SaveImageTool extends Tool { return true; } - const url = imageBufferToPngDataUrl(buffer, false); + // const url = imageBufferToPngDataUrl(buffer, false); + const canvas = vp.readImageToCanvas(); +const url = canvas.toDataURL(); if (!url) { alert("Failed to produce PNG"); return true; From cb438a7252bd2e3cc399bacc6000fb3e06eb8571 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Fri, 10 Jan 2025 09:42:22 -0700 Subject: [PATCH 02/23] create canvasDecorations view flag, fix copy image bug --- common/api/core-common.api.md | 3 ++ common/api/frontend-devtools.api.md | 14 ++++++++ .../api/summary/frontend-devtools.exports.csv | 1 + .../andremig-decoration_2025-01-10-16-41.json | 10 ++++++ core/common/src/ViewFlags.ts | 12 ++++++- core/common/src/test/ViewFlags.test.ts | 2 ++ .../public/locales/en/FrontendDevTools.json | 3 ++ .../frontend-devtools/src/FrontEndDevTools.ts | 3 +- .../src/tools/ViewportTools.ts | 22 +++++++++++++ core/frontend/src/Viewport.ts | 2 -- core/frontend/src/render/webgl/Target.ts | 32 ++++++++++--------- 11 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json diff --git a/common/api/core-common.api.md b/common/api/core-common.api.md index 379ad6300c36..d8ef51ff107f 100644 --- a/common/api/core-common.api.md +++ b/common/api/core-common.api.md @@ -10961,6 +10961,7 @@ export interface ViewFlagProps { hidEdges?: boolean; monochrome?: boolean; noCameraLights?: boolean; + noCanvasDecorations?: boolean; noConstruct?: boolean; noDim?: boolean; noFill?: boolean; @@ -10986,6 +10987,8 @@ export class ViewFlags { readonly acsTriad: boolean; readonly ambientOcclusion: boolean; readonly backgroundMap: boolean; + // (undocumented) + readonly canvasDecorations: boolean; readonly clipVolume: boolean; readonly constructions: boolean; copy(changedFlags: Partial): ViewFlags; diff --git a/common/api/frontend-devtools.api.md b/common/api/frontend-devtools.api.md index 06195128bc8b..b1b7340164e4 100644 --- a/common/api/frontend-devtools.api.md +++ b/common/api/frontend-devtools.api.md @@ -2238,6 +2238,20 @@ export class Toggle3dManipulationsTool extends ViewportToggleTool { static toolId: string; } +// @beta +export class ToggleCanvasDecorationsTool extends Tool { + // (undocumented) + static get maxArgs(): number; + // (undocumented) + static get minArgs(): number; + // (undocumented) + parseAndRun(...inArgs: string[]): Promise; + // (undocumented) + run(showDecorations: boolean): Promise; + // (undocumented) + static toolId: string; +} + // @beta export class ToggleDPIForLODTool extends RenderSystemDebugControlTool { // (undocumented) diff --git a/common/api/summary/frontend-devtools.exports.csv b/common/api/summary/frontend-devtools.exports.csv index 201666b409d9..3ca508c05ca6 100644 --- a/common/api/summary/frontend-devtools.exports.csv +++ b/common/api/summary/frontend-devtools.exports.csv @@ -196,6 +196,7 @@ alpha;interface;TextBoxProps beta;class;TileMemoryBreakdown beta;class;TileStatisticsTracker beta;class;Toggle3dManipulationsTool +beta;class;ToggleCanvasDecorationsTool beta;class;ToggleDPIForLODTool beta;class;ToggleDrapeFrustumTool beta;class;ToggleDrawingGraphicsTool diff --git a/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json b/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json new file mode 100644 index 000000000000..c857188179d5 --- /dev/null +++ b/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-frontend", + "comment": "add canvasDecorations view flag", + "type": "none" + } + ], + "packageName": "@itwin/core-frontend" +} \ No newline at end of file diff --git a/core/common/src/ViewFlags.ts b/core/common/src/ViewFlags.ts index 1dcdb9ebb30b..e0878f124ae8 100644 --- a/core/common/src/ViewFlags.ts +++ b/core/common/src/ViewFlags.ts @@ -112,6 +112,8 @@ export interface ViewFlagProps { * @see [[ViewFlags.whiteOnWhiteReversal]]. */ noWhiteOnWhiteReversal?: boolean; + /**If true, canvas decorations will not be drawn */ + noCanvasDecorations?: boolean; } function edgesRequired(renderMode: RenderMode, visibleEdges: boolean): boolean { @@ -226,6 +228,8 @@ export class ViewFlags { */ public readonly lighting: boolean; + public readonly canvasDecorations: boolean; + /** Create a new ViewFlags. * @param flags The properties to initialize. Any properties not specified are initialized to their default values. */ @@ -254,6 +258,7 @@ export class ViewFlags { this.forceSurfaceDiscard = flags?.forceSurfaceDiscard ?? false; this.whiteOnWhiteReversal = flags?.whiteOnWhiteReversal ?? true; this.lighting = flags?.lighting ?? false; + this.canvasDecorations = flags?.canvasDecorations ?? true; } /** Produce a copy of these ViewFlags with some modified properties. Any properties not explicitly specified by `changedFlags` will retain their current values. @@ -415,6 +420,8 @@ export class ViewFlags { out.forceSurfaceDiscard = true; if (!this.whiteOnWhiteReversal) out.noWhiteOnWhiteReversal = true; + if (!this.canvasDecorations) + out.noCanvasDecorations = true; out.renderMode = this.renderMode; return out; @@ -449,6 +456,7 @@ export class ViewFlags { wiremesh: this.wiremesh, forceSurfaceDiscard: this.forceSurfaceDiscard, noWhiteOnWhiteReversal: !this.whiteOnWhiteReversal, + noCanvasDecorations: !this.canvasDecorations, }; } @@ -509,6 +517,7 @@ export class ViewFlags { wiremesh: JsonUtils.asBool(json.wiremesh), forceSurfaceDiscard: JsonUtils.asBool(json.forceSurfaceDiscard), whiteOnWhiteReversal: !JsonUtils.asBool(json.noWhiteOnWhiteReversal), + canvasDecorations: !JsonUtils.asBool(json.noCanvasDecorations), }); } @@ -540,7 +549,8 @@ export class ViewFlags { && this.thematicDisplay === other.thematicDisplay && this.wiremesh === other.wiremesh && this.forceSurfaceDiscard === other.forceSurfaceDiscard - && this.whiteOnWhiteReversal === other.whiteOnWhiteReversal; + && this.whiteOnWhiteReversal === other.whiteOnWhiteReversal + && this.canvasDecorations === other.canvasDecorations; } } diff --git a/core/common/src/test/ViewFlags.test.ts b/core/common/src/test/ViewFlags.test.ts index 56ff2152024b..7318c3efe363 100644 --- a/core/common/src/test/ViewFlags.test.ts +++ b/core/common/src/test/ViewFlags.test.ts @@ -73,6 +73,7 @@ describe("ViewFlags", () => { wiremesh: on, forceSurfaceDiscard: on, noWhiteOnWhiteReversal: on, + noCanvasDecorations: on, renderMode: RenderMode.SolidFill, }; @@ -115,6 +116,7 @@ describe("ViewFlags", () => { backgroundMap: true, forceSurfaceDiscard: true, noWhiteOnWhiteReversal: true, + noCanvasDecorations: true, }); }); diff --git a/core/frontend-devtools/public/locales/en/FrontendDevTools.json b/core/frontend-devtools/public/locales/en/FrontendDevTools.json index e297165a40c1..9847d5048f28 100644 --- a/core/frontend-devtools/public/locales/en/FrontendDevTools.json +++ b/core/frontend-devtools/public/locales/en/FrontendDevTools.json @@ -331,6 +331,9 @@ "ChangeCamera": { "keyin": "fdt camera" }, + "ToggleCanvasDecorations": { + "keyin": "fdt canvas decorations" + }, "ToggleDrawingGraphics": { "keyin": "fdt drawing graphics" }, diff --git a/core/frontend-devtools/src/FrontEndDevTools.ts b/core/frontend-devtools/src/FrontEndDevTools.ts index de1fa8390764..672c41a220d7 100644 --- a/core/frontend-devtools/src/FrontEndDevTools.ts +++ b/core/frontend-devtools/src/FrontEndDevTools.ts @@ -21,7 +21,7 @@ import { } from "./tools/PlanarMaskTools"; import { ChangeCameraTool, ChangeEmphasisSettingsTool, ChangeFlashSettingsTool, ChangeHiliteModeTool, ChangeHiliteSettingsTool, DefaultTileSizeModifierTool, FadeOutTool, - FreezeSceneTool, SetAspectRatioSkewTool, ShowTileVolumesTool, Toggle3dManipulationsTool, ToggleDrawingGraphicsTool, ToggleSectionDrawingSpatialViewTool, + FreezeSceneTool, SetAspectRatioSkewTool, ShowTileVolumesTool, Toggle3dManipulationsTool, ToggleCanvasDecorationsTool, ToggleDrawingGraphicsTool, ToggleSectionDrawingSpatialViewTool, ToggleTileTreeReferencesTool, ToggleViewAttachmentBoundariesTool, ToggleViewAttachmentClipShapesTool, ToggleViewAttachmentsTool, ViewportAddRealityModel, ViewportTileSizeModifierTool, } from "./tools/ViewportTools"; @@ -167,6 +167,7 @@ export class FrontendDevTools { SetAASamplesTool, SetAspectRatioSkewTool, SetScheduleScriptTool, + ToggleCanvasDecorationsTool, ToggleVolClassIntersect, SetMapBaseTool, SharpenEffect, diff --git a/core/frontend-devtools/src/tools/ViewportTools.ts b/core/frontend-devtools/src/tools/ViewportTools.ts index 611b821aba3d..219d3c52b9f8 100644 --- a/core/frontend-devtools/src/tools/ViewportTools.ts +++ b/core/frontend-devtools/src/tools/ViewportTools.ts @@ -607,3 +607,25 @@ export class ChangeCameraTool extends Tool { return this.run(camera); } } + +/** Change the camera settings of the selected viewport. + * @beta + */ +export class ToggleCanvasDecorationsTool extends Tool { + public static override get minArgs() { return 1; } + public static override get maxArgs() { return 1; } + public static override toolId = "ToggleCanvasDecorations"; + + public override async run(showDecorations: boolean): Promise { + const vp = IModelApp.viewManager.selectedView; + if (!vp) + return false; + vp.viewFlags = vp.viewFlags.with("canvasDecorations", showDecorations); + return true; + } + + public override async parseAndRun(...inArgs: string[]): Promise { + const showDecorations = !!parseToggle(inArgs[0]); + return this.run(showDecorations); + } +} diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index fc0cb1278a7b..066f9c15d583 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -3048,7 +3048,6 @@ export class ScreenViewport extends Viewport { private readonly _backStack: ViewPose[] = []; private _currentBaseline?: ViewPose; private _lastPose?: ViewPose; // the pose the last time this view was rendered - // probably the webgl canvas private _webglCanvas?: HTMLCanvasElement; private _logo!: HTMLImageElement; private readonly _decorationCache = new DecorationsCache(); @@ -3058,7 +3057,6 @@ export class ScreenViewport extends Viewport { /** The div created to hold all viewport elements. */ public readonly vpDiv: HTMLDivElement; /** The canvas to display the view contents. */ - // probable the 2d canvas on top public readonly canvas: HTMLCanvasElement; /** The HTMLDivElement used for HTML decorations. May be referenced from the DOM by class "overlay-decorators". * @internal diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index 971ad0cfb349..4bc5898c6223 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -1178,21 +1178,21 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo return image; } - public copyImageToCanvas(combineCanvas?: boolean): HTMLCanvasElement { + public copyImageToCanvas(): HTMLCanvasElement { const image = this.readImageBuffer(); const canvas = undefined !== image ? imageBufferToCanvas(image, false) : undefined; const retCanvas = undefined !== canvas ? canvas : document.createElement("canvas"); const pixelRatio = this.devicePixelRatio; retCanvas.getContext("2d")!.scale(pixelRatio, pixelRatio); - if (combineCanvas) { const vp = IModelApp.viewManager.selectedView; - if (vp) { + // vp.rendersToScreen tells us if the view is being rendered directly to the webgl canvas on screen, which happens in the case of a single viewport. + // In that case, we need to combine the 2d canvas with the webgl canvas or we will lose canvas decorations in the copied image. + if (vp && vp.rendersToScreen) { const twoDCanvas = vp.canvas; const ctx = retCanvas.getContext("2d")!; ctx.drawImage(twoDCanvas, 0, 0); } - } return retCanvas; @@ -1480,16 +1480,18 @@ export class OnScreenTarget extends Target { this._2dCanvas.needsClear = false; } - const canvasDecs = this.graphics.canvasDecorations; - if (canvasDecs) { - for (const overlay of canvasDecs) { - ctx.save(); - if (overlay.position) - ctx.translate(overlay.position.x, overlay.position.y); - - overlay.drawDecoration(ctx); - this._2dCanvas.needsClear = true; - ctx.restore(); + if (this.currentViewFlags.canvasDecorations) { + const canvasDecs = this.graphics.canvasDecorations; + if (canvasDecs) { + for (const overlay of canvasDecs) { + ctx.save(); + if (overlay.position) + ctx.translate(overlay.position.x, overlay.position.y); + + overlay.drawDecoration(ctx); + this._2dCanvas.needsClear = true; + ctx.restore(); + } } } } @@ -1525,7 +1527,7 @@ export class OnScreenTarget extends Target { } public override readImageToCanvas(): HTMLCanvasElement { - return this._usingWebGLCanvas ? this.copyImageToCanvas(true) : this._2dCanvas.canvas; + return this._usingWebGLCanvas ? this.copyImageToCanvas() : this._2dCanvas.canvas; } } From b9d78c38a8be6f54207832d79b04645c51e4bc88 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Fri, 10 Jan 2025 09:46:55 -0700 Subject: [PATCH 03/23] cleanup --- test-apps/display-test-app/src/frontend/SaveImageTool.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test-apps/display-test-app/src/frontend/SaveImageTool.ts b/test-apps/display-test-app/src/frontend/SaveImageTool.ts index 2c01bcf2ec76..e2546b326957 100644 --- a/test-apps/display-test-app/src/frontend/SaveImageTool.ts +++ b/test-apps/display-test-app/src/frontend/SaveImageTool.ts @@ -40,9 +40,7 @@ export class SaveImageTool extends Tool { return true; } - // const url = imageBufferToPngDataUrl(buffer, false); - const canvas = vp.readImageToCanvas(); -const url = canvas.toDataURL(); + const url = imageBufferToPngDataUrl(buffer, false); if (!url) { alert("Failed to produce PNG"); return true; From 8e49ea384d9add2ee8e15ace5b23d48310335203 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Fri, 10 Jan 2025 10:05:47 -0700 Subject: [PATCH 04/23] rush change --- .../andremig-decoration_2025-01-10-17-05.json | 10 ++++++++++ .../andremig-decoration_2025-01-10-17-05.json | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json create mode 100644 common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json diff --git a/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json b/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json new file mode 100644 index 000000000000..158f01afbc03 --- /dev/null +++ b/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-common", + "comment": "add canvasDecorations view flag", + "type": "none" + } + ], + "packageName": "@itwin/core-common" +} \ No newline at end of file diff --git a/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json b/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json new file mode 100644 index 000000000000..034139d3477e --- /dev/null +++ b/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/frontend-devtools", + "comment": "Add ToggleCanvasDecorationsTool", + "type": "none" + } + ], + "packageName": "@itwin/frontend-devtools" +} \ No newline at end of file From 10664122a088f3df6510121f745d7b5e6c1f8e30 Mon Sep 17 00:00:00 2001 From: andremig-bentley <101671244+andremig-bentley@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:08:57 -0700 Subject: [PATCH 05/23] update rush change --- .../core-frontend/andremig-decoration_2025-01-10-16-41.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json b/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json index c857188179d5..00906e73270a 100644 --- a/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json +++ b/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json @@ -2,9 +2,9 @@ "changes": [ { "packageName": "@itwin/core-frontend", - "comment": "add canvasDecorations view flag", + "comment": "", "type": "none" } ], "packageName": "@itwin/core-frontend" -} \ No newline at end of file +} From e38bfa33106b846fa5806c1dc08c217e1f20c4bd Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 21 Jan 2025 08:03:03 -0700 Subject: [PATCH 06/23] rm view flag changes --- common/api/frontend-devtools.api.md | 14 ------------ .../api/summary/frontend-devtools.exports.csv | 1 - .../andremig-decoration_2025-01-10-17-05.json | 2 +- .../andremig-decoration_2025-01-10-17-05.json | 2 +- core/common/src/ViewFlags.ts | 12 +--------- core/common/src/test/ViewFlags.test.ts | 2 -- .../public/locales/en/FrontendDevTools.json | 3 --- .../frontend-devtools/src/FrontEndDevTools.ts | 3 +-- .../src/tools/ViewportTools.ts | 22 ------------------- core/frontend/src/render/webgl/Target.ts | 22 +++++++++---------- 10 files changed, 14 insertions(+), 69 deletions(-) diff --git a/common/api/frontend-devtools.api.md b/common/api/frontend-devtools.api.md index b1b7340164e4..06195128bc8b 100644 --- a/common/api/frontend-devtools.api.md +++ b/common/api/frontend-devtools.api.md @@ -2238,20 +2238,6 @@ export class Toggle3dManipulationsTool extends ViewportToggleTool { static toolId: string; } -// @beta -export class ToggleCanvasDecorationsTool extends Tool { - // (undocumented) - static get maxArgs(): number; - // (undocumented) - static get minArgs(): number; - // (undocumented) - parseAndRun(...inArgs: string[]): Promise; - // (undocumented) - run(showDecorations: boolean): Promise; - // (undocumented) - static toolId: string; -} - // @beta export class ToggleDPIForLODTool extends RenderSystemDebugControlTool { // (undocumented) diff --git a/common/api/summary/frontend-devtools.exports.csv b/common/api/summary/frontend-devtools.exports.csv index 3ca508c05ca6..201666b409d9 100644 --- a/common/api/summary/frontend-devtools.exports.csv +++ b/common/api/summary/frontend-devtools.exports.csv @@ -196,7 +196,6 @@ alpha;interface;TextBoxProps beta;class;TileMemoryBreakdown beta;class;TileStatisticsTracker beta;class;Toggle3dManipulationsTool -beta;class;ToggleCanvasDecorationsTool beta;class;ToggleDPIForLODTool beta;class;ToggleDrapeFrustumTool beta;class;ToggleDrawingGraphicsTool diff --git a/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json b/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json index 158f01afbc03..d1ac065f5d72 100644 --- a/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json +++ b/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@itwin/core-common", - "comment": "add canvasDecorations view flag", + "comment": "", "type": "none" } ], diff --git a/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json b/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json index 034139d3477e..3704c5ffd7e2 100644 --- a/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json +++ b/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@itwin/frontend-devtools", - "comment": "Add ToggleCanvasDecorationsTool", + "comment": "", "type": "none" } ], diff --git a/core/common/src/ViewFlags.ts b/core/common/src/ViewFlags.ts index 19486e980427..0d2397396d03 100644 --- a/core/common/src/ViewFlags.ts +++ b/core/common/src/ViewFlags.ts @@ -112,8 +112,6 @@ export interface ViewFlagProps { * @see [[ViewFlags.whiteOnWhiteReversal]]. */ noWhiteOnWhiteReversal?: boolean; - /**If true, canvas decorations will not be drawn */ - noCanvasDecorations?: boolean; } function edgesRequired(renderMode: RenderMode, visibleEdges: boolean): boolean { @@ -228,8 +226,6 @@ export class ViewFlags { */ public readonly lighting: boolean; - public readonly canvasDecorations: boolean; - /** Create a new ViewFlags. * @param flags The properties to initialize. Any properties not specified are initialized to their default values. */ @@ -258,7 +254,6 @@ export class ViewFlags { this.forceSurfaceDiscard = flags?.forceSurfaceDiscard ?? false; this.whiteOnWhiteReversal = flags?.whiteOnWhiteReversal ?? true; this.lighting = flags?.lighting ?? false; - this.canvasDecorations = flags?.canvasDecorations ?? true; } /** Produce a copy of these ViewFlags with some modified properties. Any properties not explicitly specified by `changedFlags` will retain their current values. @@ -418,8 +413,6 @@ export class ViewFlags { out.forceSurfaceDiscard = true; if (!this.whiteOnWhiteReversal) out.noWhiteOnWhiteReversal = true; - if (!this.canvasDecorations) - out.noCanvasDecorations = true; out.renderMode = this.renderMode; return out; @@ -454,7 +447,6 @@ export class ViewFlags { wiremesh: this.wiremesh, forceSurfaceDiscard: this.forceSurfaceDiscard, noWhiteOnWhiteReversal: !this.whiteOnWhiteReversal, - noCanvasDecorations: !this.canvasDecorations, }; } @@ -515,7 +507,6 @@ export class ViewFlags { wiremesh: JsonUtils.asBool(json.wiremesh), forceSurfaceDiscard: JsonUtils.asBool(json.forceSurfaceDiscard), whiteOnWhiteReversal: !JsonUtils.asBool(json.noWhiteOnWhiteReversal), - canvasDecorations: !JsonUtils.asBool(json.noCanvasDecorations), }); } @@ -547,8 +538,7 @@ export class ViewFlags { && this.thematicDisplay === other.thematicDisplay && this.wiremesh === other.wiremesh && this.forceSurfaceDiscard === other.forceSurfaceDiscard - && this.whiteOnWhiteReversal === other.whiteOnWhiteReversal - && this.canvasDecorations === other.canvasDecorations; + && this.whiteOnWhiteReversal === other.whiteOnWhiteReversal; } } diff --git a/core/common/src/test/ViewFlags.test.ts b/core/common/src/test/ViewFlags.test.ts index 7318c3efe363..56ff2152024b 100644 --- a/core/common/src/test/ViewFlags.test.ts +++ b/core/common/src/test/ViewFlags.test.ts @@ -73,7 +73,6 @@ describe("ViewFlags", () => { wiremesh: on, forceSurfaceDiscard: on, noWhiteOnWhiteReversal: on, - noCanvasDecorations: on, renderMode: RenderMode.SolidFill, }; @@ -116,7 +115,6 @@ describe("ViewFlags", () => { backgroundMap: true, forceSurfaceDiscard: true, noWhiteOnWhiteReversal: true, - noCanvasDecorations: true, }); }); diff --git a/core/frontend-devtools/public/locales/en/FrontendDevTools.json b/core/frontend-devtools/public/locales/en/FrontendDevTools.json index 9847d5048f28..e297165a40c1 100644 --- a/core/frontend-devtools/public/locales/en/FrontendDevTools.json +++ b/core/frontend-devtools/public/locales/en/FrontendDevTools.json @@ -331,9 +331,6 @@ "ChangeCamera": { "keyin": "fdt camera" }, - "ToggleCanvasDecorations": { - "keyin": "fdt canvas decorations" - }, "ToggleDrawingGraphics": { "keyin": "fdt drawing graphics" }, diff --git a/core/frontend-devtools/src/FrontEndDevTools.ts b/core/frontend-devtools/src/FrontEndDevTools.ts index 672c41a220d7..de1fa8390764 100644 --- a/core/frontend-devtools/src/FrontEndDevTools.ts +++ b/core/frontend-devtools/src/FrontEndDevTools.ts @@ -21,7 +21,7 @@ import { } from "./tools/PlanarMaskTools"; import { ChangeCameraTool, ChangeEmphasisSettingsTool, ChangeFlashSettingsTool, ChangeHiliteModeTool, ChangeHiliteSettingsTool, DefaultTileSizeModifierTool, FadeOutTool, - FreezeSceneTool, SetAspectRatioSkewTool, ShowTileVolumesTool, Toggle3dManipulationsTool, ToggleCanvasDecorationsTool, ToggleDrawingGraphicsTool, ToggleSectionDrawingSpatialViewTool, + FreezeSceneTool, SetAspectRatioSkewTool, ShowTileVolumesTool, Toggle3dManipulationsTool, ToggleDrawingGraphicsTool, ToggleSectionDrawingSpatialViewTool, ToggleTileTreeReferencesTool, ToggleViewAttachmentBoundariesTool, ToggleViewAttachmentClipShapesTool, ToggleViewAttachmentsTool, ViewportAddRealityModel, ViewportTileSizeModifierTool, } from "./tools/ViewportTools"; @@ -167,7 +167,6 @@ export class FrontendDevTools { SetAASamplesTool, SetAspectRatioSkewTool, SetScheduleScriptTool, - ToggleCanvasDecorationsTool, ToggleVolClassIntersect, SetMapBaseTool, SharpenEffect, diff --git a/core/frontend-devtools/src/tools/ViewportTools.ts b/core/frontend-devtools/src/tools/ViewportTools.ts index 219d3c52b9f8..611b821aba3d 100644 --- a/core/frontend-devtools/src/tools/ViewportTools.ts +++ b/core/frontend-devtools/src/tools/ViewportTools.ts @@ -607,25 +607,3 @@ export class ChangeCameraTool extends Tool { return this.run(camera); } } - -/** Change the camera settings of the selected viewport. - * @beta - */ -export class ToggleCanvasDecorationsTool extends Tool { - public static override get minArgs() { return 1; } - public static override get maxArgs() { return 1; } - public static override toolId = "ToggleCanvasDecorations"; - - public override async run(showDecorations: boolean): Promise { - const vp = IModelApp.viewManager.selectedView; - if (!vp) - return false; - vp.viewFlags = vp.viewFlags.with("canvasDecorations", showDecorations); - return true; - } - - public override async parseAndRun(...inArgs: string[]): Promise { - const showDecorations = !!parseToggle(inArgs[0]); - return this.run(showDecorations); - } -} diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index 4bc5898c6223..75e6a4a59f53 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -1480,18 +1480,16 @@ export class OnScreenTarget extends Target { this._2dCanvas.needsClear = false; } - if (this.currentViewFlags.canvasDecorations) { - const canvasDecs = this.graphics.canvasDecorations; - if (canvasDecs) { - for (const overlay of canvasDecs) { - ctx.save(); - if (overlay.position) - ctx.translate(overlay.position.x, overlay.position.y); - - overlay.drawDecoration(ctx); - this._2dCanvas.needsClear = true; - ctx.restore(); - } + const canvasDecs = this.graphics.canvasDecorations; + if (canvasDecs) { + for (const overlay of canvasDecs) { + ctx.save(); + if (overlay.position) + ctx.translate(overlay.position.x, overlay.position.y); + + overlay.drawDecoration(ctx); + this._2dCanvas.needsClear = true; + ctx.restore(); } } } From 570759b0d24a3d4cbb9d21c2deb62bd6c1ade890 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 21 Jan 2025 08:16:57 -0700 Subject: [PATCH 07/23] cleanup, extract-api --- common/api/core-common.api.md | 201 +++++++++++------- .../andremig-decoration_2025-01-10-17-05.json | 10 - .../andremig-decoration_2025-01-10-17-05.json | 10 - 3 files changed, 130 insertions(+), 91 deletions(-) delete mode 100644 common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json delete mode 100644 common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json diff --git a/common/api/core-common.api.md b/common/api/core-common.api.md index 0204a09c2bda..379ad6300c36 100644 --- a/common/api/core-common.api.md +++ b/common/api/core-common.api.md @@ -26,6 +26,7 @@ import { DbOpcode } from '@itwin/core-bentley'; import { DbResult } from '@itwin/core-bentley'; import { GeometryQuery } from '@itwin/core-geometry'; import { GeoServiceStatus } from '@itwin/core-bentley'; +import { GetMetaDataFunction } from '@itwin/core-bentley'; import { GuidString } from '@itwin/core-bentley'; import { Id64 } from '@itwin/core-bentley'; import { Id64Array } from '@itwin/core-bentley'; @@ -37,6 +38,7 @@ import { IndexedPolyface } from '@itwin/core-geometry'; import { IndexedPolyfaceVisitor } from '@itwin/core-geometry'; import { IndexedValue } from '@itwin/core-bentley'; import { IndexMap } from '@itwin/core-bentley'; +import { LogFunction } from '@itwin/core-bentley'; import { LoggingMetaData } from '@itwin/core-bentley'; import { LogLevel } from '@itwin/core-bentley'; import { LowAndHighXY } from '@itwin/core-geometry'; @@ -670,6 +672,10 @@ export abstract class BentleyCloudRpcProtocol extends WebAppRpcProtocol { supplyPathParametersForOperation(_operation: RpcOperation): OpenAPIParameter[]; } +export { BentleyError } + +export { BentleyStatus } + // @public export enum BisCodeSpec { // @internal (undocumented) @@ -868,6 +874,8 @@ export interface BriefcaseProps { readonly iModelId: GuidString; } +export { BriefcaseStatus } + // @public export function calculateSolarAngles(date: Date, location: Cartographic): { azimuth: number; @@ -1150,6 +1158,8 @@ export interface ChangesetRange { first: ChangesetIndex; } +export { ChangeSetStatus } + // @public export enum ChangesetType { Regular = 0, @@ -1899,12 +1909,12 @@ export interface CreateIModelProps extends IModelProps { } // @public -export interface CreateSnapshotIModelProps { +export interface CreateSnapshotIModelProps extends IModelEncryptionProps { readonly createClassViews?: boolean; } // @internal -export interface CreateStandaloneIModelProps { +export interface CreateStandaloneIModelProps extends IModelEncryptionProps { readonly allowEdit?: string; } @@ -2101,6 +2111,8 @@ export enum DbResponseStatus { Timeout = 4 } +export { DbResult } + // @beta (undocumented) export interface DbRuntimeStats { // (undocumented) @@ -2981,9 +2993,9 @@ export interface ElementPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { elementIds: Iterable; exclude?: boolean; modelIds?: Iterable; - // (undocumented) + // @internal (undocumented) priority?: never; - // (undocumented) + // @internal (undocumented) subCategoryIds?: never; } @@ -3315,33 +3327,66 @@ export enum FeatureIndexType { // @public export class FeatureOverrides implements FeatureAppearanceSource { constructor(); + // @internal addInvisibleElementOverridesToNeverDrawn(): void; get alwaysDrawn(): Id64.Uint32Set; + // @internal + protected readonly _alwaysDrawn: Id64.Uint32Set; alwaysDrawnIgnoresSubCategory: boolean; // @internal readonly animationNodeOverrides: Map; + // @internal protected _constructions: boolean; get defaultOverrides(): FeatureAppearance; + // @internal + protected _defaultOverrides: FeatureAppearance; + // @internal protected _dimensions: boolean; + // @internal protected readonly _elementOverrides: Id64.Uint32Map; getAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, geomClass: GeometryClass, modelLo: number, modelHi: number, type: BatchType, animationNodeId: number): FeatureAppearance | undefined; + // @internal + protected getClassifierAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, modelLo: number, modelHi: number, animationNodeId: number): FeatureAppearance | undefined; + // @internal (undocumented) + protected getElementOverrides(idLo: number, idHi: number, animationNodeId: number): FeatureAppearance | undefined; getElementOverridesById(id: Id64String): FeatureAppearance | undefined; getFeatureAppearance(feature: Feature, modelId: Id64String, type?: BatchType, animationNodeId?: number): FeatureAppearance | undefined; + // @internal (undocumented) + protected getModelOverrides(idLo: number, idHi: number): FeatureAppearance | undefined; getModelOverridesById(id: Id64String): FeatureAppearance | undefined; + // @internal (undocumented) + protected getSubCategoryOverrides(idLo: number, idHi: number): FeatureAppearance | undefined; getSubCategoryOverridesById(id: Id64String): FeatureAppearance | undefined; + // @internal getSubCategoryPriority(idLo: number, idHi: number): number; ignoreAnimationOverrides(ignore: IgnoreAnimationOverrides): void; + // @internal (undocumented) + protected readonly _ignoreAnimationOverrides: IgnoreAnimationOverrides[]; + // @internal ignoreSubCategory: boolean; + // @internal (undocumented) + protected isAlwaysDrawn(idLo: number, idHi: number): boolean; isAlwaysDrawnExclusive: boolean; isClassVisible(geomClass: GeometryClass): boolean; isFeatureVisible(feature: Feature): boolean; + // @internal (undocumented) + protected isNeverDrawn(elemIdLo: number, elemIdHi: number, animationNodeId: number): boolean; isSubCategoryIdVisible(id: Id64String): boolean; + // @internal isSubCategoryVisible(idLo: number, idHi: number): boolean; + // @internal (undocumented) isSubCategoryVisibleInModel(subcatLo: number, subcatHi: number, modelLo: number, modelHi: number): boolean; get lineWeights(): boolean; + // @internal protected _lineWeights: boolean; + // @internal + protected readonly _modelOverrides: Id64.Uint32Map; + // @internal protected readonly _modelSubCategoryOverrides: Id64.Uint32Map; get neverDrawn(): Id64.Uint32Set; + // @internal + protected readonly _neverDrawn: Id64.Uint32Set; + // @internal readonly neverDrawnAnimationNodes: Set; override(args: OverrideFeatureAppearanceArgs): void; overrideAnimationNode(id: number, app: FeatureAppearance): void; @@ -3351,6 +3396,7 @@ export class FeatureOverrides implements FeatureAppearanceSource { overrideModel(id: Id64String, app: FeatureAppearance, replaceExisting?: boolean): void; // @deprecated overrideSubCategory(id: Id64String, app: FeatureAppearance, replaceExisting?: boolean): void; + // @internal protected _patterns: boolean; setAlwaysDrawn(id: Id64String): void; setAlwaysDrawnSet(ids: Iterable, exclusive: boolean, ignoreSubCategory?: boolean): void; @@ -3359,8 +3405,11 @@ export class FeatureOverrides implements FeatureAppearanceSource { setNeverDrawn(id: Id64String): void; setNeverDrawnSet(ids: Iterable): void; setVisibleSubCategory(id: Id64String): void; + // @internal protected readonly _subCategoryOverrides: Id64.Uint32Map; + // @internal protected readonly _subCategoryPriorities: Id64.Uint32Map; + // @internal protected readonly _visibleSubCategories: Id64.Uint32Set; } @@ -3374,9 +3423,12 @@ export enum FeatureOverrideType { // @public export class FeatureTable extends IndexMap { constructor(maxFeatures: number, modelId?: Id64String, type?: BatchType); + // @internal (undocumented) get anyDefined(): boolean; findFeature(index: number): Feature | undefined; - getArray(): ReadonlyArray>; + // @internal (undocumented) + getArray(): Array>; + // @internal (undocumented) insertWithIndex(feature: Feature, index: number): void; get isPlanarClassifier(): boolean; get isUniform(): boolean; @@ -3440,32 +3492,10 @@ export interface FlatBufferGeometryStream { format: "flatbuffer"; } -// @beta -interface FontFace_2 { - familyName: string; - // (undocumented) - isBold: boolean; - // (undocumented) - isItalic: boolean; -} -export { FontFace_2 as FontFace } - -// @public -export interface FontFamilyDescriptor { - name: string; - type: FontType; -} - -// @beta -export interface FontFamilySelector { - name: string; - type?: FontType; -} - // @public export type FontId = number; -// @public @deprecated +// @public export class FontMap { constructor(props?: FontMapProps); // (undocumented) @@ -3477,21 +3507,26 @@ export class FontMap { toJSON(): FontMapProps; } -// @public @deprecated +// @public export interface FontMapProps { // (undocumented) fonts: FontProps[]; } // @public -export interface FontProps extends FontFamilyDescriptor { +export interface FontProps { id: FontId; + name: string; + type: FontType; } // @public export enum FontType { + // (undocumented) Rsc = 2, + // (undocumented) Shx = 3, + // (undocumented) TrueType = 1 } @@ -4122,6 +4157,8 @@ export enum GeometrySummaryVerbosity { // @internal (undocumented) export function getMaximumMajorTileFormatVersion(maxMajorVersion: number, formatVersion?: number): number; +export { GetMetaDataFunction } + // @internal export const getPullChangesIpcChannel: (iModelId: string) => string; @@ -4227,6 +4264,7 @@ export namespace Gradient { flags: Flags; static fromJSON(json?: SymbProps): Symb; getImage(width: number, height: number): ImageBuffer; + // @internal getThematicImageForRenderer(maxDimension: number): ImageBuffer; // (undocumented) get hasTranslucency(): boolean; @@ -4451,7 +4489,7 @@ export namespace HiddenLine { overrideColor(color: ColorDef | undefined): Style; overridePattern(pattern: LinePixels | undefined): Style; overrideWidth(width: number | undefined): Style; - // (undocumented) + // @internal (undocumented) get ovrColor(): boolean; readonly pattern?: LinePixels; // (undocumented) @@ -4460,6 +4498,7 @@ export namespace HiddenLine { } export interface StyleProps { color?: ColorDefProps; + // @internal ovrColor?: boolean; pattern?: LinePixels; width?: number; @@ -4717,11 +4756,17 @@ export interface IgnoreAnimationOverridesArgs { // @public export class ImageBuffer { + // @internal + protected constructor(data: Uint8Array, format: ImageBufferFormat, width: number); + // @internal (undocumented) + protected static computeHeight(data: Uint8Array, format: ImageBufferFormat, width: number): number; static create(data: Uint8Array, format: ImageBufferFormat, width: number): ImageBuffer; readonly data: Uint8Array; readonly format: ImageBufferFormat; static getNumBytesPerPixel(format: ImageBufferFormat): number; get height(): number; + // @internal (undocumented) + protected static isValidData(data: Uint8Array, format: ImageBufferFormat, width: number): boolean; get numBytesPerPixel(): number; readonly width: number; } @@ -4981,6 +5026,11 @@ export interface IModelCoordinatesResponseProps { iModelCoords: PointWithStatus[]; } +// @public @deprecated +export interface IModelEncryptionProps { + readonly password?: string; +} + // @public export class IModelError extends BentleyError { constructor(errorNumber: IModelErrorNumber | number, message: string, getMetaData?: LoggingMetaData); @@ -5093,6 +5143,8 @@ export interface IModelRpcProps extends IModelRpcOpenProps { readonly key: string; } +export { IModelStatus } + // @public (undocumented) export abstract class IModelTileRpcInterface extends RpcInterface { // @internal @@ -5458,8 +5510,8 @@ export function isPlacement3dProps(props: PlacementProps): props is Placement3dP // @public export function isPowerOfTwo(num: number): boolean; -// @public -export function isValidImageSourceFormat(format: number): format is ImageSourceFormat; +// @internal (undocumented) +export function isValidImageSourceFormat(format: ImageSourceFormat): boolean; // @internal export const iTwinChannel: (channel: string) => string; @@ -5695,6 +5747,10 @@ export enum LockState { Shared = 1 } +export { LogFunction } + +export { LoggingMetaData } + // @public export interface MapImageryProps { // (undocumented) @@ -5909,7 +5965,9 @@ export class MeshPolyline { } // @internal (undocumented) -export type MeshPolylineList = MeshPolyline[]; +export class MeshPolylineList extends Array { + constructor(...args: MeshPolyline[]); +} // @public export class ModelClipGroup { @@ -6040,14 +6098,14 @@ export class ModelMapLayerSettings extends MapLayerSettings { // @public export interface ModelPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { - // (undocumented) + // @internal (undocumented) elementIds?: never; - // (undocumented) + // @internal (undocumented) exclude?: never; modelIds?: Iterable; - // (undocumented) + // @internal (undocumented) priority?: never; - // (undocumented) + // @internal (undocumented) subCategoryIds?: never; } @@ -6445,7 +6503,7 @@ export interface OpenBriefcaseOptions { } // @public -export interface OpenBriefcaseProps extends OpenDbKey { +export interface OpenBriefcaseProps extends IModelEncryptionProps, OpenDbKey { readonly fileName: LocalFileName; readonly readonly?: boolean; readonly watchForChanges?: boolean; @@ -6498,9 +6556,9 @@ export enum OverriddenBy { // @public export interface OverrideElementAppearanceOptions extends OverrideFeatureAppearanceOptions { elementId: Id64String; - // (undocumented) + // @internal (undocumented) modelId?: never; - // (undocumented) + // @internal (undocumented) subCategoryId?: never; } @@ -6515,18 +6573,18 @@ export interface OverrideFeatureAppearanceOptions { // @public export interface OverrideModelAppearanceOptions extends OverrideFeatureAppearanceOptions { - // (undocumented) + // @internal (undocumented) elementId?: never; modelId: Id64String; - // (undocumented) + // @internal (undocumented) subCategoryId?: never; } // @public export interface OverrideSubCategoryAppearanceOptions extends OverrideFeatureAppearanceOptions { - // (undocumented) + // @internal (undocumented) elementId?: never; - // (undocumented) + // @internal (undocumented) modelId?: never; subCategoryId: Id64String; } @@ -6797,6 +6855,8 @@ export class PlanarClipMaskSettings { // @public export class PlanProjectionSettings { + // @internal + constructor(props: PlanProjectionSettingsProps); clone(changedProps?: PlanProjectionSettingsProps): PlanProjectionSettings; readonly elevation?: number; readonly enforceDisplayPriority?: boolean; @@ -6982,11 +7042,11 @@ export enum PrimitiveTypeCode { // @public export interface PriorityPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { - // (undocumented) + // @internal (undocumented) elementIds?: never; - // (undocumented) + // @internal (undocumented) exclude?: never; - // (undocumented) + // @internal (undocumented) modelIds?: never; priority: number; } @@ -7180,6 +7240,7 @@ export class QParams2d { static fromZeroToOne(rangeScale?: number): QParams2d; isQuantizable(point: Point2d): boolean; readonly origin: Point2d; + // @internal (undocumented) get rangeDiagonal(): Vector2d; readonly scale: Point2d; setFromRange(range: Range2d, rangeScale?: number): void; @@ -7209,6 +7270,7 @@ export class QParams3d { static fromZeroToOne(rangeScale?: number): QParams3d; isQuantizable(point: Point3d): boolean; readonly origin: Point3d; + // @internal (undocumented) get rangeDiagonal(): Vector3d; readonly scale: Point3d; setFromOriginAndScale(origin: Point3d, scale: Point3d): void; @@ -7355,8 +7417,11 @@ export namespace Quantization { const // (undocumented) rangeScale8 = 255; export function computeScale(extent: number, rangeScale?: number): number; + // @internal (undocumented) export function isInRange(qpos: number, rangeScale?: number): boolean; + // @internal (undocumented) export function isQuantizable(pos: number, origin: number, scale: number, rangeScale?: number): boolean; + // @internal (undocumented) export function isQuantized(qpos: number): boolean; export function quantize(pos: number, origin: number, scale: number, rangeScale?: number): number; export function unquantize(qpos: number, origin: number, scale: number): number; @@ -8931,18 +8996,6 @@ export class RpcSessionInvocation extends RpcInvocation { get rejected(): boolean; } -// @alpha -export interface RscFontEncodingProps { - // (undocumented) - codePage?: number; - // (undocumented) - degree?: number; - // (undocumented) - diameter?: number; - // (undocumented) - plusMinus?: number; -} - // @beta (undocumented) export type Run = TextRun | FractionRun | LineBreakRun; @@ -9336,7 +9389,7 @@ export abstract class SnapshotIModelRpcInterface extends RpcInterface { } // @public -export interface SnapshotOpenOptions extends OpenDbKey { +export interface SnapshotOpenOptions extends IModelEncryptionProps, OpenDbKey { // @internal readonly tempFileBase?: string; } @@ -9565,12 +9618,12 @@ export class SubCategoryOverride { // @public export interface SubCategoryPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { - // (undocumented) + // @internal (undocumented) elementIds?: never; - // (undocumented) + // @internal (undocumented) exclude?: never; modelIds?: Iterable; - // (undocumented) + // @internal (undocumented) priority?: never; subCategoryIds: Iterable; } @@ -9976,7 +10029,8 @@ export interface TextureLoadProps { export class TextureMapping { constructor(tx: RenderTexture, params: TextureMapping.Params); compare(other: TextureMapping): number; - computeUVParams(visitor: PolyfaceVisitor, localToWorld?: Transform): Point2d[] | undefined; + // @internal (undocumented) + computeUVParams(visitor: PolyfaceVisitor, transformToImodel: Transform): Point2d[] | undefined; // @beta normalMapParams?: NormalMapParams; readonly params: TextureMapping.Params; @@ -9998,11 +10052,15 @@ export namespace TextureMapping { repetitions: number; } export enum Mode { + // @internal (undocumented) Cubic = 4, + // @internal (undocumented) Cylindrical = 6, + // @internal (undocumented) DirectionalDrape = 3, // (undocumented) ElevationDrape = 1, + // @internal FrontProject = 8, // (undocumented) None = -1, @@ -10010,7 +10068,9 @@ export namespace TextureMapping { Parametric = 0, // (undocumented) Planar = 2, + // @internal (undocumented) Solid = 7, + // @internal (undocumented) Spherical = 5 } export interface ParamProps { @@ -10019,19 +10079,20 @@ export namespace TextureMapping { textureMat2x3?: TextureMapping.Trans2x3; textureWeight?: number; useConstantLod?: boolean; - // (undocumented) + // @internal (undocumented) worldMapping?: boolean; } export class Params { constructor(props?: TextureMapping.ParamProps); compare(other: Params): number; - computeUVParams(visitor: IndexedPolyfaceVisitor, localToWorld?: Transform): Point2d[] | undefined; + // @internal + computeUVParams(visitor: IndexedPolyfaceVisitor, transformToImodel: Transform): Point2d[] | undefined; constantLodParams: ConstantLodParams; mode: TextureMapping.Mode; textureMatrix: TextureMapping.Trans2x3; useConstantLod: boolean; weight: number; - // (undocumented) + // @internal (undocumented) worldMapping: boolean; } export class Trans2x3 { @@ -10900,7 +10961,6 @@ export interface ViewFlagProps { hidEdges?: boolean; monochrome?: boolean; noCameraLights?: boolean; - noCanvasDecorations?: boolean; noConstruct?: boolean; noDim?: boolean; noFill?: boolean; @@ -10926,8 +10986,6 @@ export class ViewFlags { readonly acsTriad: boolean; readonly ambientOcclusion: boolean; readonly backgroundMap: boolean; - // (undocumented) - readonly canvasDecorations: boolean; readonly clipVolume: boolean; readonly constructions: boolean; copy(changedFlags: Partial): ViewFlags; @@ -10945,6 +11003,7 @@ export class ViewFlags { readonly lighting: boolean; readonly materials: boolean; readonly monochrome: boolean; + // @internal normalize(): ViewFlags; override(overrides: Partial): ViewFlags; readonly patterns: boolean; diff --git a/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json b/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json deleted file mode 100644 index d1ac065f5d72..000000000000 --- a/common/changes/@itwin/core-common/andremig-decoration_2025-01-10-17-05.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@itwin/core-common", - "comment": "", - "type": "none" - } - ], - "packageName": "@itwin/core-common" -} \ No newline at end of file diff --git a/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json b/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json deleted file mode 100644 index 3704c5ffd7e2..000000000000 --- a/common/changes/@itwin/frontend-devtools/andremig-decoration_2025-01-10-17-05.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@itwin/frontend-devtools", - "comment": "", - "type": "none" - } - ], - "packageName": "@itwin/frontend-devtools" -} \ No newline at end of file From 5b86b527c5675adf35b5f798a5ddc77ca546a141 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Wed, 22 Jan 2025 09:23:38 -0700 Subject: [PATCH 08/23] Add ReadImageToCanvasOptions, SaveImageWithCanvasDecorations Tool --- common/api/core-common.api.md | 198 ++++++------------ common/api/core-frontend.api.md | 17 +- common/api/summary/core-frontend.exports.csv | 1 + .../andremig-decoration_2025-01-10-16-41.json | 4 +- core/frontend/src/Viewport.ts | 14 +- core/frontend/src/render/RenderTarget.ts | 4 +- core/frontend/src/render/webgl/Target.ts | 15 +- .../public/locales/en/SVTTools.json | 3 + .../display-test-app/src/frontend/App.ts | 3 +- .../src/frontend/SaveImageTool.ts | 50 ++++- 10 files changed, 157 insertions(+), 152 deletions(-) diff --git a/common/api/core-common.api.md b/common/api/core-common.api.md index 379ad6300c36..ce1ab313e538 100644 --- a/common/api/core-common.api.md +++ b/common/api/core-common.api.md @@ -26,7 +26,6 @@ import { DbOpcode } from '@itwin/core-bentley'; import { DbResult } from '@itwin/core-bentley'; import { GeometryQuery } from '@itwin/core-geometry'; import { GeoServiceStatus } from '@itwin/core-bentley'; -import { GetMetaDataFunction } from '@itwin/core-bentley'; import { GuidString } from '@itwin/core-bentley'; import { Id64 } from '@itwin/core-bentley'; import { Id64Array } from '@itwin/core-bentley'; @@ -38,7 +37,6 @@ import { IndexedPolyface } from '@itwin/core-geometry'; import { IndexedPolyfaceVisitor } from '@itwin/core-geometry'; import { IndexedValue } from '@itwin/core-bentley'; import { IndexMap } from '@itwin/core-bentley'; -import { LogFunction } from '@itwin/core-bentley'; import { LoggingMetaData } from '@itwin/core-bentley'; import { LogLevel } from '@itwin/core-bentley'; import { LowAndHighXY } from '@itwin/core-geometry'; @@ -672,10 +670,6 @@ export abstract class BentleyCloudRpcProtocol extends WebAppRpcProtocol { supplyPathParametersForOperation(_operation: RpcOperation): OpenAPIParameter[]; } -export { BentleyError } - -export { BentleyStatus } - // @public export enum BisCodeSpec { // @internal (undocumented) @@ -874,8 +868,6 @@ export interface BriefcaseProps { readonly iModelId: GuidString; } -export { BriefcaseStatus } - // @public export function calculateSolarAngles(date: Date, location: Cartographic): { azimuth: number; @@ -1158,8 +1150,6 @@ export interface ChangesetRange { first: ChangesetIndex; } -export { ChangeSetStatus } - // @public export enum ChangesetType { Regular = 0, @@ -1909,12 +1899,12 @@ export interface CreateIModelProps extends IModelProps { } // @public -export interface CreateSnapshotIModelProps extends IModelEncryptionProps { +export interface CreateSnapshotIModelProps { readonly createClassViews?: boolean; } // @internal -export interface CreateStandaloneIModelProps extends IModelEncryptionProps { +export interface CreateStandaloneIModelProps { readonly allowEdit?: string; } @@ -2111,8 +2101,6 @@ export enum DbResponseStatus { Timeout = 4 } -export { DbResult } - // @beta (undocumented) export interface DbRuntimeStats { // (undocumented) @@ -2993,9 +2981,9 @@ export interface ElementPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { elementIds: Iterable; exclude?: boolean; modelIds?: Iterable; - // @internal (undocumented) + // (undocumented) priority?: never; - // @internal (undocumented) + // (undocumented) subCategoryIds?: never; } @@ -3327,66 +3315,33 @@ export enum FeatureIndexType { // @public export class FeatureOverrides implements FeatureAppearanceSource { constructor(); - // @internal addInvisibleElementOverridesToNeverDrawn(): void; get alwaysDrawn(): Id64.Uint32Set; - // @internal - protected readonly _alwaysDrawn: Id64.Uint32Set; alwaysDrawnIgnoresSubCategory: boolean; // @internal readonly animationNodeOverrides: Map; - // @internal protected _constructions: boolean; get defaultOverrides(): FeatureAppearance; - // @internal - protected _defaultOverrides: FeatureAppearance; - // @internal protected _dimensions: boolean; - // @internal protected readonly _elementOverrides: Id64.Uint32Map; getAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, geomClass: GeometryClass, modelLo: number, modelHi: number, type: BatchType, animationNodeId: number): FeatureAppearance | undefined; - // @internal - protected getClassifierAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, modelLo: number, modelHi: number, animationNodeId: number): FeatureAppearance | undefined; - // @internal (undocumented) - protected getElementOverrides(idLo: number, idHi: number, animationNodeId: number): FeatureAppearance | undefined; getElementOverridesById(id: Id64String): FeatureAppearance | undefined; getFeatureAppearance(feature: Feature, modelId: Id64String, type?: BatchType, animationNodeId?: number): FeatureAppearance | undefined; - // @internal (undocumented) - protected getModelOverrides(idLo: number, idHi: number): FeatureAppearance | undefined; getModelOverridesById(id: Id64String): FeatureAppearance | undefined; - // @internal (undocumented) - protected getSubCategoryOverrides(idLo: number, idHi: number): FeatureAppearance | undefined; getSubCategoryOverridesById(id: Id64String): FeatureAppearance | undefined; - // @internal getSubCategoryPriority(idLo: number, idHi: number): number; ignoreAnimationOverrides(ignore: IgnoreAnimationOverrides): void; - // @internal (undocumented) - protected readonly _ignoreAnimationOverrides: IgnoreAnimationOverrides[]; - // @internal ignoreSubCategory: boolean; - // @internal (undocumented) - protected isAlwaysDrawn(idLo: number, idHi: number): boolean; isAlwaysDrawnExclusive: boolean; isClassVisible(geomClass: GeometryClass): boolean; isFeatureVisible(feature: Feature): boolean; - // @internal (undocumented) - protected isNeverDrawn(elemIdLo: number, elemIdHi: number, animationNodeId: number): boolean; isSubCategoryIdVisible(id: Id64String): boolean; - // @internal isSubCategoryVisible(idLo: number, idHi: number): boolean; - // @internal (undocumented) isSubCategoryVisibleInModel(subcatLo: number, subcatHi: number, modelLo: number, modelHi: number): boolean; get lineWeights(): boolean; - // @internal protected _lineWeights: boolean; - // @internal - protected readonly _modelOverrides: Id64.Uint32Map; - // @internal protected readonly _modelSubCategoryOverrides: Id64.Uint32Map; get neverDrawn(): Id64.Uint32Set; - // @internal - protected readonly _neverDrawn: Id64.Uint32Set; - // @internal readonly neverDrawnAnimationNodes: Set; override(args: OverrideFeatureAppearanceArgs): void; overrideAnimationNode(id: number, app: FeatureAppearance): void; @@ -3396,7 +3351,6 @@ export class FeatureOverrides implements FeatureAppearanceSource { overrideModel(id: Id64String, app: FeatureAppearance, replaceExisting?: boolean): void; // @deprecated overrideSubCategory(id: Id64String, app: FeatureAppearance, replaceExisting?: boolean): void; - // @internal protected _patterns: boolean; setAlwaysDrawn(id: Id64String): void; setAlwaysDrawnSet(ids: Iterable, exclusive: boolean, ignoreSubCategory?: boolean): void; @@ -3405,11 +3359,8 @@ export class FeatureOverrides implements FeatureAppearanceSource { setNeverDrawn(id: Id64String): void; setNeverDrawnSet(ids: Iterable): void; setVisibleSubCategory(id: Id64String): void; - // @internal protected readonly _subCategoryOverrides: Id64.Uint32Map; - // @internal protected readonly _subCategoryPriorities: Id64.Uint32Map; - // @internal protected readonly _visibleSubCategories: Id64.Uint32Set; } @@ -3423,12 +3374,9 @@ export enum FeatureOverrideType { // @public export class FeatureTable extends IndexMap { constructor(maxFeatures: number, modelId?: Id64String, type?: BatchType); - // @internal (undocumented) get anyDefined(): boolean; findFeature(index: number): Feature | undefined; - // @internal (undocumented) - getArray(): Array>; - // @internal (undocumented) + getArray(): ReadonlyArray>; insertWithIndex(feature: Feature, index: number): void; get isPlanarClassifier(): boolean; get isUniform(): boolean; @@ -3492,10 +3440,32 @@ export interface FlatBufferGeometryStream { format: "flatbuffer"; } +// @beta +interface FontFace_2 { + familyName: string; + // (undocumented) + isBold: boolean; + // (undocumented) + isItalic: boolean; +} +export { FontFace_2 as FontFace } + // @public -export type FontId = number; +export interface FontFamilyDescriptor { + name: string; + type: FontType; +} + +// @beta +export interface FontFamilySelector { + name: string; + type?: FontType; +} // @public +export type FontId = number; + +// @public @deprecated export class FontMap { constructor(props?: FontMapProps); // (undocumented) @@ -3507,26 +3477,21 @@ export class FontMap { toJSON(): FontMapProps; } -// @public +// @public @deprecated export interface FontMapProps { // (undocumented) fonts: FontProps[]; } // @public -export interface FontProps { +export interface FontProps extends FontFamilyDescriptor { id: FontId; - name: string; - type: FontType; } // @public export enum FontType { - // (undocumented) Rsc = 2, - // (undocumented) Shx = 3, - // (undocumented) TrueType = 1 } @@ -4157,8 +4122,6 @@ export enum GeometrySummaryVerbosity { // @internal (undocumented) export function getMaximumMajorTileFormatVersion(maxMajorVersion: number, formatVersion?: number): number; -export { GetMetaDataFunction } - // @internal export const getPullChangesIpcChannel: (iModelId: string) => string; @@ -4264,7 +4227,6 @@ export namespace Gradient { flags: Flags; static fromJSON(json?: SymbProps): Symb; getImage(width: number, height: number): ImageBuffer; - // @internal getThematicImageForRenderer(maxDimension: number): ImageBuffer; // (undocumented) get hasTranslucency(): boolean; @@ -4489,7 +4451,7 @@ export namespace HiddenLine { overrideColor(color: ColorDef | undefined): Style; overridePattern(pattern: LinePixels | undefined): Style; overrideWidth(width: number | undefined): Style; - // @internal (undocumented) + // (undocumented) get ovrColor(): boolean; readonly pattern?: LinePixels; // (undocumented) @@ -4498,7 +4460,6 @@ export namespace HiddenLine { } export interface StyleProps { color?: ColorDefProps; - // @internal ovrColor?: boolean; pattern?: LinePixels; width?: number; @@ -4756,17 +4717,11 @@ export interface IgnoreAnimationOverridesArgs { // @public export class ImageBuffer { - // @internal - protected constructor(data: Uint8Array, format: ImageBufferFormat, width: number); - // @internal (undocumented) - protected static computeHeight(data: Uint8Array, format: ImageBufferFormat, width: number): number; static create(data: Uint8Array, format: ImageBufferFormat, width: number): ImageBuffer; readonly data: Uint8Array; readonly format: ImageBufferFormat; static getNumBytesPerPixel(format: ImageBufferFormat): number; get height(): number; - // @internal (undocumented) - protected static isValidData(data: Uint8Array, format: ImageBufferFormat, width: number): boolean; get numBytesPerPixel(): number; readonly width: number; } @@ -5026,11 +4981,6 @@ export interface IModelCoordinatesResponseProps { iModelCoords: PointWithStatus[]; } -// @public @deprecated -export interface IModelEncryptionProps { - readonly password?: string; -} - // @public export class IModelError extends BentleyError { constructor(errorNumber: IModelErrorNumber | number, message: string, getMetaData?: LoggingMetaData); @@ -5143,8 +5093,6 @@ export interface IModelRpcProps extends IModelRpcOpenProps { readonly key: string; } -export { IModelStatus } - // @public (undocumented) export abstract class IModelTileRpcInterface extends RpcInterface { // @internal @@ -5510,8 +5458,8 @@ export function isPlacement3dProps(props: PlacementProps): props is Placement3dP // @public export function isPowerOfTwo(num: number): boolean; -// @internal (undocumented) -export function isValidImageSourceFormat(format: ImageSourceFormat): boolean; +// @public +export function isValidImageSourceFormat(format: number): format is ImageSourceFormat; // @internal export const iTwinChannel: (channel: string) => string; @@ -5747,10 +5695,6 @@ export enum LockState { Shared = 1 } -export { LogFunction } - -export { LoggingMetaData } - // @public export interface MapImageryProps { // (undocumented) @@ -5965,9 +5909,7 @@ export class MeshPolyline { } // @internal (undocumented) -export class MeshPolylineList extends Array { - constructor(...args: MeshPolyline[]); -} +export type MeshPolylineList = MeshPolyline[]; // @public export class ModelClipGroup { @@ -6098,14 +6040,14 @@ export class ModelMapLayerSettings extends MapLayerSettings { // @public export interface ModelPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { - // @internal (undocumented) + // (undocumented) elementIds?: never; - // @internal (undocumented) + // (undocumented) exclude?: never; modelIds?: Iterable; - // @internal (undocumented) + // (undocumented) priority?: never; - // @internal (undocumented) + // (undocumented) subCategoryIds?: never; } @@ -6503,7 +6445,7 @@ export interface OpenBriefcaseOptions { } // @public -export interface OpenBriefcaseProps extends IModelEncryptionProps, OpenDbKey { +export interface OpenBriefcaseProps extends OpenDbKey { readonly fileName: LocalFileName; readonly readonly?: boolean; readonly watchForChanges?: boolean; @@ -6556,9 +6498,9 @@ export enum OverriddenBy { // @public export interface OverrideElementAppearanceOptions extends OverrideFeatureAppearanceOptions { elementId: Id64String; - // @internal (undocumented) + // (undocumented) modelId?: never; - // @internal (undocumented) + // (undocumented) subCategoryId?: never; } @@ -6573,18 +6515,18 @@ export interface OverrideFeatureAppearanceOptions { // @public export interface OverrideModelAppearanceOptions extends OverrideFeatureAppearanceOptions { - // @internal (undocumented) + // (undocumented) elementId?: never; modelId: Id64String; - // @internal (undocumented) + // (undocumented) subCategoryId?: never; } // @public export interface OverrideSubCategoryAppearanceOptions extends OverrideFeatureAppearanceOptions { - // @internal (undocumented) + // (undocumented) elementId?: never; - // @internal (undocumented) + // (undocumented) modelId?: never; subCategoryId: Id64String; } @@ -6855,8 +6797,6 @@ export class PlanarClipMaskSettings { // @public export class PlanProjectionSettings { - // @internal - constructor(props: PlanProjectionSettingsProps); clone(changedProps?: PlanProjectionSettingsProps): PlanProjectionSettings; readonly elevation?: number; readonly enforceDisplayPriority?: boolean; @@ -7042,11 +6982,11 @@ export enum PrimitiveTypeCode { // @public export interface PriorityPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { - // @internal (undocumented) + // (undocumented) elementIds?: never; - // @internal (undocumented) + // (undocumented) exclude?: never; - // @internal (undocumented) + // (undocumented) modelIds?: never; priority: number; } @@ -7240,7 +7180,6 @@ export class QParams2d { static fromZeroToOne(rangeScale?: number): QParams2d; isQuantizable(point: Point2d): boolean; readonly origin: Point2d; - // @internal (undocumented) get rangeDiagonal(): Vector2d; readonly scale: Point2d; setFromRange(range: Range2d, rangeScale?: number): void; @@ -7270,7 +7209,6 @@ export class QParams3d { static fromZeroToOne(rangeScale?: number): QParams3d; isQuantizable(point: Point3d): boolean; readonly origin: Point3d; - // @internal (undocumented) get rangeDiagonal(): Vector3d; readonly scale: Point3d; setFromOriginAndScale(origin: Point3d, scale: Point3d): void; @@ -7417,11 +7355,8 @@ export namespace Quantization { const // (undocumented) rangeScale8 = 255; export function computeScale(extent: number, rangeScale?: number): number; - // @internal (undocumented) export function isInRange(qpos: number, rangeScale?: number): boolean; - // @internal (undocumented) export function isQuantizable(pos: number, origin: number, scale: number, rangeScale?: number): boolean; - // @internal (undocumented) export function isQuantized(qpos: number): boolean; export function quantize(pos: number, origin: number, scale: number, rangeScale?: number): number; export function unquantize(qpos: number, origin: number, scale: number): number; @@ -8996,6 +8931,18 @@ export class RpcSessionInvocation extends RpcInvocation { get rejected(): boolean; } +// @alpha +export interface RscFontEncodingProps { + // (undocumented) + codePage?: number; + // (undocumented) + degree?: number; + // (undocumented) + diameter?: number; + // (undocumented) + plusMinus?: number; +} + // @beta (undocumented) export type Run = TextRun | FractionRun | LineBreakRun; @@ -9389,7 +9336,7 @@ export abstract class SnapshotIModelRpcInterface extends RpcInterface { } // @public -export interface SnapshotOpenOptions extends IModelEncryptionProps, OpenDbKey { +export interface SnapshotOpenOptions extends OpenDbKey { // @internal readonly tempFileBase?: string; } @@ -9618,12 +9565,12 @@ export class SubCategoryOverride { // @public export interface SubCategoryPlanarClipMaskArgs extends BasicPlanarClipMaskArgs { - // @internal (undocumented) + // (undocumented) elementIds?: never; - // @internal (undocumented) + // (undocumented) exclude?: never; modelIds?: Iterable; - // @internal (undocumented) + // (undocumented) priority?: never; subCategoryIds: Iterable; } @@ -10029,8 +9976,7 @@ export interface TextureLoadProps { export class TextureMapping { constructor(tx: RenderTexture, params: TextureMapping.Params); compare(other: TextureMapping): number; - // @internal (undocumented) - computeUVParams(visitor: PolyfaceVisitor, transformToImodel: Transform): Point2d[] | undefined; + computeUVParams(visitor: PolyfaceVisitor, localToWorld?: Transform): Point2d[] | undefined; // @beta normalMapParams?: NormalMapParams; readonly params: TextureMapping.Params; @@ -10052,15 +9998,11 @@ export namespace TextureMapping { repetitions: number; } export enum Mode { - // @internal (undocumented) Cubic = 4, - // @internal (undocumented) Cylindrical = 6, - // @internal (undocumented) DirectionalDrape = 3, // (undocumented) ElevationDrape = 1, - // @internal FrontProject = 8, // (undocumented) None = -1, @@ -10068,9 +10010,7 @@ export namespace TextureMapping { Parametric = 0, // (undocumented) Planar = 2, - // @internal (undocumented) Solid = 7, - // @internal (undocumented) Spherical = 5 } export interface ParamProps { @@ -10079,20 +10019,19 @@ export namespace TextureMapping { textureMat2x3?: TextureMapping.Trans2x3; textureWeight?: number; useConstantLod?: boolean; - // @internal (undocumented) + // (undocumented) worldMapping?: boolean; } export class Params { constructor(props?: TextureMapping.ParamProps); compare(other: Params): number; - // @internal - computeUVParams(visitor: IndexedPolyfaceVisitor, transformToImodel: Transform): Point2d[] | undefined; + computeUVParams(visitor: IndexedPolyfaceVisitor, localToWorld?: Transform): Point2d[] | undefined; constantLodParams: ConstantLodParams; mode: TextureMapping.Mode; textureMatrix: TextureMapping.Trans2x3; useConstantLod: boolean; weight: number; - // @internal (undocumented) + // (undocumented) worldMapping: boolean; } export class Trans2x3 { @@ -11003,7 +10942,6 @@ export class ViewFlags { readonly lighting: boolean; readonly materials: boolean; readonly monochrome: boolean; - // @internal normalize(): ViewFlags; override(overrides: Partial): ViewFlags; readonly patterns: boolean; diff --git a/common/api/core-frontend.api.md b/common/api/core-frontend.api.md index eb3e24f1bfd3..f0c2fc9c63bf 100644 --- a/common/api/core-frontend.api.md +++ b/common/api/core-frontend.api.md @@ -7194,7 +7194,7 @@ export abstract class MapTilingScheme { readonly numberOfLevelZeroTilesX: number; readonly numberOfLevelZeroTilesY: number; // @alpha (undocumented) - get rootLevel(): 0 | -1; + get rootLevel(): -1 | 0; readonly rowZeroAtNorthPole: boolean; tileBordersNorthPole(row: number, level: number): boolean; tileBordersSouthPole(row: number, level: number): boolean; @@ -8234,7 +8234,7 @@ export class OffScreenTarget extends Target { // (undocumented) onResized(): void; // (undocumented) - readImageToCanvas(): HTMLCanvasElement; + readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement; // (undocumented) setViewRect(rect: ViewRect, temporary: boolean): void; // (undocumented) @@ -8318,7 +8318,7 @@ export class OnScreenTarget extends Target { // (undocumented) pickOverlayDecoration(pt: XAndY): CanvasDecoration | undefined; // (undocumented) - readImageToCanvas(): HTMLCanvasElement; + readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement; // (undocumented) setRenderToScreen(toScreen: boolean): HTMLCanvasElement | undefined; // (undocumented) @@ -9070,6 +9070,11 @@ export interface ReadImageBufferArgs { upsideDown?: boolean; } +// @public +export interface ReadImageToCanvasOptions { + includeCanvasDecorations?: boolean; +} + // @internal (undocumented) export function readImdlContent(args: ImdlReaderCreateArgs & { parseDocument?: (parseOpts: ImdlParserOptions) => Promise; @@ -10351,7 +10356,7 @@ export abstract class RenderTarget implements IDisposable, RenderMemory.Consumer // @internal (undocumented) readImageBuffer(_args?: ReadImageBufferArgs): ImageBuffer | undefined; // @internal (undocumented) - readImageToCanvas(): HTMLCanvasElement; + readImageToCanvas(_options?: ReadImageToCanvasOptions): HTMLCanvasElement; // @internal (undocumented) abstract readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable: boolean, excludedElements?: Iterable): void; // @internal (undocumented) @@ -11612,7 +11617,7 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo // (undocumented) computeEdgeWeight(pass: RenderPass, baseWeight: number): number; // (undocumented) - copyImageToCanvas(): HTMLCanvasElement; + copyImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement; // (undocumented) createPlanarClassifier(properties?: ActiveSpatialClassifier): PlanarClassifier; // (undocumented) @@ -14748,7 +14753,7 @@ export abstract class Viewport implements IDisposable, TileUser { // @deprecated readImage(rect?: ViewRect, targetSize?: Point2d, flipVertically?: boolean): ImageBuffer | undefined; readImageBuffer(args?: ReadImageBufferArgs): ImageBuffer | undefined; - readImageToCanvas(): HTMLCanvasElement; + readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement; readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable?: boolean): void; readPixels(args: ReadPixelsArgs): void; // @internal diff --git a/common/api/summary/core-frontend.exports.csv b/common/api/summary/core-frontend.exports.csv index 993470fcd00b..89aab09df09f 100644 --- a/common/api/summary/core-frontend.exports.csv +++ b/common/api/summary/core-frontend.exports.csv @@ -582,6 +582,7 @@ public;function;readGltfGraphics public;interface;ReadGltfGraphicsArgs beta;function;readGltfTemplate public;interface;ReadImageBufferArgs +public;interface;ReadImageToCanvasOptions internal;function;readImdlContent public;interface;ReadMeshArgs internal;class;ReadonlyTileUserSet diff --git a/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json b/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json index 00906e73270a..74b4417f44b6 100644 --- a/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json +++ b/common/changes/@itwin/core-frontend/andremig-decoration_2025-01-10-16-41.json @@ -2,9 +2,9 @@ "changes": [ { "packageName": "@itwin/core-frontend", - "comment": "", + "comment": "Add ReadImageToCanvasOptions", "type": "none" } ], "packageName": "@itwin/core-frontend" -} +} \ No newline at end of file diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index 066f9c15d583..98cdce1e8f95 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -262,6 +262,16 @@ export interface ReadPixelsArgs { excludedElements?: Iterable; } +/** Arguments supplied to [[Viewport.readImageToCanvas]]. + * @public + */ +export interface ReadImageToCanvasOptions { + /** If defined, canvas decorations will be included in the saved image. + * @note This only affects single viewport applications. For multi-viewport applications, the canvas decorations are always included. + */ + includeCanvasDecorations?: boolean; +} + /** A Viewport renders the contents of one or more [GeometricModel]($backend)s onto an `HTMLCanvasElement`. * * It holds a [[ViewState]] object that defines its viewing parameters; the ViewState in turn defines the [[DisplayStyleState]], @@ -2703,8 +2713,8 @@ export abstract class Viewport implements IDisposable, TileUser { /** Reads the current image from this viewport into an HTMLCanvasElement with a Canvas2dRenderingContext such that additional 2d graphics can be drawn onto it. * @see [[readImageBuffer]] to obtain the image as an array of RGBA pixels. */ - public readImageToCanvas(): HTMLCanvasElement { - return this.target.readImageToCanvas(); + public readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { + return this.target.readImageToCanvas(options); } /** Used internally by `waitForSceneCompletion`. diff --git a/core/frontend/src/render/RenderTarget.ts b/core/frontend/src/render/RenderTarget.ts index 63e8c552973a..513bd7cb06be 100644 --- a/core/frontend/src/render/RenderTarget.ts +++ b/core/frontend/src/render/RenderTarget.ts @@ -12,7 +12,7 @@ import { Point2d, XAndY } from "@itwin/core-geometry"; import { IModelConnection } from "../IModelConnection"; import { HiliteSet } from "../SelectionSet"; import { SceneContext } from "../ViewContext"; -import { ReadImageBufferArgs, Viewport } from "../Viewport"; +import { ReadImageBufferArgs, ReadImageToCanvasOptions, Viewport } from "../Viewport"; import { ViewRect } from "../common/ViewRect"; import { CanvasDecoration } from "./CanvasDecoration"; import { Decorations } from "./Decorations"; @@ -176,7 +176,7 @@ export abstract class RenderTarget implements IDisposable, RenderMemory.Consumer /** @internal */ public readImageBuffer(_args?: ReadImageBufferArgs): ImageBuffer | undefined { return undefined; } /** @internal */ - public readImageToCanvas(): HTMLCanvasElement { return document.createElement("canvas"); } + public readImageToCanvas(_options?: ReadImageToCanvasOptions): HTMLCanvasElement { return document.createElement("canvas"); } /** @internal */ public collectStatistics(_stats: RenderMemory.Statistics): void { } diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index 75e6a4a59f53..fb742f4e6bd3 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -15,7 +15,7 @@ import { ViewRect } from "../../common/ViewRect"; import { canvasToImageBuffer, canvasToResizedCanvasWithBars, imageBufferToCanvas } from "../../common/ImageUtil"; import { HiliteSet, ModelSubCategoryHiliteMode } from "../../SelectionSet"; import { SceneContext } from "../../ViewContext"; -import { ReadImageBufferArgs, Viewport } from "../../Viewport"; +import { ReadImageBufferArgs, ReadImageToCanvasOptions, Viewport } from "../../Viewport"; import { IModelConnection } from "../../IModelConnection"; import { CanvasDecoration } from "../CanvasDecoration"; import { Decorations } from "../Decorations"; @@ -1178,7 +1178,7 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo return image; } - public copyImageToCanvas(): HTMLCanvasElement { + public copyImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { const image = this.readImageBuffer(); const canvas = undefined !== image ? imageBufferToCanvas(image, false) : undefined; const retCanvas = undefined !== canvas ? canvas : document.createElement("canvas"); @@ -1188,7 +1188,8 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo const vp = IModelApp.viewManager.selectedView; // vp.rendersToScreen tells us if the view is being rendered directly to the webgl canvas on screen, which happens in the case of a single viewport. // In that case, we need to combine the 2d canvas with the webgl canvas or we will lose canvas decorations in the copied image. - if (vp && vp.rendersToScreen) { + // options.includeCanvasDecorations will be true if the caller wants to include the decorations in the copied image. + if (vp && vp.rendersToScreen && options?.includeCanvasDecorations) { const twoDCanvas = vp.canvas; const ctx = retCanvas.getContext("2d")!; ctx.drawImage(twoDCanvas, 0, 0); @@ -1524,8 +1525,8 @@ export class OnScreenTarget extends Target { return toScreen ? this._webglCanvas.canvas : undefined; } - public override readImageToCanvas(): HTMLCanvasElement { - return this._usingWebGLCanvas ? this.copyImageToCanvas() : this._2dCanvas.canvas; + public override readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { + return this._usingWebGLCanvas ? this.copyImageToCanvas(options) : this._2dCanvas.canvas; } } @@ -1568,8 +1569,8 @@ export class OffScreenTarget extends Target { this.renderSystem.frameBufferStack.pop(); } - public override readImageToCanvas(): HTMLCanvasElement { - return this.copyImageToCanvas(); + public override readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { + return this.copyImageToCanvas(options); } } diff --git a/test-apps/display-test-app/public/locales/en/SVTTools.json b/test-apps/display-test-app/public/locales/en/SVTTools.json index 82336cdd7394..37ee0309c457 100644 --- a/test-apps/display-test-app/public/locales/en/SVTTools.json +++ b/test-apps/display-test-app/public/locales/en/SVTTools.json @@ -55,6 +55,9 @@ "SaveImage": { "keyin": "dta save image" }, + "SaveImageWithCanvasDecorations": { + "keyin": "dta save image with decorations" + }, "OutputShaders": { "keyin": "dta output shaders" }, diff --git a/test-apps/display-test-app/src/frontend/App.ts b/test-apps/display-test-app/src/frontend/App.ts index 6fc76b02b1c9..81923c77b2ea 100644 --- a/test-apps/display-test-app/src/frontend/App.ts +++ b/test-apps/display-test-app/src/frontend/App.ts @@ -57,7 +57,7 @@ import { MarkupTool, ModelClipTool, ZoomToSelectedElementsTool } from "./Viewer" import { MacroTool } from "./MacroTools"; import { RecordTileSizesTool } from "./TileSizeRecorder"; import { TerrainDrapeTool } from "./TerrainDrapeTool"; -import { SaveImageTool } from "./SaveImageTool"; +import { SaveImageTool, SaveImageWithCanvasDecorationsTool } from "./SaveImageTool"; import { ToggleSecondaryIModelTool } from "./TiledGraphics"; import { BingTerrainMeshProvider } from "./BingTerrainProvider"; import { AttachCustomRealityDataTool, registerRealityDataSourceProvider } from "./RealityDataProvider"; @@ -358,6 +358,7 @@ export class DisplayTestApp { ResizeWindowTool, RestoreWindowTool, SaveImageTool, + SaveImageWithCanvasDecorationsTool, ShutDownTool, SignInTool, SignOutTool, diff --git a/test-apps/display-test-app/src/frontend/SaveImageTool.ts b/test-apps/display-test-app/src/frontend/SaveImageTool.ts index e2546b326957..fa3d2f2d7839 100644 --- a/test-apps/display-test-app/src/frontend/SaveImageTool.ts +++ b/test-apps/display-test-app/src/frontend/SaveImageTool.ts @@ -5,8 +5,8 @@ import { ProcessDetector } from "@itwin/core-bentley"; import { Point2d } from "@itwin/core-geometry"; -import { imageBufferToPngDataUrl, IModelApp, openImageDataUrlInNewWindow, Tool } from "@itwin/core-frontend"; -import { parseArgs } from "@itwin/frontend-devtools"; +import { imageBufferToPngDataUrl, IModelApp, openImageDataUrlInNewWindow, ReadImageToCanvasOptions, Tool } from "@itwin/core-frontend"; +import { parseArgs, parseToggle } from "@itwin/frontend-devtools"; interface SaveImageOptions { copyToClipboard?: boolean; @@ -89,3 +89,49 @@ export class SaveImageTool extends Tool { return this.run(opts); } } + +export class SaveImageWithCanvasDecorationsTool extends Tool { + public static override toolId = "SaveImageWithCanvasDecorations"; + public static override get minArgs() { return 0; } + public static override get maxArgs() { return 1; } + + public override async run(showDecorations: boolean): Promise { + const vp = IModelApp.viewManager.selectedView; + if (!vp) + return false; + + const width = vp.viewRect.width; + const height = vp.viewRect.height; + + await vp.waitForSceneCompletion(); + const buffer = vp.readImageBuffer({ size: new Point2d(width, height) }); + if (!buffer) { + alert("Failed to read image"); + return true; + } + + const options: ReadImageToCanvasOptions = { + includeCanvasDecorations: showDecorations, + } + + const canvas = vp.readImageToCanvas(options); + const url = canvas.toDataURL(); + + if (!url) { + alert("Failed to produce PNG"); + return true; + } + + openImageDataUrlInNewWindow(url, "Saved View"); + return true; + } + + public override async parseAndRun(...args: string[]): Promise { + let showDecorations; + if (!args[0]) + showDecorations = true; + else + showDecorations = !!parseToggle(args[0]); + return this.run(showDecorations); + } +} From b3b93be2f514d001050296d83f79d0159795a2a9 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Wed, 22 Jan 2025 09:41:05 -0700 Subject: [PATCH 09/23] extract api --- common/api/core-frontend.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/api/core-frontend.api.md b/common/api/core-frontend.api.md index 83ea15c88d06..2cbe0acca4e4 100644 --- a/common/api/core-frontend.api.md +++ b/common/api/core-frontend.api.md @@ -7190,7 +7190,7 @@ export abstract class MapTilingScheme { readonly numberOfLevelZeroTilesX: number; readonly numberOfLevelZeroTilesY: number; // @alpha (undocumented) - get rootLevel(): -1 | 0; + get rootLevel(): 0 | -1; readonly rowZeroAtNorthPole: boolean; tileBordersNorthPole(row: number, level: number): boolean; tileBordersSouthPole(row: number, level: number): boolean; From 0807157e2f99f46330537c4b5a0e6d21c80f2321 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Wed, 22 Jan 2025 15:54:48 -0700 Subject: [PATCH 10/23] rm SaveImageWithDecorations tool, update dta readme --- test-apps/display-test-app/README.md | 3 +- .../public/locales/en/SVTTools.json | 3 - .../display-test-app/src/frontend/App.ts | 3 +- .../src/frontend/SaveImageTool.ts | 64 ++++--------------- 4 files changed, 17 insertions(+), 56 deletions(-) diff --git a/test-apps/display-test-app/README.md b/test-apps/display-test-app/README.md index 8e6f00f7aa2f..28fbfd4763bc 100644 --- a/test-apps/display-test-app/README.md +++ b/test-apps/display-test-app/README.md @@ -202,7 +202,7 @@ You can use these environment variables to alter the default behavior of various * If defined, the scope to be used for OIDC auth. * IMJS_OIDC_REDIRECT_URI * If defined, the redirect URI to be used for OIDC auth. - * NOTE: as long as IMJS_OIDC_HEADLESS is not defined, OIDC auth will default to using "http://localhost:3000/signin-callback" for this. + * NOTE: as long as IMJS_OIDC_HEADLESS is not defined, OIDC auth will default to using "" for this. * IMJS_OIDC_CLIENT_SECRET * If defined in iOS, the client secret to be used for OIDC auth. * IMJS_BRIEFCASE_CACHE_LOCATION @@ -251,6 +251,7 @@ display-test-app has access to all key-ins defined in the `@itwin/core-frontend` * `h=height` - the desired height of the image in pixels. e.g. `h=480`. * `d=dimensions` - the desired width and height of the image in pixels. The image will be square. e.g. `d=768`. * `c=0|1` - if `1`, instead of opening a new window to display the image, the image will be copied to the clipboard. NOTE: this probably doesn't work in Firefox. + * `i=0|1` - if `1`, canvas decorations will be included in the saved image. By default they are not. NOTE: This is only true if a single viewport is present. If multiple viewports are present, canvas decorations will always be included in the saved image. * `dta record fps` *numFrames* - record average frames-per-second over the specified number of frames (default: 150) and output to status bar. * `dta zoom selected` - zoom the selected viewport to the elements in the selection set. Optional arguments specify the margin or padding percent as follows: * `l=` `r=` `t=` `b=` followed by a number indicating the left, right, top, and/or bottom padding or margin percent. diff --git a/test-apps/display-test-app/public/locales/en/SVTTools.json b/test-apps/display-test-app/public/locales/en/SVTTools.json index 37ee0309c457..82336cdd7394 100644 --- a/test-apps/display-test-app/public/locales/en/SVTTools.json +++ b/test-apps/display-test-app/public/locales/en/SVTTools.json @@ -55,9 +55,6 @@ "SaveImage": { "keyin": "dta save image" }, - "SaveImageWithCanvasDecorations": { - "keyin": "dta save image with decorations" - }, "OutputShaders": { "keyin": "dta output shaders" }, diff --git a/test-apps/display-test-app/src/frontend/App.ts b/test-apps/display-test-app/src/frontend/App.ts index 81923c77b2ea..6fc76b02b1c9 100644 --- a/test-apps/display-test-app/src/frontend/App.ts +++ b/test-apps/display-test-app/src/frontend/App.ts @@ -57,7 +57,7 @@ import { MarkupTool, ModelClipTool, ZoomToSelectedElementsTool } from "./Viewer" import { MacroTool } from "./MacroTools"; import { RecordTileSizesTool } from "./TileSizeRecorder"; import { TerrainDrapeTool } from "./TerrainDrapeTool"; -import { SaveImageTool, SaveImageWithCanvasDecorationsTool } from "./SaveImageTool"; +import { SaveImageTool } from "./SaveImageTool"; import { ToggleSecondaryIModelTool } from "./TiledGraphics"; import { BingTerrainMeshProvider } from "./BingTerrainProvider"; import { AttachCustomRealityDataTool, registerRealityDataSourceProvider } from "./RealityDataProvider"; @@ -358,7 +358,6 @@ export class DisplayTestApp { ResizeWindowTool, RestoreWindowTool, SaveImageTool, - SaveImageWithCanvasDecorationsTool, ShutDownTool, SignInTool, SignOutTool, diff --git a/test-apps/display-test-app/src/frontend/SaveImageTool.ts b/test-apps/display-test-app/src/frontend/SaveImageTool.ts index fa3d2f2d7839..d63f22fbba10 100644 --- a/test-apps/display-test-app/src/frontend/SaveImageTool.ts +++ b/test-apps/display-test-app/src/frontend/SaveImageTool.ts @@ -5,19 +5,20 @@ import { ProcessDetector } from "@itwin/core-bentley"; import { Point2d } from "@itwin/core-geometry"; -import { imageBufferToPngDataUrl, IModelApp, openImageDataUrlInNewWindow, ReadImageToCanvasOptions, Tool } from "@itwin/core-frontend"; -import { parseArgs, parseToggle } from "@itwin/frontend-devtools"; +import { imageBufferToPngDataUrl, IModelApp, openImageDataUrlInNewWindow, Tool } from "@itwin/core-frontend"; +import { parseArgs } from "@itwin/frontend-devtools"; interface SaveImageOptions { copyToClipboard?: boolean; width?: number; height?: number; + includeDecorations?: boolean; } export class SaveImageTool extends Tool { public static override toolId = "SaveImage"; public static override get minArgs() { return 0; } - public static override get maxArgs() { return 3; } + public static override get maxArgs() { return 4; } public override async run(opts?: SaveImageOptions): Promise { const vp = IModelApp.viewManager.selectedView; @@ -40,7 +41,14 @@ export class SaveImageTool extends Tool { return true; } - const url = imageBufferToPngDataUrl(buffer, false); + let url; + if (opts?.includeDecorations) { + const canvas = vp.readImageToCanvas({includeCanvasDecorations: opts.includeDecorations}); + url = canvas.toDataURL(); + } else { + url = imageBufferToPngDataUrl(buffer, false); + } + if (!url) { alert("Failed to produce PNG"); return true; @@ -86,52 +94,8 @@ export class SaveImageTool extends Tool { opts.height = args.getInteger("h"); } - return this.run(opts); - } -} - -export class SaveImageWithCanvasDecorationsTool extends Tool { - public static override toolId = "SaveImageWithCanvasDecorations"; - public static override get minArgs() { return 0; } - public static override get maxArgs() { return 1; } - - public override async run(showDecorations: boolean): Promise { - const vp = IModelApp.viewManager.selectedView; - if (!vp) - return false; - - const width = vp.viewRect.width; - const height = vp.viewRect.height; - - await vp.waitForSceneCompletion(); - const buffer = vp.readImageBuffer({ size: new Point2d(width, height) }); - if (!buffer) { - alert("Failed to read image"); - return true; - } - - const options: ReadImageToCanvasOptions = { - includeCanvasDecorations: showDecorations, - } - - const canvas = vp.readImageToCanvas(options); - const url = canvas.toDataURL(); - - if (!url) { - alert("Failed to produce PNG"); - return true; - } - - openImageDataUrlInNewWindow(url, "Saved View"); - return true; - } + opts.includeDecorations = args.getBoolean("i"); - public override async parseAndRun(...args: string[]): Promise { - let showDecorations; - if (!args[0]) - showDecorations = true; - else - showDecorations = !!parseToggle(args[0]); - return this.run(showDecorations); + return this.run(opts); } } From 1231e4199e6cc9b4b814c2e6f5e63d9f8f4dd126 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Wed, 22 Jan 2025 16:02:07 -0700 Subject: [PATCH 11/23] cleanup --- core/frontend/src/render/webgl/Target.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index fb742f4e6bd3..503e63211fc9 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -1189,7 +1189,7 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo // vp.rendersToScreen tells us if the view is being rendered directly to the webgl canvas on screen, which happens in the case of a single viewport. // In that case, we need to combine the 2d canvas with the webgl canvas or we will lose canvas decorations in the copied image. // options.includeCanvasDecorations will be true if the caller wants to include the decorations in the copied image. - if (vp && vp.rendersToScreen && options?.includeCanvasDecorations) { + if (vp?.rendersToScreen && options?.includeCanvasDecorations) { const twoDCanvas = vp.canvas; const ctx = retCanvas.getContext("2d")!; ctx.drawImage(twoDCanvas, 0, 0); From ae3eac8cf35defeeb44432b1dd68188dfafe82c1 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Thu, 23 Jan 2025 07:39:54 -0700 Subject: [PATCH 12/23] NextVersion --- docs/changehistory/NextVersion.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index 5986f73ea4fa..ff0ba840f72f 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -34,6 +34,8 @@ Table of contents: - [Change to pullMerge](#change-to-pullmerge) - [No pending/local changes](#no-pendinglocal-changes) - [With pending/local changes](#with-pendinglocal-changes) + - [Graphics](#graphics) + - [Read Image To Canvas](#read-image-to-canvas) ## Selection set @@ -286,3 +288,9 @@ This method offers several advantages: 4. In the future, this method will be essential for lock-less editing as it enables applications to merge changes with domain intelligence. For more information read [Pull merge & conflict resolution](../learning/backend/PullMerge.md) + +## Graphics + +### Read Image To Canvas + +Previously, when using [Viewport.readImageToCanvas]($core-frontend) with a single open viewport, canvas decorations were not included in the saved image. Sometimes this behavior was useful, so the [ReadImageToCanvasOptions]($core-frontend) interface was created to allow the option to choose whether or not canvas decorations are included in the saved image. Now, if [ReadImageToCanvasOptions.includeCanvasDecorations]($core-frontend) is true, canvas decorations will be included in the saved image. If undefined or false, previous behavior will persist and canvas decorations will not be included. All existing calls to [Viewport.readImageToCanvas]($core-frontend) will be unaffected by this change as the inclusion of[ReadImageToCanvasOptions]($core-frontend) is optional, and when they are undefined, previous behavior will persist. From 802c2156fe700ed693786f33527e8cee209842c4 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Thu, 23 Jan 2025 07:44:02 -0700 Subject: [PATCH 13/23] next version cleanup --- docs/changehistory/NextVersion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index ff0ba840f72f..ed7bd128cd90 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -293,4 +293,4 @@ For more information read [Pull merge & conflict resolution](../learning/backend ### Read Image To Canvas -Previously, when using [Viewport.readImageToCanvas]($core-frontend) with a single open viewport, canvas decorations were not included in the saved image. Sometimes this behavior was useful, so the [ReadImageToCanvasOptions]($core-frontend) interface was created to allow the option to choose whether or not canvas decorations are included in the saved image. Now, if [ReadImageToCanvasOptions.includeCanvasDecorations]($core-frontend) is true, canvas decorations will be included in the saved image. If undefined or false, previous behavior will persist and canvas decorations will not be included. All existing calls to [Viewport.readImageToCanvas]($core-frontend) will be unaffected by this change as the inclusion of[ReadImageToCanvasOptions]($core-frontend) is optional, and when they are undefined, previous behavior will persist. +Previously, when using [Viewport.readImageToCanvas]($core-frontend) with a single open viewport, canvas decorations were not included in the saved image. Sometimes this behavior was useful, so the [ReadImageToCanvasOptions]($core-frontend) interface was created to allow the option to choose whether or not canvas decorations are included in the saved image. Now, if [ReadImageToCanvasOptions.includeCanvasDecorations]($core-frontend) is true, canvas decorations will be included in the saved image. If undefined or false, previous behavior will persist and canvas decorations will not be included. All existing calls to [Viewport.readImageToCanvas]($core-frontend) will be unaffected by this change as the inclusion of [ReadImageToCanvasOptions]($core-frontend) is optional, and when they are undefined, previous behavior will persist. From 6b71824344b6f237aef5a06cddf80c7a6634347d Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Fri, 24 Jan 2025 12:36:49 -0700 Subject: [PATCH 14/23] test progress, includeCanvasDecorations -> omitCanvasDecorations, consistent behavior for multi vps --- core/frontend/src/Viewport.ts | 6 +- core/frontend/src/render/webgl/Target.ts | 83 ++++++++++++------ core/frontend/src/test/Viewport.test.ts | 103 ++++++++++++++++++++++- 3 files changed, 162 insertions(+), 30 deletions(-) diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index 6ccd943e5370..86c3a94fd8c2 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -266,10 +266,8 @@ export interface ReadPixelsArgs { * @public */ export interface ReadImageToCanvasOptions { - /** If defined, canvas decorations will be included in the saved image. - * @note This only affects single viewport applications. For multi-viewport applications, the canvas decorations are always included. - */ - includeCanvasDecorations?: boolean; + /** If true, canvas decorations will be included in the saved image. */ + omitCanvasDecorations: boolean; } /** A Viewport renders the contents of one or more [GeometricModel]($backend)s onto an `HTMLCanvasElement`. diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index bdd39d2bee3f..9d96cce8ac8c 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -15,7 +15,7 @@ import { ViewRect } from "../../common/ViewRect"; import { canvasToImageBuffer, canvasToResizedCanvasWithBars, imageBufferToCanvas } from "../../common/ImageUtil"; import { HiliteSet, ModelSubCategoryHiliteMode } from "../../SelectionSet"; import { SceneContext } from "../../ViewContext"; -import { ReadImageBufferArgs, ReadImageToCanvasOptions, Viewport } from "../../Viewport"; +import { ReadImageBufferArgs, ReadImageToCanvasOptions, ScreenViewport, Viewport } from "../../Viewport"; import { IModelConnection } from "../../IModelConnection"; import { CanvasDecoration } from "../CanvasDecoration"; import { Decorations } from "../Decorations"; @@ -158,6 +158,8 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo public displayNormalMaps = true; public freezeRealityTiles = false; + + protected _omitCanvasDecorations? = false; public get shadowFrustum(): Frustum | undefined { const map = this.solarShadowMap; return map.isEnabled && map.isReady ? map.frustum : undefined; @@ -1178,25 +1180,54 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo return image; } - public copyImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { + private getCanvasFromImageBuffer(): HTMLCanvasElement { const image = this.readImageBuffer(); const canvas = undefined !== image ? imageBufferToCanvas(image, false) : undefined; const retCanvas = undefined !== canvas ? canvas : document.createElement("canvas"); - const pixelRatio = this.devicePixelRatio; - retCanvas.getContext("2d")!.scale(pixelRatio, pixelRatio); - - const vp = IModelApp.viewManager.selectedView; - // vp.rendersToScreen tells us if the view is being rendered directly to the webgl canvas on screen, which happens in the case of a single viewport. - // In that case, we need to combine the 2d canvas with the webgl canvas or we will lose canvas decorations in the copied image. - // options.includeCanvasDecorations will be true if the caller wants to include the decorations in the copied image. - if (vp?.rendersToScreen && options?.includeCanvasDecorations) { - const twoDCanvas = vp.canvas; - const ctx = retCanvas.getContext("2d")!; - ctx.drawImage(twoDCanvas, 0, 0); - } + return retCanvas; + } + private combineCanvasWithViewportOverlayCanvas(baseCanvas: HTMLCanvasElement, vp: ScreenViewport): HTMLCanvasElement { + const overlayCanvas = vp.canvas; + const ctx = baseCanvas.getContext("2d")!; + ctx.drawImage(overlayCanvas, 0, 0); + return baseCanvas; + } - return retCanvas; + private toggleOmitCanvasDecorations(hideDecorations: boolean, vp: ScreenViewport) { + this._omitCanvasDecorations = hideDecorations; + vp.invalidateScene(); + } + + public copyImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { + + /** There are four cases to consider: + * 1. Single viewport, with decorations: Must combine the webgl canvas with the 2d canvas, as canvas decorations are overlayed on top of the webgl canvas using the 2d canvas. + * 2. Single viewport, no decorations: Basic Copy image from image buffer. Decorations will be automaticaly excluded as they are overlayed on the separate 2d canvas. + * 3. Multiple viewports, with decorations: Basic copy image from image buffer. In this case, decorations will be included as they are not overlayed on a separate canvas + * when multiple viewports are present. + * 4. Multiple viewports, no decorations: Turn off decorations, copy image, then turn decorations back on. + */ + + const vp = IModelApp.viewManager.selectedView; + if (!vp) + return document.createElement("canvas"); + + // case 1 + if (vp.rendersToScreen && !options?.omitCanvasDecorations) { + return this.combineCanvasWithViewportOverlayCanvas(this.getCanvasFromImageBuffer(), vp); + } + + // case 4 + if (!vp.rendersToScreen && options?.omitCanvasDecorations) { + this.toggleOmitCanvasDecorations(true, vp); + const retCanvas = this.getCanvasFromImageBuffer(); + this.toggleOmitCanvasDecorations(false, vp); + return retCanvas; + } + + // cases 2 and 3 + return this.getCanvasFromImageBuffer(); } public drawPlanarClassifiers() { @@ -1481,16 +1512,18 @@ export class OnScreenTarget extends Target { this._2dCanvas.needsClear = false; } - const canvasDecs = this.graphics.canvasDecorations; - if (canvasDecs) { - for (const overlay of canvasDecs) { - ctx.save(); - if (overlay.position) - ctx.translate(overlay.position.x, overlay.position.y); - - overlay.drawDecoration(ctx); - this._2dCanvas.needsClear = true; - ctx.restore(); + if (!this._omitCanvasDecorations) { + const canvasDecs = this.graphics.canvasDecorations; + if (canvasDecs) { + for (const overlay of canvasDecs) { + ctx.save(); + if (overlay.position) + ctx.translate(overlay.position.x, overlay.position.y); + + overlay.drawDecoration(ctx); + this._2dCanvas.needsClear = true; + ctx.restore(); + } } } } diff --git a/core/frontend/src/test/Viewport.test.ts b/core/frontend/src/test/Viewport.test.ts index b3a707add146..8117087f88fa 100644 --- a/core/frontend/src/test/Viewport.test.ts +++ b/core/frontend/src/test/Viewport.test.ts @@ -10,7 +10,7 @@ import { AnalysisStyle, ColorDef, EmptyLocalization, Feature, ImageBuffer, ImageBufferFormat, ImageMapLayerSettings, } from "@itwin/core-common"; import { ViewRect } from "../common/ViewRect"; -import { OffScreenViewport, ScreenViewport, Viewport } from "../Viewport"; +import { OffScreenViewport, ReadImageToCanvasOptions, ScreenViewport, Viewport } from "../Viewport"; import { DisplayStyle3dState } from "../DisplayStyleState"; import { SpatialViewState } from "../SpatialViewState"; import { IModelApp } from "../IModelApp"; @@ -21,6 +21,7 @@ import { Pixel } from "../render/Pixel"; import { GraphicType } from "../common/render/GraphicType"; import { RenderGraphic } from "../render/RenderGraphic"; import { Decorator } from "../ViewManager"; +import { CanvasDecoration } from "../core-frontend"; describe("Viewport", () => { beforeAll(async () => IModelApp.startup({ localization: new EmptyLocalization() })); @@ -716,4 +717,104 @@ describe("Viewport", () => { }); }); }); + + describe("Read Image To Canvas", () => { + + class PixelCanvasDecoration implements CanvasDecoration { + public drawDecoration(ctx: CanvasRenderingContext2D) { + ctx.fillStyle = "red"; + ctx.fillRect(0,0,1,1); + } + } + + class PixelCanvasDecorator implements Decorator { + public decorate(context: DecorateContext) { + + const builder = context.createGraphicBuilder(GraphicType.WorldDecoration, undefined); + builder.addLineString2d([new Point2d(0,0), new Point2d(1,1)],0); + context.addDecorationFromBuilder(builder); + context.addCanvasDecoration(new PixelCanvasDecoration()); + } + } + + function createViewport(): ScreenViewport { + const state = SpatialViewState.createBlank(createBlankConnection(), { x: 0, y: 0, z: 0 }, { x: 1, y: 1, z: 1 }) + const parentDiv = document.createElement("div"); + parentDiv.setAttribute("height", "100px"); + parentDiv.setAttribute("width", "100px"); + parentDiv.style.height = parentDiv.style.width = "100px"; + document.body.appendChild(parentDiv); + return ScreenViewport.create(parentDiv, state); + } + + const activeDecorators: Decorator[] = []; + function addDecorator(dec: Decorator) { + IModelApp.viewManager.dropDecorator(dec); + IModelApp.viewManager.addDecorator(dec); + activeDecorators.push(dec); + } + + function getPixelRgb(pixel: Uint8ClampedArray): [number, number, number] { + return [pixel[3], pixel[2], pixel[1]]; + } + + afterEach(() => { + for (const dec of activeDecorators) { + IModelApp.viewManager.dropDecorator(dec); + } + + activeDecorators.length = 0; + }); + + it("should not include canvas decorations if omitCanvasDecorations is true", () => { + const vp = createViewport(); + IModelApp.viewManager.addViewport(vp); + + addDecorator(new PixelCanvasDecorator()); + vp.renderFrame(); + + const readImageOptions: ReadImageToCanvasOptions = { + omitCanvasDecorations: true, + }; + + const canvas = vp.readImageToCanvas(readImageOptions); + const ctx = canvas.getContext("2d"); + const pixel = ctx!.getImageData(0, 0, 1, 1).data; + const rgb = getPixelRgb(pixel); + expect(rgb).toEqual([0,0,0]); + + + + IModelApp.viewManager.dropViewport(vp); + }); + + it("should include canvas decorations if omitCanvasDecorations is false", () => { + const vp = createViewport(); + IModelApp.viewManager.addViewport(vp); + + addDecorator(new PixelCanvasDecorator()); + vp.renderFrame(); + + const readImageOptions: ReadImageToCanvasOptions = { + omitCanvasDecorations: false, + }; + + const canvas = vp.readImageToCanvas(readImageOptions); + const ctx = canvas.getContext("2d"); + const pixel = ctx!.getImageData(0, 0, 1, 1).data; + const rgb = getPixelRgb(pixel); + expect(rgb).toEqual([255,0,0]); + + IModelApp.viewManager.dropViewport(vp); + }); + + /** + * Other changes to the above tests which produced the same bug-free behavior (not what we want): + * Use openBlankViewport to get a blank viewport instead of creating a screen viewport directly + * Set vp.rendersToScreen to true before calling vp.renderFrame (for both vp types) + * Use vp.target.SetRenderToScreen to do the same thing + * Making sure all Viewports are removed before adding a new one at the beginning of the test + * Fully shutting down IModel App and then restarting it at the beginning of the test + */ + }); }); From c5255a677bdd1b2af5c3a38462c2b66913cbbd26 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 28 Jan 2025 07:42:29 -0700 Subject: [PATCH 15/23] Fix tests, finish readimagetocanvas implementations, update docs/readme/nextversion --- common/api/core-frontend.api.md | 6 +- core/frontend/src/Viewport.ts | 21 +++- core/frontend/src/render/webgl/Target.ts | 79 ++++---------- core/frontend/src/test/Viewport.test.ts | 102 +++++++++++++----- docs/changehistory/NextVersion.md | 4 +- test-apps/display-test-app/README.md | 2 +- .../src/frontend/SaveImageTool.ts | 15 +-- 7 files changed, 131 insertions(+), 98 deletions(-) diff --git a/common/api/core-frontend.api.md b/common/api/core-frontend.api.md index ded08b838def..756347724ace 100644 --- a/common/api/core-frontend.api.md +++ b/common/api/core-frontend.api.md @@ -9073,7 +9073,7 @@ export interface ReadImageBufferArgs { // @public export interface ReadImageToCanvasOptions { - includeCanvasDecorations?: boolean; + omitCanvasDecorations: boolean; } // @internal (undocumented) @@ -14764,7 +14764,9 @@ export abstract class Viewport implements Disposable, TileUser { // @deprecated readImage(rect?: ViewRect, targetSize?: Point2d, flipVertically?: boolean): ImageBuffer | undefined; readImageBuffer(args?: ReadImageBufferArgs): ImageBuffer | undefined; - readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement; + // @deprecated + readImageToCanvas(): HTMLCanvasElement; + readImageToCanvas(options: ReadImageToCanvasOptions): HTMLCanvasElement; readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable?: boolean): void; readPixels(args: ReadPixelsArgs): void; // @internal diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index 86c3a94fd8c2..b77f997e31b9 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -2714,12 +2714,29 @@ export abstract class Viewport implements Disposable, TileUser { } /** Reads the current image from this viewport into an HTMLCanvasElement with a Canvas2dRenderingContext such that additional 2d graphics can be drawn onto it. - * @see [[readImageBuffer]] to obtain the image as an array of RGBA pixels. - */ + * When using this overload, the returned image will not include canvas decorations if only one viewport is active. + * If multiple viewports are active, the returned image will always include canvas decorations. + * @deprecated in 5.0 Use the overload accepting a ReadImageToCanvasOptions. + */ + public readImageToCanvas(): HTMLCanvasElement; + + /** Reads the current image from this viewport into an HTMLCanvasElement with a Canvas2dRenderingContext such that additional 2d graphics can be drawn onto it. + * This overload allows for specifying whether canvas decorations will be omitted from the returned image by passing in [[ReadImageToCanvasOptions]]. + * The canvas decorations will be consistently omitted or included regardless of the number of active viewports. + * @param options Options for reading the image to the canvas. + */ + // eslint-disable-next-line @typescript-eslint/unified-signatures + public readImageToCanvas(options: ReadImageToCanvasOptions): HTMLCanvasElement; + + /** Reads the current image from this viewport into an HTMLCanvasElement with a Canvas2dRenderingContext such that additional 2d graphics can be drawn onto it. + * @see [[readImageBuffer]] to obtain the image as an array of RGBA pixels. + * @internal + */ public readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { return this.target.readImageToCanvas(options); } + /** Used internally by `waitForSceneCompletion`. * @internal */ diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index 9d96cce8ac8c..9717de0bdfbf 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -15,7 +15,7 @@ import { ViewRect } from "../../common/ViewRect"; import { canvasToImageBuffer, canvasToResizedCanvasWithBars, imageBufferToCanvas } from "../../common/ImageUtil"; import { HiliteSet, ModelSubCategoryHiliteMode } from "../../SelectionSet"; import { SceneContext } from "../../ViewContext"; -import { ReadImageBufferArgs, ReadImageToCanvasOptions, ScreenViewport, Viewport } from "../../Viewport"; +import { ReadImageBufferArgs, ReadImageToCanvasOptions, Viewport } from "../../Viewport"; import { IModelConnection } from "../../IModelConnection"; import { CanvasDecoration } from "../CanvasDecoration"; import { Decorations } from "../Decorations"; @@ -159,7 +159,6 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo public freezeRealityTiles = false; - protected _omitCanvasDecorations? = false; public get shadowFrustum(): Frustum | undefined { const map = this.solarShadowMap; return map.isEnabled && map.isReady ? map.frustum : undefined; @@ -1180,54 +1179,25 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo return image; } - private getCanvasFromImageBuffer(): HTMLCanvasElement { - const image = this.readImageBuffer(); - const canvas = undefined !== image ? imageBufferToCanvas(image, false) : undefined; - const retCanvas = undefined !== canvas ? canvas : document.createElement("canvas"); - return retCanvas; - } - - private combineCanvasWithViewportOverlayCanvas(baseCanvas: HTMLCanvasElement, vp: ScreenViewport): HTMLCanvasElement { - const overlayCanvas = vp.canvas; - const ctx = baseCanvas.getContext("2d")!; - ctx.drawImage(overlayCanvas, 0, 0); - return baseCanvas; - } - - private toggleOmitCanvasDecorations(hideDecorations: boolean, vp: ScreenViewport) { - this._omitCanvasDecorations = hideDecorations; - vp.invalidateScene(); - } - public copyImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { - - /** There are four cases to consider: - * 1. Single viewport, with decorations: Must combine the webgl canvas with the 2d canvas, as canvas decorations are overlayed on top of the webgl canvas using the 2d canvas. - * 2. Single viewport, no decorations: Basic Copy image from image buffer. Decorations will be automaticaly excluded as they are overlayed on the separate 2d canvas. - * 3. Multiple viewports, with decorations: Basic copy image from image buffer. In this case, decorations will be included as they are not overlayed on a separate canvas - * when multiple viewports are present. - * 4. Multiple viewports, no decorations: Turn off decorations, copy image, then turn decorations back on. - */ - const vp = IModelApp.viewManager.selectedView; if (!vp) return document.createElement("canvas"); - // case 1 - if (vp.rendersToScreen && !options?.omitCanvasDecorations) { - return this.combineCanvasWithViewportOverlayCanvas(this.getCanvasFromImageBuffer(), vp); - } + const image = this.readImageBuffer(); + const canvas = undefined !== image ? imageBufferToCanvas(image, false) : undefined; + const retCanvas = undefined !== canvas ? canvas : document.createElement("canvas"); - // case 4 - if (!vp.rendersToScreen && options?.omitCanvasDecorations) { - this.toggleOmitCanvasDecorations(true, vp); - const retCanvas = this.getCanvasFromImageBuffer(); - this.toggleOmitCanvasDecorations(false, vp); - return retCanvas; + if ((options !== undefined) && (!options.omitCanvasDecorations)) { + const overlayCanvas = vp.canvas; + const ctx = retCanvas.getContext("2d")!; + ctx.drawImage(overlayCanvas, 0, 0); } - // cases 2 and 3 - return this.getCanvasFromImageBuffer(); + const pixelRatio = this.devicePixelRatio; + retCanvas.getContext("2d")!.scale(pixelRatio, pixelRatio); + + return retCanvas; } public drawPlanarClassifiers() { @@ -1512,18 +1482,16 @@ export class OnScreenTarget extends Target { this._2dCanvas.needsClear = false; } - if (!this._omitCanvasDecorations) { - const canvasDecs = this.graphics.canvasDecorations; - if (canvasDecs) { - for (const overlay of canvasDecs) { - ctx.save(); - if (overlay.position) - ctx.translate(overlay.position.x, overlay.position.y); - - overlay.drawDecoration(ctx); - this._2dCanvas.needsClear = true; - ctx.restore(); - } + const canvasDecs = this.graphics.canvasDecorations; + if (canvasDecs) { + for (const overlay of canvasDecs) { + ctx.save(); + if (overlay.position) + ctx.translate(overlay.position.x, overlay.position.y); + + overlay.drawDecoration(ctx); + this._2dCanvas.needsClear = true; + ctx.restore(); } } } @@ -1559,10 +1527,9 @@ export class OnScreenTarget extends Target { } public override readImageToCanvas(options?: ReadImageToCanvasOptions): HTMLCanvasElement { - return this._usingWebGLCanvas ? this.copyImageToCanvas(options) : this._2dCanvas.canvas; + return this._usingWebGLCanvas || options?.omitCanvasDecorations ? this.copyImageToCanvas(options) : this._2dCanvas.canvas; } } - /** @internal */ export class OffScreenTarget extends Target { public constructor(rect: ViewRect) { diff --git a/core/frontend/src/test/Viewport.test.ts b/core/frontend/src/test/Viewport.test.ts index 8117087f88fa..cf0a2fdc4de0 100644 --- a/core/frontend/src/test/Viewport.test.ts +++ b/core/frontend/src/test/Viewport.test.ts @@ -731,7 +731,6 @@ describe("Viewport", () => { public decorate(context: DecorateContext) { const builder = context.createGraphicBuilder(GraphicType.WorldDecoration, undefined); - builder.addLineString2d([new Point2d(0,0), new Point2d(1,1)],0); context.addDecorationFromBuilder(builder); context.addCanvasDecoration(new PixelCanvasDecoration()); } @@ -740,24 +739,19 @@ describe("Viewport", () => { function createViewport(): ScreenViewport { const state = SpatialViewState.createBlank(createBlankConnection(), { x: 0, y: 0, z: 0 }, { x: 1, y: 1, z: 1 }) const parentDiv = document.createElement("div"); - parentDiv.setAttribute("height", "100px"); - parentDiv.setAttribute("width", "100px"); - parentDiv.style.height = parentDiv.style.width = "100px"; + parentDiv.setAttribute("height", "1px"); + parentDiv.setAttribute("width", "1px"); + parentDiv.style.height = parentDiv.style.width = "1px"; document.body.appendChild(parentDiv); return ScreenViewport.create(parentDiv, state); } const activeDecorators: Decorator[] = []; function addDecorator(dec: Decorator) { - IModelApp.viewManager.dropDecorator(dec); IModelApp.viewManager.addDecorator(dec); activeDecorators.push(dec); } - function getPixelRgb(pixel: Uint8ClampedArray): [number, number, number] { - return [pixel[3], pixel[2], pixel[1]]; - } - afterEach(() => { for (const dec of activeDecorators) { IModelApp.viewManager.dropDecorator(dec); @@ -766,10 +760,9 @@ describe("Viewport", () => { activeDecorators.length = 0; }); - it("should not include canvas decorations if omitCanvasDecorations is true", () => { + it("should not include canvas decorations if omitCanvasDecorations is true or ReadImageToCanvasOptions is undefined", () => { const vp = createViewport(); IModelApp.viewManager.addViewport(vp); - addDecorator(new PixelCanvasDecorator()); vp.renderFrame(); @@ -777,21 +770,26 @@ describe("Viewport", () => { omitCanvasDecorations: true, }; - const canvas = vp.readImageToCanvas(readImageOptions); - const ctx = canvas.getContext("2d"); - const pixel = ctx!.getImageData(0, 0, 1, 1).data; - const rgb = getPixelRgb(pixel); + let canvas = vp.readImageToCanvas(readImageOptions); + let ctx = canvas.getContext("2d"); + let pixel = ctx!.getImageData(0, 0, 1, 1).data; + let rgb = [pixel[0], pixel[1], pixel[2]]; expect(rgb).toEqual([0,0,0]); + canvas = vp.readImageToCanvas(); + ctx = canvas.getContext("2d"); + pixel = ctx!.getImageData(0, 0, 1, 1).data; + rgb = [pixel[0], pixel[1], pixel[2]]; + expect(rgb).toEqual([0,0,0]); IModelApp.viewManager.dropViewport(vp); - }); + }); + it("should include canvas decorations if omitCanvasDecorations is false", () => { const vp = createViewport(); IModelApp.viewManager.addViewport(vp); - addDecorator(new PixelCanvasDecorator()); vp.renderFrame(); @@ -802,19 +800,71 @@ describe("Viewport", () => { const canvas = vp.readImageToCanvas(readImageOptions); const ctx = canvas.getContext("2d"); const pixel = ctx!.getImageData(0, 0, 1, 1).data; - const rgb = getPixelRgb(pixel); + const rgb = [pixel[0], pixel[1], pixel[2]]; expect(rgb).toEqual([255,0,0]); IModelApp.viewManager.dropViewport(vp); }); - /** - * Other changes to the above tests which produced the same bug-free behavior (not what we want): - * Use openBlankViewport to get a blank viewport instead of creating a screen viewport directly - * Set vp.rendersToScreen to true before calling vp.renderFrame (for both vp types) - * Use vp.target.SetRenderToScreen to do the same thing - * Making sure all Viewports are removed before adding a new one at the beginning of the test - * Fully shutting down IModel App and then restarting it at the beginning of the test - */ + it("should not include canvas decorations if omitCanvasDecorations is true with multiple viewports", () => { + const vp = createViewport(); + const vp2 = createViewport(); + IModelApp.viewManager.addViewport(vp); + IModelApp.viewManager.addViewport(vp2); + + addDecorator(new PixelCanvasDecorator()); + vp.renderFrame(); + vp2.renderFrame(); + + const readImageOptions: ReadImageToCanvasOptions = { + omitCanvasDecorations: true, + }; + + let canvas = vp.readImageToCanvas(readImageOptions); + let ctx = canvas.getContext("2d"); + let pixel = ctx!.getImageData(0, 0, 1, 1).data; + let rgb = [pixel[0], pixel[1], pixel[2]]; + expect(rgb).toEqual([0,0,0]); + + canvas = vp2.readImageToCanvas(readImageOptions); + ctx = canvas.getContext("2d"); + pixel = ctx!.getImageData(0, 0, 1, 1).data; + rgb = [pixel[0], pixel[1], pixel[2]]; + expect(rgb).toEqual([0,0,0]); + + IModelApp.viewManager.dropViewport(vp); + IModelApp.viewManager.dropViewport(vp2); + }); + + + it("should include canvas decorations if omitCanvasDecorations is false or ReadImageToCanvasOptions is undefined with multiple viewports", () => { + const vp = createViewport(); + const vp2 = createViewport(); + IModelApp.viewManager.addViewport(vp); + IModelApp.viewManager.addViewport(vp2); + + addDecorator(new PixelCanvasDecorator()); + vp.renderFrame(); + vp2.renderFrame(); + + const readImageOptions: ReadImageToCanvasOptions = { + omitCanvasDecorations: false, + }; + + let canvas = vp.readImageToCanvas(readImageOptions); + let ctx = canvas.getContext("2d"); + let pixel = ctx!.getImageData(0, 0, 1, 1).data; + let rgb = [pixel[0], pixel[1], pixel[2]]; + expect(rgb).toEqual([255,0,0]); + + canvas = vp2.readImageToCanvas(); + ctx = canvas.getContext("2d"); + pixel = ctx!.getImageData(0, 0, 1, 1).data; + rgb = [pixel[0], pixel[1], pixel[2]]; + expect(rgb).toEqual([255,0,0]); + + IModelApp.viewManager.dropViewport(vp); + IModelApp.viewManager.dropViewport(vp2); + }); }); }); diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index ed7bd128cd90..7dfded9e3673 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -293,4 +293,6 @@ For more information read [Pull merge & conflict resolution](../learning/backend ### Read Image To Canvas -Previously, when using [Viewport.readImageToCanvas]($core-frontend) with a single open viewport, canvas decorations were not included in the saved image. Sometimes this behavior was useful, so the [ReadImageToCanvasOptions]($core-frontend) interface was created to allow the option to choose whether or not canvas decorations are included in the saved image. Now, if [ReadImageToCanvasOptions.includeCanvasDecorations]($core-frontend) is true, canvas decorations will be included in the saved image. If undefined or false, previous behavior will persist and canvas decorations will not be included. All existing calls to [Viewport.readImageToCanvas]($core-frontend) will be unaffected by this change as the inclusion of [ReadImageToCanvasOptions]($core-frontend) is optional, and when they are undefined, previous behavior will persist. +Previously, when using [Viewport.readImageToCanvas]($core-frontend) with a single open viewport, canvas decorations were not included in the saved image. Sometimes this behavior was useful, so an overload to [Viewport.readImageToCanvas]($core-frontend) using the new [ReadImageToCanvasOptions]($core-frontend) interface. This now allows the option to choose whether or not canvas decorations are omitted in the saved image. Now, if [ReadImageToCanvasOptions.omitCanvasDecorations]($core-frontend) is true, canvas decorations will be omitted in the saved image. + +If [ReadImageToCanvasOptions]($core-frontend) are undefined in the call to [Viewport.readImageToCanvas]($core-frontend), previous behavior will persist and canvas decorations will not be included. This means canvas decorations will not be included when there is a single open viewport, but will be included when there are multiple open viewports. All existing calls to [Viewport.readImageToCanvas]($core-frontend) will be unaffected by this change as the inclusion of [ReadImageToCanvasOptions]($core-frontend) is optional, and when they are undefined, previous behavior will persist. diff --git a/test-apps/display-test-app/README.md b/test-apps/display-test-app/README.md index 28fbfd4763bc..cdaa3093f1e1 100644 --- a/test-apps/display-test-app/README.md +++ b/test-apps/display-test-app/README.md @@ -251,7 +251,7 @@ display-test-app has access to all key-ins defined in the `@itwin/core-frontend` * `h=height` - the desired height of the image in pixels. e.g. `h=480`. * `d=dimensions` - the desired width and height of the image in pixels. The image will be square. e.g. `d=768`. * `c=0|1` - if `1`, instead of opening a new window to display the image, the image will be copied to the clipboard. NOTE: this probably doesn't work in Firefox. - * `i=0|1` - if `1`, canvas decorations will be included in the saved image. By default they are not. NOTE: This is only true if a single viewport is present. If multiple viewports are present, canvas decorations will always be included in the saved image. + * `o=0|1` - if `1`, canvas decorations will be omitted in the saved image. By default, they are included. * `dta record fps` *numFrames* - record average frames-per-second over the specified number of frames (default: 150) and output to status bar. * `dta zoom selected` - zoom the selected viewport to the elements in the selection set. Optional arguments specify the margin or padding percent as follows: * `l=` `r=` `t=` `b=` followed by a number indicating the left, right, top, and/or bottom padding or margin percent. diff --git a/test-apps/display-test-app/src/frontend/SaveImageTool.ts b/test-apps/display-test-app/src/frontend/SaveImageTool.ts index d63f22fbba10..099191083a4b 100644 --- a/test-apps/display-test-app/src/frontend/SaveImageTool.ts +++ b/test-apps/display-test-app/src/frontend/SaveImageTool.ts @@ -5,14 +5,14 @@ import { ProcessDetector } from "@itwin/core-bentley"; import { Point2d } from "@itwin/core-geometry"; -import { imageBufferToPngDataUrl, IModelApp, openImageDataUrlInNewWindow, Tool } from "@itwin/core-frontend"; +import { IModelApp, openImageDataUrlInNewWindow, Tool } from "@itwin/core-frontend"; import { parseArgs } from "@itwin/frontend-devtools"; interface SaveImageOptions { copyToClipboard?: boolean; width?: number; height?: number; - includeDecorations?: boolean; + omitCanvasDecorations?: boolean; } export class SaveImageTool extends Tool { @@ -41,13 +41,8 @@ export class SaveImageTool extends Tool { return true; } - let url; - if (opts?.includeDecorations) { - const canvas = vp.readImageToCanvas({includeCanvasDecorations: opts.includeDecorations}); - url = canvas.toDataURL(); - } else { - url = imageBufferToPngDataUrl(buffer, false); - } + const canvas = vp.readImageToCanvas({omitCanvasDecorations: !!opts?.omitCanvasDecorations}); + const url = canvas.toDataURL(); if (!url) { alert("Failed to produce PNG"); @@ -94,7 +89,7 @@ export class SaveImageTool extends Tool { opts.height = args.getInteger("h"); } - opts.includeDecorations = args.getBoolean("i"); + opts.omitCanvasDecorations = args.getBoolean("o"); return this.run(opts); } From f2aadcdd16470fe101ca483efcbe83257ab8f7f8 Mon Sep 17 00:00:00 2001 From: andremig-bentley <101671244+andremig-bentley@users.noreply.github.com> Date: Tue, 28 Jan 2025 08:04:58 -0700 Subject: [PATCH 16/23] Update docs/changehistory/NextVersion.md Co-authored-by: Ben Polinsky <78756012+ben-polinsky@users.noreply.github.com> --- docs/changehistory/NextVersion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index 300401c515a2..2a84548b02d3 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -329,6 +329,6 @@ For more information read [Pull merge & conflict resolution](../learning/backend ### Read Image To Canvas -Previously, when using [Viewport.readImageToCanvas]($core-frontend) with a single open viewport, canvas decorations were not included in the saved image. Sometimes this behavior was useful, so an overload to [Viewport.readImageToCanvas]($core-frontend) using the new [ReadImageToCanvasOptions]($core-frontend) interface. This now allows the option to choose whether or not canvas decorations are omitted in the saved image. Now, if [ReadImageToCanvasOptions.omitCanvasDecorations]($core-frontend) is true, canvas decorations will be omitted in the saved image. +Previously, when using [Viewport.readImageToCanvas]($core-frontend) with a single open viewport, canvas decorations were not included in the saved image. Sometimes this behavior was useful, so an overload to [Viewport.readImageToCanvas]($core-frontend) using the new [ReadImageToCanvasOptions]($core-frontend) interface was created. This now allows the option to choose whether or not canvas decorations are omitted in the saved image: if [ReadImageToCanvasOptions.omitCanvasDecorations]($core-frontend) is true, canvas decorations will be omitted. If [ReadImageToCanvasOptions]($core-frontend) are undefined in the call to [Viewport.readImageToCanvas]($core-frontend), previous behavior will persist and canvas decorations will not be included. This means canvas decorations will not be included when there is a single open viewport, but will be included when there are multiple open viewports. All existing calls to [Viewport.readImageToCanvas]($core-frontend) will be unaffected by this change as the inclusion of [ReadImageToCanvasOptions]($core-frontend) is optional, and when they are undefined, previous behavior will persist. From c4925db3b3eaf09bb7a79d9f1a521423cd665f8a Mon Sep 17 00:00:00 2001 From: andremig-bentley <101671244+andremig-bentley@users.noreply.github.com> Date: Tue, 28 Jan 2025 08:05:08 -0700 Subject: [PATCH 17/23] Update core/frontend/src/Viewport.ts Co-authored-by: Paul Connelly <22944042+pmconne@users.noreply.github.com> --- core/frontend/src/Viewport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index 52bb1ea0f424..f0026eb38e52 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -266,7 +266,7 @@ export interface ReadPixelsArgs { * @public */ export interface ReadImageToCanvasOptions { - /** If true, canvas decorations will be included in the saved image. */ + /** If true, canvas decorations will not be included in the saved image. */ omitCanvasDecorations: boolean; } From 39f693e9a9646c4dc69f615c9af4489d1e654649 Mon Sep 17 00:00:00 2001 From: andremig-bentley <101671244+andremig-bentley@users.noreply.github.com> Date: Tue, 28 Jan 2025 08:08:56 -0700 Subject: [PATCH 18/23] Update core/frontend/src/Viewport.ts Co-authored-by: Paul Connelly <22944042+pmconne@users.noreply.github.com> --- core/frontend/src/Viewport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index f0026eb38e52..a9cefc0d9782 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -267,7 +267,7 @@ export interface ReadPixelsArgs { */ export interface ReadImageToCanvasOptions { /** If true, canvas decorations will not be included in the saved image. */ - omitCanvasDecorations: boolean; + omitCanvasDecorations?: boolean; } /** A Viewport renders the contents of one or more [GeometricModel]($backend)s onto an `HTMLCanvasElement`. From 411752c4ee339cebb91415750ba0b637f1fd29f6 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 28 Jan 2025 08:18:18 -0700 Subject: [PATCH 19/23] add default false tests --- core/frontend/src/test/Viewport.test.ts | 36 +++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/core/frontend/src/test/Viewport.test.ts b/core/frontend/src/test/Viewport.test.ts index 7327ee90cd2d..f9e142e26b73 100644 --- a/core/frontend/src/test/Viewport.test.ts +++ b/core/frontend/src/test/Viewport.test.ts @@ -758,20 +758,30 @@ describe("Viewport", () => { }); - it("should include canvas decorations if omitCanvasDecorations is false", () => { + it("should include canvas decorations if omitCanvasDecorations is false or undefined", () => { const vp = createViewport(); IModelApp.viewManager.addViewport(vp); addDecorator(new PixelCanvasDecorator()); vp.renderFrame(); - const readImageOptions: ReadImageToCanvasOptions = { + let readImageOptions: ReadImageToCanvasOptions = { omitCanvasDecorations: false, }; - const canvas = vp.readImageToCanvas(readImageOptions); - const ctx = canvas.getContext("2d"); - const pixel = ctx!.getImageData(0, 0, 1, 1).data; - const rgb = [pixel[0], pixel[1], pixel[2]]; + let canvas = vp.readImageToCanvas(readImageOptions); + let ctx = canvas.getContext("2d"); + let pixel = ctx!.getImageData(0, 0, 1, 1).data; + let rgb = [pixel[0], pixel[1], pixel[2]]; + expect(rgb).toEqual([255,0,0]); + + readImageOptions = { + omitCanvasDecorations: undefined, + }; + + canvas = vp.readImageToCanvas(readImageOptions); + ctx = canvas.getContext("2d"); + pixel = ctx!.getImageData(0, 0, 1, 1).data; + rgb = [pixel[0], pixel[1], pixel[2]]; expect(rgb).toEqual([255,0,0]); IModelApp.viewManager.dropViewport(vp); @@ -808,7 +818,7 @@ describe("Viewport", () => { }); - it("should include canvas decorations if omitCanvasDecorations is false or ReadImageToCanvasOptions is undefined with multiple viewports", () => { + it("should include canvas decorations if omitCanvasDecorations is false, undefined, or ReadImageToCanvasOptions is undefined with multiple viewports", () => { const vp = createViewport(); const vp2 = createViewport(); IModelApp.viewManager.addViewport(vp); @@ -818,7 +828,7 @@ describe("Viewport", () => { vp.renderFrame(); vp2.renderFrame(); - const readImageOptions: ReadImageToCanvasOptions = { + let readImageOptions: ReadImageToCanvasOptions = { omitCanvasDecorations: false, }; @@ -828,6 +838,16 @@ describe("Viewport", () => { let rgb = [pixel[0], pixel[1], pixel[2]]; expect(rgb).toEqual([255,0,0]); + readImageOptions = { + omitCanvasDecorations: undefined, + }; + + canvas = vp2.readImageToCanvas(readImageOptions); + ctx = canvas.getContext("2d"); + pixel = ctx!.getImageData(0, 0, 1, 1).data; + rgb = [pixel[0], pixel[1], pixel[2]]; + expect(rgb).toEqual([255,0,0]); + canvas = vp2.readImageToCanvas(); ctx = canvas.getContext("2d"); pixel = ctx!.getImageData(0, 0, 1, 1).data; From 8e28410a15069127d864c5cea434ad975f5b7875 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 28 Jan 2025 08:48:30 -0700 Subject: [PATCH 20/23] extract-api --- common/api/core-frontend.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/api/core-frontend.api.md b/common/api/core-frontend.api.md index 8558c5b28a86..a2aab8f88a4a 100644 --- a/common/api/core-frontend.api.md +++ b/common/api/core-frontend.api.md @@ -9054,7 +9054,7 @@ export interface ReadImageBufferArgs { // @public export interface ReadImageToCanvasOptions { - omitCanvasDecorations: boolean; + omitCanvasDecorations?: boolean; } // @internal (undocumented) From 0b98c203845548ffeeba11d51870db2ff0bd5c1f Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 28 Jan 2025 10:21:21 -0700 Subject: [PATCH 21/23] rm deprecated test --- core/frontend/src/test/Viewport.test.ts | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/core/frontend/src/test/Viewport.test.ts b/core/frontend/src/test/Viewport.test.ts index f9e142e26b73..2d220b9fe727 100644 --- a/core/frontend/src/test/Viewport.test.ts +++ b/core/frontend/src/test/Viewport.test.ts @@ -731,7 +731,7 @@ describe("Viewport", () => { activeDecorators.length = 0; }); - it("should not include canvas decorations if omitCanvasDecorations is true or ReadImageToCanvasOptions is undefined", () => { + it("should not include canvas decorations if omitCanvasDecorations is true", () => { const vp = createViewport(); IModelApp.viewManager.addViewport(vp); addDecorator(new PixelCanvasDecorator()); @@ -741,17 +741,10 @@ describe("Viewport", () => { omitCanvasDecorations: true, }; - let canvas = vp.readImageToCanvas(readImageOptions); - let ctx = canvas.getContext("2d"); - let pixel = ctx!.getImageData(0, 0, 1, 1).data; - let rgb = [pixel[0], pixel[1], pixel[2]]; - expect(rgb).toEqual([0,0,0]); - - - canvas = vp.readImageToCanvas(); - ctx = canvas.getContext("2d"); - pixel = ctx!.getImageData(0, 0, 1, 1).data; - rgb = [pixel[0], pixel[1], pixel[2]]; + const canvas = vp.readImageToCanvas(readImageOptions); + const ctx = canvas.getContext("2d"); + const pixel = ctx!.getImageData(0, 0, 1, 1).data; + const rgb = [pixel[0], pixel[1], pixel[2]]; expect(rgb).toEqual([0,0,0]); IModelApp.viewManager.dropViewport(vp); @@ -818,7 +811,7 @@ describe("Viewport", () => { }); - it("should include canvas decorations if omitCanvasDecorations is false, undefined, or ReadImageToCanvasOptions is undefined with multiple viewports", () => { + it("should include canvas decorations if omitCanvasDecorations is false or undefined with multiple viewports", () => { const vp = createViewport(); const vp2 = createViewport(); IModelApp.viewManager.addViewport(vp); @@ -848,12 +841,6 @@ describe("Viewport", () => { rgb = [pixel[0], pixel[1], pixel[2]]; expect(rgb).toEqual([255,0,0]); - canvas = vp2.readImageToCanvas(); - ctx = canvas.getContext("2d"); - pixel = ctx!.getImageData(0, 0, 1, 1).data; - rgb = [pixel[0], pixel[1], pixel[2]]; - expect(rgb).toEqual([255,0,0]); - IModelApp.viewManager.dropViewport(vp); IModelApp.viewManager.dropViewport(vp2); }); From 7948a3500ff48a8e193d995e33989a23b3db1cad Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 28 Jan 2025 11:05:52 -0700 Subject: [PATCH 22/23] rm deprecated calls --- core/markup/src/Markup.ts | 2 +- .../display-performance-test-app/src/frontend/TestRunner.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/markup/src/Markup.ts b/core/markup/src/Markup.ts index 2227e6d99203..800107560417 100644 --- a/core/markup/src/Markup.ts +++ b/core/markup/src/Markup.ts @@ -309,7 +309,7 @@ export class MarkupApp { /** @internal */ protected static async readMarkup(): Promise { const result = this.props.result; - let canvas = this.markup!.vp.readImageToCanvas(); + let canvas = this.markup!.vp.readImageToCanvas({omitCanvasDecorations: false}); let svg, image; try { svg = this.readMarkupSvg(); // read the current svg data for the markup diff --git a/test-apps/display-performance-test-app/src/frontend/TestRunner.ts b/test-apps/display-performance-test-app/src/frontend/TestRunner.ts index 80cff437a84e..7a5dfc7f39a6 100644 --- a/test-apps/display-performance-test-app/src/frontend/TestRunner.ts +++ b/test-apps/display-performance-test-app/src/frontend/TestRunner.ts @@ -361,7 +361,7 @@ export class TestRunner { if (testConfig.testType === "image" || testConfig.testType === "both") { this.updateTestNames(test, undefined, true); - const canvas = vp.readImageToCanvas(); + const canvas = vp.readImageToCanvas({omitCanvasDecorations: false}); await savePng(this.getImageName(test), canvas); if (testConfig.testType === "image") @@ -1028,7 +1028,7 @@ export class TestRunner { private async createReadPixelsImages(test: TestCase, pix: Pixel.Selector, pixStr: string): Promise { const vp = test.viewport; - const canvas = vp.readImageToCanvas(); + const canvas = vp.readImageToCanvas({omitCanvasDecorations: false}); const ctx = canvas.getContext("2d"); if (!ctx) return; From eae7cb43e21e8864d1a2be1b1fb81e77bd75a3b7 Mon Sep 17 00:00:00 2001 From: andremig-bentley Date: Tue, 28 Jan 2025 11:13:14 -0700 Subject: [PATCH 23/23] rush change --- .../andremig-decoration_2025-01-28-18-12.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@itwin/core-markup/andremig-decoration_2025-01-28-18-12.json diff --git a/common/changes/@itwin/core-markup/andremig-decoration_2025-01-28-18-12.json b/common/changes/@itwin/core-markup/andremig-decoration_2025-01-28-18-12.json new file mode 100644 index 000000000000..7a94265251a1 --- /dev/null +++ b/common/changes/@itwin/core-markup/andremig-decoration_2025-01-28-18-12.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-markup", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/core-markup" +} \ No newline at end of file