js-draw 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/bundle.js +2 -2
- package/dist/cjs/SVGLoader.d.ts +1 -1
- package/dist/cjs/SVGLoader.js +11 -2
- package/dist/cjs/image/EditorImage.d.ts +20 -0
- package/dist/cjs/image/EditorImage.js +40 -11
- package/dist/cjs/rendering/Display.d.ts +9 -0
- package/dist/cjs/rendering/Display.js +39 -5
- package/dist/cjs/rendering/caching/CacheRecordManager.js +3 -4
- package/dist/cjs/rendering/caching/RenderingCache.d.ts +1 -0
- package/dist/cjs/rendering/caching/RenderingCache.js +4 -0
- package/dist/cjs/rendering/caching/RenderingCacheNode.js +19 -11
- package/dist/cjs/rendering/caching/types.d.ts +1 -0
- package/dist/cjs/rendering/renderers/CanvasRenderer.js +3 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/SVGLoader.d.ts +1 -1
- package/dist/mjs/SVGLoader.mjs +11 -2
- package/dist/mjs/image/EditorImage.d.ts +20 -0
- package/dist/mjs/image/EditorImage.mjs +38 -10
- package/dist/mjs/rendering/Display.d.ts +9 -0
- package/dist/mjs/rendering/Display.mjs +39 -5
- package/dist/mjs/rendering/caching/CacheRecordManager.mjs +3 -4
- package/dist/mjs/rendering/caching/RenderingCache.d.ts +1 -0
- package/dist/mjs/rendering/caching/RenderingCache.mjs +4 -0
- package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +20 -12
- package/dist/mjs/rendering/caching/types.d.ts +1 -0
- package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +3 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +4 -4
@@ -28,6 +28,7 @@ export default class Display {
|
|
28
28
|
this.editor = editor;
|
29
29
|
this.parent = parent;
|
30
30
|
this.textRerenderOutput = null;
|
31
|
+
this.devicePixelRatio = window.devicePixelRatio ?? 1;
|
31
32
|
/**
|
32
33
|
* @returns the color at the given point on the dry ink renderer, or `null` if `screenPos`
|
33
34
|
* is not on the display.
|
@@ -112,16 +113,29 @@ export default class Display {
|
|
112
113
|
this.parent.appendChild(wetInkCanvas);
|
113
114
|
}
|
114
115
|
this.resizeSurfacesCallback = () => {
|
116
|
+
const expectedWidth = (canvas) => {
|
117
|
+
return Math.ceil(canvas.clientWidth * this.devicePixelRatio);
|
118
|
+
};
|
119
|
+
const expectedHeight = (canvas) => {
|
120
|
+
return Math.ceil(canvas.clientHeight * this.devicePixelRatio);
|
121
|
+
};
|
115
122
|
const hasSizeMismatch = (canvas) => {
|
116
|
-
return canvas
|
123
|
+
return expectedHeight(canvas) !== canvas.height || expectedWidth(canvas) !== canvas.width;
|
117
124
|
};
|
118
125
|
// Ensure that the drawing surfaces sizes match the
|
119
126
|
// canvas' sizes to prevent stretching.
|
120
127
|
if (hasSizeMismatch(dryInkCanvas) || hasSizeMismatch(wetInkCanvas)) {
|
121
|
-
dryInkCanvas.width = dryInkCanvas
|
122
|
-
dryInkCanvas.height = dryInkCanvas
|
123
|
-
wetInkCanvas.width = wetInkCanvas
|
124
|
-
wetInkCanvas.height = wetInkCanvas
|
128
|
+
dryInkCanvas.width = expectedWidth(dryInkCanvas);
|
129
|
+
dryInkCanvas.height = expectedHeight(dryInkCanvas);
|
130
|
+
wetInkCanvas.width = expectedWidth(wetInkCanvas);
|
131
|
+
wetInkCanvas.height = expectedHeight(wetInkCanvas);
|
132
|
+
// Ensure correct drawing operations on high-resolution screens.
|
133
|
+
// See
|
134
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas#scaling_for_high_resolution_displays
|
135
|
+
wetInkCtx.resetTransform();
|
136
|
+
dryInkCtx.resetTransform();
|
137
|
+
dryInkCtx.scale(this.devicePixelRatio, this.devicePixelRatio);
|
138
|
+
wetInkCtx.scale(this.devicePixelRatio, this.devicePixelRatio);
|
125
139
|
this.editor.notifier.dispatch(EditorEventType.DisplayResized, {
|
126
140
|
kind: EditorEventType.DisplayResized,
|
127
141
|
newSize: Vec2.of(this.width, this.height),
|
@@ -130,7 +144,10 @@ export default class Display {
|
|
130
144
|
};
|
131
145
|
this.resizeSurfacesCallback();
|
132
146
|
this.flattenCallback = () => {
|
147
|
+
dryInkCtx.save();
|
148
|
+
dryInkCtx.resetTransform();
|
133
149
|
dryInkCtx.drawImage(wetInkCanvas, 0, 0);
|
150
|
+
dryInkCtx.restore();
|
134
151
|
};
|
135
152
|
this.getColorAt = (screenPos) => {
|
136
153
|
const pixel = dryInkCtx.getImageData(screenPos.x, screenPos.y, 1, 1);
|
@@ -156,6 +173,23 @@ export default class Display {
|
|
156
173
|
textRendererOutputContainer.replaceChildren(rerenderButton, this.textRerenderOutput);
|
157
174
|
this.editor.createHTMLOverlay(textRendererOutputContainer);
|
158
175
|
}
|
176
|
+
/**
|
177
|
+
* Sets the device-pixel-ratio.
|
178
|
+
*
|
179
|
+
* Intended for debugging. Users do not need to call this manually.
|
180
|
+
*
|
181
|
+
* @internal
|
182
|
+
*/
|
183
|
+
setDevicePixelRatio(dpr) {
|
184
|
+
const minDpr = 0.001;
|
185
|
+
const maxDpr = 10;
|
186
|
+
if (isFinite(dpr) && dpr >= minDpr && dpr <= maxDpr && dpr !== this.devicePixelRatio) {
|
187
|
+
this.devicePixelRatio = dpr;
|
188
|
+
this.resizeSurfacesCallback?.();
|
189
|
+
return this.editor.queueRerender();
|
190
|
+
}
|
191
|
+
return undefined;
|
192
|
+
}
|
159
193
|
/**
|
160
194
|
* Rerenders the text-based display.
|
161
195
|
* The text-based display is intended for screen readers and can be navigated to by pressing `tab`.
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import CacheRecord from './CacheRecord.mjs';
|
2
|
-
const debugMode = false;
|
3
2
|
export class CacheRecordManager {
|
4
3
|
constructor(cacheProps) {
|
5
4
|
// Fixed-size array: Cache blocks are assigned indicies into [cachedCanvases].
|
@@ -16,19 +15,19 @@ export class CacheRecordManager {
|
|
16
15
|
const record = new CacheRecord(onDealloc, this.cacheState);
|
17
16
|
record.setRenderingRegion(drawTo);
|
18
17
|
this.cacheRecords.push(record);
|
19
|
-
if (debugMode) {
|
18
|
+
if (this.cacheState.debugMode) {
|
20
19
|
console.log('[Cache] Cache spaces used: ', this.cacheRecords.length, ' of ', this.maxCanvases);
|
21
20
|
}
|
22
21
|
return record;
|
23
22
|
}
|
24
23
|
else {
|
25
24
|
const lru = this.getLeastRecentlyUsedRecord();
|
26
|
-
if (debugMode) {
|
25
|
+
if (this.cacheState.debugMode) {
|
27
26
|
console.log('[Cache] Re-alloc. Times allocated: ', lru.allocCount, '\nLast used cycle: ', lru.getLastUsedCycle(), '\nCurrent cycle: ', this.cacheState.currentRenderingCycle);
|
28
27
|
}
|
29
28
|
lru.realloc(onDealloc);
|
30
29
|
lru.setRenderingRegion(drawTo);
|
31
|
-
if (debugMode) {
|
30
|
+
if (this.cacheState.debugMode) {
|
32
31
|
console.log('[Cache] Now re-alloc\'d. Last used cycle: ', lru.getLastUsedCycle());
|
33
32
|
console.assert(lru['cacheState'] === this.cacheState, '[Cache] Unequal cache states! cacheState should be a shared object!');
|
34
33
|
}
|
@@ -8,6 +8,7 @@ export default class RenderingCache {
|
|
8
8
|
props: cacheProps,
|
9
9
|
currentRenderingCycle: 0,
|
10
10
|
recordManager: this.recordManager,
|
11
|
+
debugMode: false,
|
11
12
|
};
|
12
13
|
this.recordManager.setSharedState(this.sharedState);
|
13
14
|
}
|
@@ -44,4 +45,7 @@ export default class RenderingCache {
|
|
44
45
|
getDebugInfo() {
|
45
46
|
return this.recordManager.getDebugInfo();
|
46
47
|
}
|
48
|
+
setIsDebugMode(debugMode) {
|
49
|
+
this.sharedState.debugMode = debugMode;
|
50
|
+
}
|
47
51
|
}
|
@@ -1,10 +1,8 @@
|
|
1
1
|
// A cache record with sub-nodes.
|
2
|
-
import { sortLeavesByZIndex } from '../../image/EditorImage.mjs';
|
2
|
+
import { computeFirstIndexToRender, sortLeavesByZIndex } from '../../image/EditorImage.mjs';
|
3
3
|
import { Rect2, Color4 } from '@js-draw/math';
|
4
4
|
// 3x3 divisions for each node.
|
5
5
|
const cacheDivisionSize = 3;
|
6
|
-
// True: Show rendering updates.
|
7
|
-
const debugMode = false;
|
8
6
|
export default class RenderingCacheNode {
|
9
7
|
constructor(region, cacheState) {
|
10
8
|
this.region = region;
|
@@ -161,8 +159,8 @@ export default class RenderingCacheNode {
|
|
161
159
|
items.forEach(item => item.render(screenRenderer, viewport.visibleRect));
|
162
160
|
return;
|
163
161
|
}
|
164
|
-
if (debugMode) {
|
165
|
-
screenRenderer.drawRect(this.region,
|
162
|
+
if (this.cacheState.debugMode) {
|
163
|
+
screenRenderer.drawRect(this.region, viewport.getSizeOfPixelOnCanvas(), { fill: Color4.yellow });
|
166
164
|
}
|
167
165
|
// Could we render direclty from [this] or do we need to recurse?
|
168
166
|
const couldRender = this.renderingWouldBeHighEnoughResolution(viewport);
|
@@ -197,7 +195,9 @@ export default class RenderingCacheNode {
|
|
197
195
|
}
|
198
196
|
let leafApproxRenderTime = 0;
|
199
197
|
for (const leaf of leavesByIds) {
|
200
|
-
|
198
|
+
if (!tooSmallToRender(leaf.getBBox())) {
|
199
|
+
leafApproxRenderTime += leaf.getContent().getProportionalRenderingTime();
|
200
|
+
}
|
201
201
|
}
|
202
202
|
// Is it worth it to render the items?
|
203
203
|
if (leafApproxRenderTime > this.cacheState.props.minProportionalRenderTimePerCache) {
|
@@ -233,26 +233,30 @@ export default class RenderingCacheNode {
|
|
233
233
|
this.renderedMaxZIndex = zIndex;
|
234
234
|
}
|
235
235
|
}
|
236
|
-
if (debugMode) {
|
237
|
-
|
236
|
+
if (this.cacheState.debugMode) {
|
237
|
+
// Clay for adding new elements
|
238
|
+
screenRenderer.drawRect(this.region, 2 * viewport.getSizeOfPixelOnCanvas(), { fill: Color4.clay });
|
238
239
|
}
|
239
240
|
}
|
240
241
|
}
|
241
|
-
else if (debugMode) {
|
242
|
+
else if (this.cacheState.debugMode) {
|
242
243
|
console.log('Decided on a full re-render. Reason: At least one of the following is false:', '\n leafIds.length > this.renderedIds.length: ', leafIds.length > this.renderedIds.length, '\n this.allRenderedIdsIn(leafIds): ', this.allRenderedIdsIn(leafIds), '\n this.renderedMaxZIndex !== null: ', this.renderedMaxZIndex !== null, '\n\nthis.rerenderedIds: ', this.renderedIds, ', leafIds: ', leafIds);
|
243
244
|
}
|
244
245
|
if (fullRerenderNeeded) {
|
245
246
|
thisRenderer = this.cachedRenderer.startRender();
|
246
247
|
thisRenderer.clear();
|
247
248
|
this.renderedMaxZIndex = null;
|
248
|
-
|
249
|
+
const startIndex = computeFirstIndexToRender(leaves, this.region);
|
250
|
+
for (let i = startIndex; i < leaves.length; i++) {
|
251
|
+
const leaf = leaves[i];
|
249
252
|
const content = leaf.getContent();
|
250
253
|
this.renderedMaxZIndex ??= content.getZIndex();
|
251
254
|
this.renderedMaxZIndex = Math.max(this.renderedMaxZIndex, content.getZIndex());
|
252
255
|
leaf.render(thisRenderer, this.region);
|
253
256
|
}
|
254
|
-
if (debugMode) {
|
255
|
-
|
257
|
+
if (this.cacheState.debugMode) {
|
258
|
+
// Red for full rerender
|
259
|
+
screenRenderer.drawRect(this.region, 3 * viewport.getSizeOfPixelOnCanvas(), { fill: Color4.red });
|
256
260
|
}
|
257
261
|
}
|
258
262
|
this.renderedIds = leafIds;
|
@@ -269,6 +273,10 @@ export default class RenderingCacheNode {
|
|
269
273
|
leaf.render(screenRenderer, this.region.intersection(viewport.visibleRect));
|
270
274
|
}
|
271
275
|
screenRenderer.endObject();
|
276
|
+
if (this.cacheState.debugMode) {
|
277
|
+
// Green for no cache needed render
|
278
|
+
screenRenderer.drawRect(this.region, 2 * viewport.getSizeOfPixelOnCanvas(), { fill: Color4.green });
|
279
|
+
}
|
272
280
|
}
|
273
281
|
}
|
274
282
|
else {
|
@@ -77,7 +77,10 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
77
77
|
return Vec2.of(this.ctx.canvas.clientWidth, this.ctx.canvas.clientHeight);
|
78
78
|
}
|
79
79
|
clear() {
|
80
|
+
this.ctx.save();
|
81
|
+
this.ctx.resetTransform();
|
80
82
|
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
83
|
+
this.ctx.restore();
|
81
84
|
}
|
82
85
|
beginPath(startPoint) {
|
83
86
|
startPoint = this.canvasToScreen(startPoint);
|
package/dist/mjs/version.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.9.0",
|
4
4
|
"description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
|
5
5
|
"types": "./dist/mjs/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -55,7 +55,7 @@
|
|
55
55
|
"license": "MIT",
|
56
56
|
"private": false,
|
57
57
|
"scripts": {
|
58
|
-
"dist-test": "
|
58
|
+
"dist-test": "cd dist-test/test_imports && npm install && npm run test",
|
59
59
|
"dist": "npm run build && npm run dist-test",
|
60
60
|
"build": "rm -rf ./dist/* && mkdir -p dist && build-tool build",
|
61
61
|
"watch": "rm -rf ./dist/* && mkdir -p dist && build-tool watch",
|
@@ -64,7 +64,7 @@
|
|
64
64
|
"postpack": "ts-node tools/copyREADME.ts revert"
|
65
65
|
},
|
66
66
|
"dependencies": {
|
67
|
-
"@js-draw/math": "^1.
|
67
|
+
"@js-draw/math": "^1.9.0",
|
68
68
|
"@melloware/coloris": "0.21.0"
|
69
69
|
},
|
70
70
|
"devDependencies": {
|
@@ -86,5 +86,5 @@
|
|
86
86
|
"freehand",
|
87
87
|
"svg"
|
88
88
|
],
|
89
|
-
"gitHead": "
|
89
|
+
"gitHead": "e824c37e9f216852cf096976e3a74fb4f177ead3"
|
90
90
|
}
|