@nasser-sw/fabric 7.0.1-beta8 → 7.0.1-beta9
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/debug/konva-master/CHANGELOG.md +1475 -0
- package/debug/konva-master/LICENSE +22 -0
- package/debug/konva-master/README.md +209 -0
- package/debug/konva-master/gulpfile.mjs +110 -0
- package/debug/konva-master/package.json +139 -0
- package/debug/konva-master/release.sh +62 -0
- package/debug/konva-master/resources/doc-includes/ContainerParams.txt +6 -0
- package/debug/konva-master/resources/doc-includes/NodeParams.txt +20 -0
- package/debug/konva-master/resources/doc-includes/ShapeParams.txt +53 -0
- package/debug/konva-master/resources/jsdoc.conf.json +28 -0
- package/debug/konva-master/rollup.config.mjs +32 -0
- package/debug/konva-master/src/Animation.ts +237 -0
- package/debug/konva-master/src/BezierFunctions.ts +826 -0
- package/debug/konva-master/src/Canvas.ts +230 -0
- package/debug/konva-master/src/Container.ts +649 -0
- package/debug/konva-master/src/Context.ts +1017 -0
- package/debug/konva-master/src/Core.ts +5 -0
- package/debug/konva-master/src/DragAndDrop.ts +173 -0
- package/debug/konva-master/src/Factory.ts +246 -0
- package/debug/konva-master/src/FastLayer.ts +29 -0
- package/debug/konva-master/src/Global.ts +210 -0
- package/debug/konva-master/src/Group.ts +31 -0
- package/debug/konva-master/src/Layer.ts +546 -0
- package/debug/konva-master/src/Node.ts +3477 -0
- package/debug/konva-master/src/PointerEvents.ts +67 -0
- package/debug/konva-master/src/Shape.ts +2081 -0
- package/debug/konva-master/src/Stage.ts +1000 -0
- package/debug/konva-master/src/Tween.ts +811 -0
- package/debug/konva-master/src/Util.ts +1123 -0
- package/debug/konva-master/src/Validators.ts +210 -0
- package/debug/konva-master/src/_CoreInternals.ts +85 -0
- package/debug/konva-master/src/_FullInternals.ts +171 -0
- package/debug/konva-master/src/canvas-backend.ts +36 -0
- package/debug/konva-master/src/filters/Blur.ts +388 -0
- package/debug/konva-master/src/filters/Brighten.ts +48 -0
- package/debug/konva-master/src/filters/Brightness.ts +30 -0
- package/debug/konva-master/src/filters/Contrast.ts +75 -0
- package/debug/konva-master/src/filters/Emboss.ts +207 -0
- package/debug/konva-master/src/filters/Enhance.ts +154 -0
- package/debug/konva-master/src/filters/Grayscale.ts +25 -0
- package/debug/konva-master/src/filters/HSL.ts +108 -0
- package/debug/konva-master/src/filters/HSV.ts +106 -0
- package/debug/konva-master/src/filters/Invert.ts +23 -0
- package/debug/konva-master/src/filters/Kaleidoscope.ts +274 -0
- package/debug/konva-master/src/filters/Mask.ts +220 -0
- package/debug/konva-master/src/filters/Noise.ts +44 -0
- package/debug/konva-master/src/filters/Pixelate.ts +107 -0
- package/debug/konva-master/src/filters/Posterize.ts +46 -0
- package/debug/konva-master/src/filters/RGB.ts +82 -0
- package/debug/konva-master/src/filters/RGBA.ts +103 -0
- package/debug/konva-master/src/filters/Sepia.ts +27 -0
- package/debug/konva-master/src/filters/Solarize.ts +29 -0
- package/debug/konva-master/src/filters/Threshold.ts +44 -0
- package/debug/konva-master/src/index.ts +3 -0
- package/debug/konva-master/src/shapes/Arc.ts +176 -0
- package/debug/konva-master/src/shapes/Arrow.ts +231 -0
- package/debug/konva-master/src/shapes/Circle.ts +76 -0
- package/debug/konva-master/src/shapes/Ellipse.ts +121 -0
- package/debug/konva-master/src/shapes/Image.ts +319 -0
- package/debug/konva-master/src/shapes/Label.ts +386 -0
- package/debug/konva-master/src/shapes/Line.ts +364 -0
- package/debug/konva-master/src/shapes/Path.ts +1013 -0
- package/debug/konva-master/src/shapes/Rect.ts +79 -0
- package/debug/konva-master/src/shapes/RegularPolygon.ts +167 -0
- package/debug/konva-master/src/shapes/Ring.ts +94 -0
- package/debug/konva-master/src/shapes/Sprite.ts +370 -0
- package/debug/konva-master/src/shapes/Star.ts +125 -0
- package/debug/konva-master/src/shapes/Text.ts +1065 -0
- package/debug/konva-master/src/shapes/TextPath.ts +583 -0
- package/debug/konva-master/src/shapes/Transformer.ts +1889 -0
- package/debug/konva-master/src/shapes/Wedge.ts +129 -0
- package/debug/konva-master/src/skia-backend.ts +35 -0
- package/debug/konva-master/src/types.ts +84 -0
- package/debug/konva-master/tsconfig.json +31 -0
- package/debug/konva-master/tsconfig.test.json +7 -0
- package/dist/index.js +915 -23
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +915 -23
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +915 -23
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +915 -23
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.json.min.mjs +1 -1
- package/dist/package.json.mjs +1 -1
- package/dist/src/shapes/Text/Text.d.ts +19 -0
- package/dist/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist/src/shapes/Text/Text.min.mjs +1 -1
- package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.mjs +238 -4
- package/dist/src/shapes/Text/Text.mjs.map +1 -1
- package/dist/src/shapes/Textbox.d.ts +38 -1
- package/dist/src/shapes/Textbox.d.ts.map +1 -1
- package/dist/src/shapes/Textbox.min.mjs +1 -1
- package/dist/src/shapes/Textbox.min.mjs.map +1 -1
- package/dist/src/shapes/Textbox.mjs +497 -15
- package/dist/src/shapes/Textbox.mjs.map +1 -1
- package/dist/src/text/examples/arabicTextExample.d.ts +60 -0
- package/dist/src/text/examples/arabicTextExample.d.ts.map +1 -0
- package/dist/src/text/measure.d.ts +9 -0
- package/dist/src/text/measure.d.ts.map +1 -1
- package/dist/src/text/measure.min.mjs +1 -1
- package/dist/src/text/measure.min.mjs.map +1 -1
- package/dist/src/text/measure.mjs +175 -4
- package/dist/src/text/measure.mjs.map +1 -1
- package/dist/src/text/overlayEditor.d.ts.map +1 -1
- package/dist/src/text/overlayEditor.min.mjs +1 -1
- package/dist/src/text/overlayEditor.min.mjs.map +1 -1
- package/dist/src/text/overlayEditor.mjs +7 -0
- package/dist/src/text/overlayEditor.mjs.map +1 -1
- package/dist/src/text/scriptUtils.d.ts +142 -0
- package/dist/src/text/scriptUtils.d.ts.map +1 -0
- package/dist/src/text/scriptUtils.min.mjs +2 -0
- package/dist/src/text/scriptUtils.min.mjs.map +1 -0
- package/dist/src/text/scriptUtils.mjs +212 -0
- package/dist/src/text/scriptUtils.mjs.map +1 -0
- package/dist-extensions/src/shapes/Text/Text.d.ts +19 -0
- package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Textbox.d.ts +38 -1
- package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
- package/dist-extensions/src/text/measure.d.ts +9 -0
- package/dist-extensions/src/text/measure.d.ts.map +1 -1
- package/dist-extensions/src/text/overlayEditor.d.ts.map +1 -1
- package/dist-extensions/src/text/scriptUtils.d.ts +142 -0
- package/dist-extensions/src/text/scriptUtils.d.ts.map +1 -0
- package/fabric-test-editor.html +2401 -46
- package/fonts/STV Bold.ttf +0 -0
- package/fonts/STV Light.ttf +0 -0
- package/fonts/STV Regular.ttf +0 -0
- package/package.json +1 -1
- package/src/shapes/Text/Text.ts +238 -5
- package/src/shapes/Textbox.ts +521 -11
- package/src/text/measure.ts +200 -50
- package/src/text/overlayEditor.ts +7 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { Factory } from '../Factory.ts';
|
|
2
|
+
import type { Filter } from '../Node.ts';
|
|
3
|
+
import { Node } from '../Node.ts';
|
|
4
|
+
import { Util } from '../Util.ts';
|
|
5
|
+
import { getNumberValidator } from '../Validators.ts';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* ToPolar Filter. Converts image data to polar coordinates. Performs
|
|
9
|
+
* w*h*4 pixel reads and w*h pixel writes. The r axis is placed along
|
|
10
|
+
* what would be the y axis and the theta axis along the x axis.
|
|
11
|
+
* @function
|
|
12
|
+
* @author ippo615
|
|
13
|
+
* @memberof Konva.Filters
|
|
14
|
+
* @param {ImageData} src, the source image data (what will be transformed)
|
|
15
|
+
* @param {ImageData} dst, the destination image data (where it will be saved)
|
|
16
|
+
* @param {Object} opt
|
|
17
|
+
* @param {Number} [opt.polarCenterX] horizontal location for the center of the circle,
|
|
18
|
+
* default is in the middle
|
|
19
|
+
* @param {Number} [opt.polarCenterY] vertical location for the center of the circle,
|
|
20
|
+
* default is in the middle
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const ToPolar = function (src, dst, opt) {
|
|
24
|
+
const srcPixels = src.data,
|
|
25
|
+
dstPixels = dst.data,
|
|
26
|
+
xSize = src.width,
|
|
27
|
+
ySize = src.height,
|
|
28
|
+
xMid = opt.polarCenterX || xSize / 2,
|
|
29
|
+
yMid = opt.polarCenterY || ySize / 2;
|
|
30
|
+
|
|
31
|
+
// Find the largest radius
|
|
32
|
+
let rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
|
33
|
+
let x = xSize - xMid;
|
|
34
|
+
let y = ySize - yMid;
|
|
35
|
+
const rad = Math.sqrt(x * x + y * y);
|
|
36
|
+
rMax = rad > rMax ? rad : rMax;
|
|
37
|
+
|
|
38
|
+
// We'll be uisng y as the radius, and x as the angle (theta=t)
|
|
39
|
+
const rSize = ySize,
|
|
40
|
+
tSize = xSize;
|
|
41
|
+
|
|
42
|
+
// We want to cover all angles (0-360) and we need to convert to
|
|
43
|
+
// radians (*PI/180)
|
|
44
|
+
const conversion = ((360 / tSize) * Math.PI) / 180;
|
|
45
|
+
|
|
46
|
+
// var x1, x2, x1i, x2i, y1, y2, y1i, y2i, scale;
|
|
47
|
+
|
|
48
|
+
for (let theta = 0; theta < tSize; theta += 1) {
|
|
49
|
+
const sin = Math.sin(theta * conversion);
|
|
50
|
+
const cos = Math.cos(theta * conversion);
|
|
51
|
+
for (let radius = 0; radius < rSize; radius += 1) {
|
|
52
|
+
x = Math.floor(xMid + ((rMax * radius) / rSize) * cos);
|
|
53
|
+
y = Math.floor(yMid + ((rMax * radius) / rSize) * sin);
|
|
54
|
+
let i = (y * xSize + x) * 4;
|
|
55
|
+
const r = srcPixels[i + 0];
|
|
56
|
+
const g = srcPixels[i + 1];
|
|
57
|
+
const b = srcPixels[i + 2];
|
|
58
|
+
const a = srcPixels[i + 3];
|
|
59
|
+
|
|
60
|
+
// Store it
|
|
61
|
+
//i = (theta * xSize + radius) * 4;
|
|
62
|
+
i = (theta + radius * xSize) * 4;
|
|
63
|
+
dstPixels[i + 0] = r;
|
|
64
|
+
dstPixels[i + 1] = g;
|
|
65
|
+
dstPixels[i + 2] = b;
|
|
66
|
+
dstPixels[i + 3] = a;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
* FromPolar Filter. Converts image data from polar coordinates back to rectangular.
|
|
73
|
+
* Performs w*h*4 pixel reads and w*h pixel writes.
|
|
74
|
+
* @function
|
|
75
|
+
* @author ippo615
|
|
76
|
+
* @memberof Konva.Filters
|
|
77
|
+
* @param {ImageData} src, the source image data (what will be transformed)
|
|
78
|
+
* @param {ImageData} dst, the destination image data (where it will be saved)
|
|
79
|
+
* @param {Object} opt
|
|
80
|
+
* @param {Number} [opt.polarCenterX] horizontal location for the center of the circle,
|
|
81
|
+
* default is in the middle
|
|
82
|
+
* @param {Number} [opt.polarCenterY] vertical location for the center of the circle,
|
|
83
|
+
* default is in the middle
|
|
84
|
+
* @param {Number} [opt.polarRotation] amount to rotate the image counterclockwis,
|
|
85
|
+
* 0 is no rotation, 360 degrees is a full rotation
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
const FromPolar = function (src, dst, opt) {
|
|
89
|
+
const srcPixels = src.data,
|
|
90
|
+
dstPixels = dst.data,
|
|
91
|
+
xSize = src.width,
|
|
92
|
+
ySize = src.height,
|
|
93
|
+
xMid = opt.polarCenterX || xSize / 2,
|
|
94
|
+
yMid = opt.polarCenterY || ySize / 2;
|
|
95
|
+
|
|
96
|
+
// Find the largest radius
|
|
97
|
+
let rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
|
98
|
+
let x = xSize - xMid;
|
|
99
|
+
let y = ySize - yMid;
|
|
100
|
+
const rad = Math.sqrt(x * x + y * y);
|
|
101
|
+
rMax = rad > rMax ? rad : rMax;
|
|
102
|
+
|
|
103
|
+
// We'll be uisng x as the radius, and y as the angle (theta=t)
|
|
104
|
+
const rSize = ySize,
|
|
105
|
+
tSize = xSize,
|
|
106
|
+
phaseShift = opt.polarRotation || 0;
|
|
107
|
+
|
|
108
|
+
// We need to convert to degrees and we need to make sure
|
|
109
|
+
// it's between (0-360)
|
|
110
|
+
// var conversion = tSize/360*180/Math.PI;
|
|
111
|
+
//var conversion = tSize/360*180/Math.PI;
|
|
112
|
+
|
|
113
|
+
let x1, y1;
|
|
114
|
+
|
|
115
|
+
for (x = 0; x < xSize; x += 1) {
|
|
116
|
+
for (y = 0; y < ySize; y += 1) {
|
|
117
|
+
const dx = x - xMid;
|
|
118
|
+
const dy = y - yMid;
|
|
119
|
+
const radius = (Math.sqrt(dx * dx + dy * dy) * rSize) / rMax;
|
|
120
|
+
let theta =
|
|
121
|
+
((Math.atan2(dy, dx) * 180) / Math.PI + 360 + phaseShift) % 360;
|
|
122
|
+
theta = (theta * tSize) / 360;
|
|
123
|
+
x1 = Math.floor(theta);
|
|
124
|
+
y1 = Math.floor(radius);
|
|
125
|
+
let i = (y1 * xSize + x1) * 4;
|
|
126
|
+
const r = srcPixels[i + 0];
|
|
127
|
+
const g = srcPixels[i + 1];
|
|
128
|
+
const b = srcPixels[i + 2];
|
|
129
|
+
const a = srcPixels[i + 3];
|
|
130
|
+
|
|
131
|
+
// Store it
|
|
132
|
+
i = (y * xSize + x) * 4;
|
|
133
|
+
dstPixels[i + 0] = r;
|
|
134
|
+
dstPixels[i + 1] = g;
|
|
135
|
+
dstPixels[i + 2] = b;
|
|
136
|
+
dstPixels[i + 3] = a;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
//Konva.Filters.ToPolar = Util._FilterWrapDoubleBuffer(ToPolar);
|
|
142
|
+
//Konva.Filters.FromPolar = Util._FilterWrapDoubleBuffer(FromPolar);
|
|
143
|
+
|
|
144
|
+
// create a temporary canvas for working - shared between multiple calls
|
|
145
|
+
|
|
146
|
+
/*
|
|
147
|
+
* Kaleidoscope Filter.
|
|
148
|
+
* @function
|
|
149
|
+
* @name Kaleidoscope
|
|
150
|
+
* @author ippo615
|
|
151
|
+
* @memberof Konva.Filters
|
|
152
|
+
* @example
|
|
153
|
+
* node.cache();
|
|
154
|
+
* node.filters([Konva.Filters.Kaleidoscope]);
|
|
155
|
+
* node.kaleidoscopePower(3);
|
|
156
|
+
* node.kaleidoscopeAngle(45);
|
|
157
|
+
*/
|
|
158
|
+
export const Kaleidoscope: Filter = function (imageData) {
|
|
159
|
+
const xSize = imageData.width,
|
|
160
|
+
ySize = imageData.height;
|
|
161
|
+
|
|
162
|
+
let x, y, xoff, i, r, g, b, a, srcPos, dstPos;
|
|
163
|
+
let power = Math.round(this.kaleidoscopePower());
|
|
164
|
+
const angle = Math.round(this.kaleidoscopeAngle());
|
|
165
|
+
const offset = Math.floor((xSize * (angle % 360)) / 360);
|
|
166
|
+
|
|
167
|
+
if (power < 1) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Work with our shared buffer canvas
|
|
172
|
+
const tempCanvas = Util.createCanvasElement();
|
|
173
|
+
tempCanvas.width = xSize;
|
|
174
|
+
tempCanvas.height = ySize;
|
|
175
|
+
const scratchData = tempCanvas
|
|
176
|
+
.getContext('2d')!
|
|
177
|
+
.getImageData(0, 0, xSize, ySize);
|
|
178
|
+
Util.releaseCanvas(tempCanvas);
|
|
179
|
+
// Convert thhe original to polar coordinates
|
|
180
|
+
ToPolar(imageData, scratchData, {
|
|
181
|
+
polarCenterX: xSize / 2,
|
|
182
|
+
polarCenterY: ySize / 2,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Determine how big each section will be, if it's too small
|
|
186
|
+
// make it bigger
|
|
187
|
+
let minSectionSize = xSize / Math.pow(2, power);
|
|
188
|
+
while (minSectionSize <= 8) {
|
|
189
|
+
minSectionSize = minSectionSize * 2;
|
|
190
|
+
power -= 1;
|
|
191
|
+
}
|
|
192
|
+
minSectionSize = Math.ceil(minSectionSize);
|
|
193
|
+
let sectionSize = minSectionSize;
|
|
194
|
+
|
|
195
|
+
// Copy the offset region to 0
|
|
196
|
+
// Depending on the size of filter and location of the offset we may need
|
|
197
|
+
// to copy the section backwards to prevent it from rewriting itself
|
|
198
|
+
let xStart = 0,
|
|
199
|
+
xEnd = sectionSize,
|
|
200
|
+
xDelta = 1;
|
|
201
|
+
if (offset + minSectionSize > xSize) {
|
|
202
|
+
xStart = sectionSize;
|
|
203
|
+
xEnd = 0;
|
|
204
|
+
xDelta = -1;
|
|
205
|
+
}
|
|
206
|
+
for (y = 0; y < ySize; y += 1) {
|
|
207
|
+
for (x = xStart; x !== xEnd; x += xDelta) {
|
|
208
|
+
xoff = Math.round(x + offset) % xSize;
|
|
209
|
+
srcPos = (xSize * y + xoff) * 4;
|
|
210
|
+
r = scratchData.data[srcPos + 0];
|
|
211
|
+
g = scratchData.data[srcPos + 1];
|
|
212
|
+
b = scratchData.data[srcPos + 2];
|
|
213
|
+
a = scratchData.data[srcPos + 3];
|
|
214
|
+
dstPos = (xSize * y + x) * 4;
|
|
215
|
+
scratchData.data[dstPos + 0] = r;
|
|
216
|
+
scratchData.data[dstPos + 1] = g;
|
|
217
|
+
scratchData.data[dstPos + 2] = b;
|
|
218
|
+
scratchData.data[dstPos + 3] = a;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Perform the actual effect
|
|
223
|
+
for (y = 0; y < ySize; y += 1) {
|
|
224
|
+
sectionSize = Math.floor(minSectionSize);
|
|
225
|
+
for (i = 0; i < power; i += 1) {
|
|
226
|
+
for (x = 0; x < sectionSize + 1; x += 1) {
|
|
227
|
+
srcPos = (xSize * y + x) * 4;
|
|
228
|
+
r = scratchData.data[srcPos + 0];
|
|
229
|
+
g = scratchData.data[srcPos + 1];
|
|
230
|
+
b = scratchData.data[srcPos + 2];
|
|
231
|
+
a = scratchData.data[srcPos + 3];
|
|
232
|
+
dstPos = (xSize * y + sectionSize * 2 - x - 1) * 4;
|
|
233
|
+
scratchData.data[dstPos + 0] = r;
|
|
234
|
+
scratchData.data[dstPos + 1] = g;
|
|
235
|
+
scratchData.data[dstPos + 2] = b;
|
|
236
|
+
scratchData.data[dstPos + 3] = a;
|
|
237
|
+
}
|
|
238
|
+
sectionSize *= 2;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Convert back from polar coordinates
|
|
243
|
+
FromPolar(scratchData, imageData, { polarRotation: 0 });
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* get/set kaleidoscope power. Use with {@link Konva.Filters.Kaleidoscope} filter.
|
|
248
|
+
* @name Konva.Node#kaleidoscopePower
|
|
249
|
+
* @method
|
|
250
|
+
* @param {Integer} power of kaleidoscope
|
|
251
|
+
* @returns {Integer}
|
|
252
|
+
*/
|
|
253
|
+
Factory.addGetterSetter(
|
|
254
|
+
Node,
|
|
255
|
+
'kaleidoscopePower',
|
|
256
|
+
2,
|
|
257
|
+
getNumberValidator(),
|
|
258
|
+
Factory.afterSetFilter
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* get/set kaleidoscope angle. Use with {@link Konva.Filters.Kaleidoscope} filter.
|
|
263
|
+
* @name Konva.Node#kaleidoscopeAngle
|
|
264
|
+
* @method
|
|
265
|
+
* @param {Integer} degrees
|
|
266
|
+
* @returns {Integer}
|
|
267
|
+
*/
|
|
268
|
+
Factory.addGetterSetter(
|
|
269
|
+
Node,
|
|
270
|
+
'kaleidoscopeAngle',
|
|
271
|
+
0,
|
|
272
|
+
getNumberValidator(),
|
|
273
|
+
Factory.afterSetFilter
|
|
274
|
+
);
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Factory } from '../Factory.ts';
|
|
2
|
+
import type { Filter } from '../Node.ts';
|
|
3
|
+
import { Node } from '../Node.ts';
|
|
4
|
+
import { getNumberValidator } from '../Validators.ts';
|
|
5
|
+
|
|
6
|
+
function pixelAt(idata, x: number, y: number) {
|
|
7
|
+
let idx = (y * idata.width + x) * 4;
|
|
8
|
+
const d: Array<number> = [];
|
|
9
|
+
d.push(
|
|
10
|
+
idata.data[idx++],
|
|
11
|
+
idata.data[idx++],
|
|
12
|
+
idata.data[idx++],
|
|
13
|
+
idata.data[idx++]
|
|
14
|
+
);
|
|
15
|
+
return d;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function rgbDistance(p1, p2) {
|
|
19
|
+
return Math.sqrt(
|
|
20
|
+
Math.pow(p1[0] - p2[0], 2) +
|
|
21
|
+
Math.pow(p1[1] - p2[1], 2) +
|
|
22
|
+
Math.pow(p1[2] - p2[2], 2)
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function rgbMean(pTab) {
|
|
27
|
+
const m = [0, 0, 0];
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < pTab.length; i++) {
|
|
30
|
+
m[0] += pTab[i][0];
|
|
31
|
+
m[1] += pTab[i][1];
|
|
32
|
+
m[2] += pTab[i][2];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
m[0] /= pTab.length;
|
|
36
|
+
m[1] /= pTab.length;
|
|
37
|
+
m[2] /= pTab.length;
|
|
38
|
+
|
|
39
|
+
return m;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function backgroundMask(idata, threshold) {
|
|
43
|
+
const rgbv_no = pixelAt(idata, 0, 0);
|
|
44
|
+
const rgbv_ne = pixelAt(idata, idata.width - 1, 0);
|
|
45
|
+
const rgbv_so = pixelAt(idata, 0, idata.height - 1);
|
|
46
|
+
const rgbv_se = pixelAt(idata, idata.width - 1, idata.height - 1);
|
|
47
|
+
|
|
48
|
+
const thres = threshold || 10;
|
|
49
|
+
if (
|
|
50
|
+
rgbDistance(rgbv_no, rgbv_ne) < thres &&
|
|
51
|
+
rgbDistance(rgbv_ne, rgbv_se) < thres &&
|
|
52
|
+
rgbDistance(rgbv_se, rgbv_so) < thres &&
|
|
53
|
+
rgbDistance(rgbv_so, rgbv_no) < thres
|
|
54
|
+
) {
|
|
55
|
+
// Mean color
|
|
56
|
+
const mean = rgbMean([rgbv_ne, rgbv_no, rgbv_se, rgbv_so]);
|
|
57
|
+
|
|
58
|
+
// Mask based on color distance
|
|
59
|
+
const mask: Array<number> = [];
|
|
60
|
+
for (let i = 0; i < idata.width * idata.height; i++) {
|
|
61
|
+
const d = rgbDistance(mean, [
|
|
62
|
+
idata.data[i * 4],
|
|
63
|
+
idata.data[i * 4 + 1],
|
|
64
|
+
idata.data[i * 4 + 2],
|
|
65
|
+
]);
|
|
66
|
+
mask[i] = d < thres ? 0 : 255;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return mask;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function applyMask(idata, mask) {
|
|
74
|
+
for (let i = 0; i < idata.width * idata.height; i++) {
|
|
75
|
+
idata.data[4 * i + 3] = mask[i];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function erodeMask(mask, sw, sh) {
|
|
80
|
+
const weights = [1, 1, 1, 1, 0, 1, 1, 1, 1];
|
|
81
|
+
const side = Math.round(Math.sqrt(weights.length));
|
|
82
|
+
const halfSide = Math.floor(side / 2);
|
|
83
|
+
|
|
84
|
+
const maskResult: Array<number> = [];
|
|
85
|
+
for (let y = 0; y < sh; y++) {
|
|
86
|
+
for (let x = 0; x < sw; x++) {
|
|
87
|
+
const so = y * sw + x;
|
|
88
|
+
let a = 0;
|
|
89
|
+
for (let cy = 0; cy < side; cy++) {
|
|
90
|
+
for (let cx = 0; cx < side; cx++) {
|
|
91
|
+
const scy = y + cy - halfSide;
|
|
92
|
+
const scx = x + cx - halfSide;
|
|
93
|
+
|
|
94
|
+
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
|
95
|
+
const srcOff = scy * sw + scx;
|
|
96
|
+
const wt = weights[cy * side + cx];
|
|
97
|
+
|
|
98
|
+
a += mask[srcOff] * wt;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
maskResult[so] = a === 255 * 8 ? 255 : 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return maskResult;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function dilateMask(mask, sw, sh) {
|
|
111
|
+
const weights = [1, 1, 1, 1, 1, 1, 1, 1, 1];
|
|
112
|
+
const side = Math.round(Math.sqrt(weights.length));
|
|
113
|
+
const halfSide = Math.floor(side / 2);
|
|
114
|
+
|
|
115
|
+
const maskResult: Array<number> = [];
|
|
116
|
+
for (let y = 0; y < sh; y++) {
|
|
117
|
+
for (let x = 0; x < sw; x++) {
|
|
118
|
+
const so = y * sw + x;
|
|
119
|
+
let a = 0;
|
|
120
|
+
for (let cy = 0; cy < side; cy++) {
|
|
121
|
+
for (let cx = 0; cx < side; cx++) {
|
|
122
|
+
const scy = y + cy - halfSide;
|
|
123
|
+
const scx = x + cx - halfSide;
|
|
124
|
+
|
|
125
|
+
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
|
126
|
+
const srcOff = scy * sw + scx;
|
|
127
|
+
const wt = weights[cy * side + cx];
|
|
128
|
+
|
|
129
|
+
a += mask[srcOff] * wt;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
maskResult[so] = a >= 255 * 4 ? 255 : 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return maskResult;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function smoothEdgeMask(mask, sw: number, sh: number) {
|
|
142
|
+
const weights = [
|
|
143
|
+
1 / 9,
|
|
144
|
+
1 / 9,
|
|
145
|
+
1 / 9,
|
|
146
|
+
1 / 9,
|
|
147
|
+
1 / 9,
|
|
148
|
+
1 / 9,
|
|
149
|
+
1 / 9,
|
|
150
|
+
1 / 9,
|
|
151
|
+
1 / 9,
|
|
152
|
+
];
|
|
153
|
+
const side = Math.round(Math.sqrt(weights.length));
|
|
154
|
+
const halfSide = Math.floor(side / 2);
|
|
155
|
+
|
|
156
|
+
const maskResult: Array<number> = [];
|
|
157
|
+
for (let y = 0; y < sh; y++) {
|
|
158
|
+
for (let x = 0; x < sw; x++) {
|
|
159
|
+
const so = y * sw + x;
|
|
160
|
+
let a = 0;
|
|
161
|
+
for (let cy = 0; cy < side; cy++) {
|
|
162
|
+
for (let cx = 0; cx < side; cx++) {
|
|
163
|
+
const scy = y + cy - halfSide;
|
|
164
|
+
const scx = x + cx - halfSide;
|
|
165
|
+
|
|
166
|
+
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
|
167
|
+
const srcOff = scy * sw + scx;
|
|
168
|
+
const wt = weights[cy * side + cx];
|
|
169
|
+
|
|
170
|
+
a += mask[srcOff] * wt;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
maskResult[so] = a;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return maskResult;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Mask Filter
|
|
184
|
+
* @function
|
|
185
|
+
* @name Mask
|
|
186
|
+
* @memberof Konva.Filters
|
|
187
|
+
* @param {Object} imageData
|
|
188
|
+
* @example
|
|
189
|
+
* node.cache();
|
|
190
|
+
* node.filters([Konva.Filters.Mask]);
|
|
191
|
+
* node.threshold(200);
|
|
192
|
+
*/
|
|
193
|
+
export const Mask: Filter = function (imageData) {
|
|
194
|
+
// Detect pixels close to the background color
|
|
195
|
+
const threshold = this.threshold();
|
|
196
|
+
let mask = backgroundMask(imageData, threshold);
|
|
197
|
+
if (mask) {
|
|
198
|
+
// Erode
|
|
199
|
+
mask = erodeMask(mask, imageData.width, imageData.height);
|
|
200
|
+
|
|
201
|
+
// Dilate
|
|
202
|
+
mask = dilateMask(mask, imageData.width, imageData.height);
|
|
203
|
+
|
|
204
|
+
// Gradient
|
|
205
|
+
mask = smoothEdgeMask(mask, imageData.width, imageData.height);
|
|
206
|
+
|
|
207
|
+
// Apply mask
|
|
208
|
+
applyMask(imageData, mask);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return imageData;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
Factory.addGetterSetter(
|
|
215
|
+
Node,
|
|
216
|
+
'threshold',
|
|
217
|
+
0,
|
|
218
|
+
getNumberValidator(),
|
|
219
|
+
Factory.afterSetFilter
|
|
220
|
+
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Factory } from '../Factory.ts';
|
|
2
|
+
import type { Filter } from '../Node.ts';
|
|
3
|
+
import { Node } from '../Node.ts';
|
|
4
|
+
import { getNumberValidator } from '../Validators.ts';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Noise Filter. Randomly adds or substracts to the color channels
|
|
8
|
+
* @function
|
|
9
|
+
* @name Noise
|
|
10
|
+
* @memberof Konva.Filters
|
|
11
|
+
* @param {Object} imageData
|
|
12
|
+
* @author ippo615
|
|
13
|
+
* @example
|
|
14
|
+
* node.cache();
|
|
15
|
+
* node.filters([Konva.Filters.Noise]);
|
|
16
|
+
* node.noise(0.8);
|
|
17
|
+
*/
|
|
18
|
+
export const Noise: Filter = function (imageData) {
|
|
19
|
+
const amount = this.noise() * 255,
|
|
20
|
+
data = imageData.data,
|
|
21
|
+
nPixels = data.length,
|
|
22
|
+
half = amount / 2;
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < nPixels; i += 4) {
|
|
25
|
+
data[i + 0] += half - 2 * half * Math.random();
|
|
26
|
+
data[i + 1] += half - 2 * half * Math.random();
|
|
27
|
+
data[i + 2] += half - 2 * half * Math.random();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
Factory.addGetterSetter(
|
|
32
|
+
Node,
|
|
33
|
+
'noise',
|
|
34
|
+
0.2,
|
|
35
|
+
getNumberValidator(),
|
|
36
|
+
Factory.afterSetFilter
|
|
37
|
+
);
|
|
38
|
+
/**
|
|
39
|
+
* get/set noise amount. Must be a value between 0 and 1. Use with {@link Konva.Filters.Noise} filter.
|
|
40
|
+
* @name Konva.Node#noise
|
|
41
|
+
* @method
|
|
42
|
+
* @param {Number} noise
|
|
43
|
+
* @returns {Number}
|
|
44
|
+
*/
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Factory } from '../Factory.ts';
|
|
2
|
+
import { Util } from '../Util.ts';
|
|
3
|
+
import type { Filter } from '../Node.ts';
|
|
4
|
+
import { Node } from '../Node.ts';
|
|
5
|
+
import { getNumberValidator } from '../Validators.ts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Pixelate Filter. Averages groups of pixels and redraws
|
|
9
|
+
* them as larger pixels
|
|
10
|
+
* @function
|
|
11
|
+
* @name Pixelate
|
|
12
|
+
* @memberof Konva.Filters
|
|
13
|
+
* @param {Object} imageData
|
|
14
|
+
* @author ippo615
|
|
15
|
+
* @example
|
|
16
|
+
* node.cache();
|
|
17
|
+
* node.filters([Konva.Filters.Pixelate]);
|
|
18
|
+
* node.pixelSize(10);
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export const Pixelate: Filter = function (imageData) {
|
|
22
|
+
let pixelSize = Math.ceil(this.pixelSize()),
|
|
23
|
+
width = imageData.width,
|
|
24
|
+
height = imageData.height,
|
|
25
|
+
//pixelsPerBin = pixelSize * pixelSize,
|
|
26
|
+
nBinsX = Math.ceil(width / pixelSize),
|
|
27
|
+
nBinsY = Math.ceil(height / pixelSize),
|
|
28
|
+
data = imageData.data;
|
|
29
|
+
|
|
30
|
+
if (pixelSize <= 0) {
|
|
31
|
+
Util.error('pixelSize value can not be <= 0');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (let xBin = 0; xBin < nBinsX; xBin += 1) {
|
|
36
|
+
for (let yBin = 0; yBin < nBinsY; yBin += 1) {
|
|
37
|
+
// Initialize the color accumlators to 0
|
|
38
|
+
let red = 0;
|
|
39
|
+
let green = 0;
|
|
40
|
+
let blue = 0;
|
|
41
|
+
let alpha = 0;
|
|
42
|
+
|
|
43
|
+
// Determine which pixels are included in this bin
|
|
44
|
+
const xBinStart = xBin * pixelSize;
|
|
45
|
+
const xBinEnd = xBinStart + pixelSize;
|
|
46
|
+
const yBinStart = yBin * pixelSize;
|
|
47
|
+
const yBinEnd = yBinStart + pixelSize;
|
|
48
|
+
|
|
49
|
+
// Add all of the pixels to this bin!
|
|
50
|
+
let pixelsInBin = 0;
|
|
51
|
+
for (let x = xBinStart; x < xBinEnd; x += 1) {
|
|
52
|
+
if (x >= width) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
for (let y = yBinStart; y < yBinEnd; y += 1) {
|
|
56
|
+
if (y >= height) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const i = (width * y + x) * 4;
|
|
60
|
+
red += data[i + 0];
|
|
61
|
+
green += data[i + 1];
|
|
62
|
+
blue += data[i + 2];
|
|
63
|
+
alpha += data[i + 3];
|
|
64
|
+
pixelsInBin += 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Make sure the channels are between 0-255
|
|
69
|
+
red = red / pixelsInBin;
|
|
70
|
+
green = green / pixelsInBin;
|
|
71
|
+
blue = blue / pixelsInBin;
|
|
72
|
+
alpha = alpha / pixelsInBin;
|
|
73
|
+
|
|
74
|
+
// Draw this bin
|
|
75
|
+
for (let x = xBinStart; x < xBinEnd; x += 1) {
|
|
76
|
+
if (x >= width) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
for (let y = yBinStart; y < yBinEnd; y += 1) {
|
|
80
|
+
if (y >= height) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const i = (width * y + x) * 4;
|
|
84
|
+
data[i + 0] = red;
|
|
85
|
+
data[i + 1] = green;
|
|
86
|
+
data[i + 2] = blue;
|
|
87
|
+
data[i + 3] = alpha;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
Factory.addGetterSetter(
|
|
95
|
+
Node,
|
|
96
|
+
'pixelSize',
|
|
97
|
+
8,
|
|
98
|
+
getNumberValidator(),
|
|
99
|
+
Factory.afterSetFilter
|
|
100
|
+
);
|
|
101
|
+
/**
|
|
102
|
+
* get/set pixel size. Use with {@link Konva.Filters.Pixelate} filter.
|
|
103
|
+
* @name Konva.Node#pixelSize
|
|
104
|
+
* @method
|
|
105
|
+
* @param {Integer} pixelSize
|
|
106
|
+
* @returns {Integer}
|
|
107
|
+
*/
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Factory } from '../Factory.ts';
|
|
2
|
+
import type { Filter } from '../Node.ts';
|
|
3
|
+
import { Node } from '../Node.ts';
|
|
4
|
+
import { getNumberValidator } from '../Validators.ts';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Posterize Filter. Adjusts the channels so that there are no more
|
|
8
|
+
* than n different values for that channel. This is also applied
|
|
9
|
+
* to the alpha channel.
|
|
10
|
+
* @function
|
|
11
|
+
* @name Posterize
|
|
12
|
+
* @author ippo615
|
|
13
|
+
* @memberof Konva.Filters
|
|
14
|
+
* @param {Object} imageData
|
|
15
|
+
* @example
|
|
16
|
+
* node.cache();
|
|
17
|
+
* node.filters([Konva.Filters.Posterize]);
|
|
18
|
+
* node.levels(0.8); // between 0 and 1
|
|
19
|
+
*/
|
|
20
|
+
export const Posterize: Filter = function (imageData) {
|
|
21
|
+
// level must be between 1 and 255
|
|
22
|
+
const levels = Math.round(this.levels() * 254) + 1,
|
|
23
|
+
data = imageData.data,
|
|
24
|
+
len = data.length,
|
|
25
|
+
scale = 255 / levels;
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < len; i += 1) {
|
|
28
|
+
data[i] = Math.floor(data[i] / scale) * scale;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
Factory.addGetterSetter(
|
|
33
|
+
Node,
|
|
34
|
+
'levels',
|
|
35
|
+
0.5,
|
|
36
|
+
getNumberValidator(),
|
|
37
|
+
Factory.afterSetFilter
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* get/set levels. Must be a number between 0 and 1. Use with {@link Konva.Filters.Posterize} filter.
|
|
42
|
+
* @name Konva.Node#levels
|
|
43
|
+
* @method
|
|
44
|
+
* @param {Number} level between 0 and 1
|
|
45
|
+
* @returns {Number}
|
|
46
|
+
*/
|