@leafer-ui/paint 1.0.0-beta.9 → 1.0.0-rc.2

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/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@leafer-ui/paint",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0-rc.2",
4
4
  "description": "@leafer-ui/paint",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
7
7
  "main": "src/index.ts",
8
+ "types": "types/index.d.ts",
8
9
  "files": [
9
- "src"
10
+ "src",
11
+ "types",
12
+ "dist"
10
13
  ],
11
14
  "repository": {
12
15
  "type": "git",
@@ -19,11 +22,10 @@
19
22
  "leaferjs"
20
23
  ],
21
24
  "dependencies": {
22
- "@leafer/core": "1.0.0-beta.9",
23
- "@leafer-ui/color": "1.0.0-beta.9"
25
+ "@leafer/core": "1.0.0-rc.2"
24
26
  },
25
27
  "devDependencies": {
26
- "@leafer/interface": "1.0.0-beta.9",
27
- "@leafer-ui/interface": "1.0.0-beta.9"
28
+ "@leafer/interface": "1.0.0-rc.2",
29
+ "@leafer-ui/interface": "1.0.0-rc.2"
28
30
  }
29
31
  }
package/src/Compute.ts CHANGED
@@ -1,34 +1,33 @@
1
- import { IUI, IPaint, ILeafPaint, IRGB } from '@leafer-ui/interface'
2
- import { ColorConvert } from '@leafer-ui/color'
1
+ import { IUI, IPaint, ILeafPaint, IRGB, IBooleanMap } from '@leafer-ui/interface'
2
+ import { ColorConvert } from '@leafer-ui/core'
3
3
 
4
- import { image } from "./paint/image"
4
+ import { image } from "./paint/image/image"
5
5
  import { linearGradient } from './paint/linear'
6
6
  import { radialGradient } from "./paint/radial"
7
7
  import { conicGradient } from "./paint/conic"
8
+ import { recycleImage } from './paint/image'
8
9
 
9
10
 
10
- export function computeFill(ui: IUI): void {
11
- compute(ui, 'fill')
12
- }
13
-
14
- export function computeStroke(ui: IUI): void {
15
- compute(ui, 'stroke')
16
- }
11
+ let recycleMap: IBooleanMap
17
12
 
18
- function compute(ui: IUI, attrName: string): void {
13
+ export function compute(ui: IUI, attrName: string): void {
14
+ const value: ILeafPaint[] = []
15
+ let item: ILeafPaint
19
16
  let paints = ui.__.__input[attrName] as IPaint[]
20
17
 
21
- let item: ILeafPaint
22
- const value: ILeafPaint[] = []
23
18
  if (!(paints instanceof Array)) paints = [paints]
24
19
 
20
+ recycleMap = recycleImage(ui.__, attrName)
21
+
25
22
  for (let i = 0, len = paints.length; i < len; i++) {
26
23
  item = getLeafPaint(ui, paints[i], attrName)
27
24
  if (item) value.push(item)
28
25
  }
26
+
29
27
  ui.__['_' + attrName] = value.length ? value : undefined
30
28
  }
31
29
 
30
+
32
31
  function getLeafPaint(ui: IUI, paint: IPaint, attrName: string): ILeafPaint {
33
32
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0) return undefined
34
33
  const { boxBounds } = ui.__layout
@@ -38,7 +37,7 @@ function getLeafPaint(ui: IUI, paint: IPaint, attrName: string): ILeafPaint {
38
37
  let { type, blendMode, color, opacity } = paint
39
38
  return { type, blendMode, style: ColorConvert.string(color, opacity) }
40
39
  case 'image':
41
- return image(ui, attrName, paint, boxBounds)
40
+ return image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url])
42
41
  case 'linear':
43
42
  return linearGradient(paint, boxBounds)
44
43
  case 'radial':
package/src/Fill.ts CHANGED
@@ -2,43 +2,50 @@ import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
3
  import { ILeafPaint, IUI } from '@leafer-ui/interface'
4
4
 
5
- import { drawText } from './FillText'
5
+ import { checkImage } from './paint/image'
6
+ import { fillText } from './FillText'
6
7
 
7
8
 
8
- export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string | object): void {
9
+ export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string): void {
9
10
  canvas.fillStyle = fill
10
- ui.__.__font ? drawText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill())
11
+ ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill())
11
12
  }
12
13
 
14
+
13
15
  export function fills(ui: IUI, canvas: ILeaferCanvas, fills: ILeafPaint[]): void {
14
16
  let item: ILeafPaint
15
17
  const { windingRule, __font } = ui.__
16
18
  for (let i = 0, len = fills.length; i < len; i++) {
17
19
  item = fills[i]
18
- canvas.fillStyle = item.style
19
20
 
20
- if (item.transform) {
21
- canvas.save()
21
+ if (item.image && checkImage(ui, canvas, item, !__font)) continue
22
+
23
+ if (item.style) {
22
24
 
23
- const { a, b, c, d, e, f } = item.transform
24
- canvas.transform(a, b, c, d, e, f)
25
+ canvas.fillStyle = item.style
25
26
 
26
- if (item.blendMode) canvas.blendMode = item.blendMode
27
+ if (item.transform) {
28
+ canvas.save()
27
29
 
28
- __font ? drawText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
30
+ canvas.transform(item.transform)
29
31
 
30
- canvas.restore()
31
- } else {
32
- if (item.blendMode) {
33
- canvas.saveBlendMode(item.blendMode)
32
+ if (item.blendMode) canvas.blendMode = item.blendMode
34
33
 
35
- __font ? drawText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
34
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
36
35
 
37
- canvas.restoreBlendMode()
36
+ canvas.restore()
38
37
  } else {
38
+ if (item.blendMode) {
39
+ canvas.saveBlendMode(item.blendMode)
40
+
41
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
42
+
43
+ canvas.restoreBlendMode()
44
+ } else {
39
45
 
40
- __font ? drawText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
46
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
41
47
 
48
+ }
42
49
  }
43
50
  }
44
51
 
package/src/FillText.ts CHANGED
@@ -3,7 +3,7 @@ import { ILeaferCanvas } from '@leafer/interface'
3
3
  import { IUI, ITextRowData } from '@leafer-ui/interface'
4
4
 
5
5
 
6
- export function drawText(ui: IUI, canvas: ILeaferCanvas): void {
6
+ export function fillText(ui: IUI, canvas: ILeaferCanvas): void {
7
7
 
8
8
  let row: ITextRowData
9
9
  const { rows, decorationY, decorationHeight } = ui.__.__textDrawData
package/src/Shape.ts CHANGED
@@ -13,12 +13,14 @@ export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions):
13
13
  let worldCanvas: ILeaferCanvas
14
14
 
15
15
  const { __world } = ui
16
- let { a: scaleX, d: scaleY } = __world
16
+ let { scaleX, scaleY } = __world
17
+ if (scaleX < 0) scaleX = -scaleX
18
+ if (scaleY < 0) scaleY = -scaleY
17
19
 
18
20
  if (!current.bounds.includes(__world, options.matrix)) {
19
21
 
20
22
  const { renderShapeSpread: spread } = ui.__layout
21
- const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, spread * scaleX) : current.bounds, __world, options.matrix)
23
+ const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, spread * scaleX, spread * scaleY) : current.bounds, __world, options.matrix)
22
24
  matrix = current.bounds.getFitMatrix(worldClipBounds)
23
25
 
24
26
  if (matrix.a < 1) {
package/src/Stroke.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
- import { IUI, ILeafStrokePaint, ILeafPaint } from '@leafer-ui/interface'
3
+ import { IUI, ILeafPaint } from '@leafer-ui/interface'
4
4
 
5
- import { strokeText, strokesText } from './StrokeText'
5
+ import { strokeText, drawStrokesStyle } from './StrokeText'
6
6
 
7
7
 
8
- export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
8
+ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string): void {
9
9
  const options = ui.__
10
10
  const { strokeWidth, strokeAlign, __font } = options
11
11
  if (!strokeWidth) return
@@ -29,7 +29,7 @@ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object):
29
29
  canvas.save()
30
30
  canvas.setStroke(stroke, strokeWidth * 2, options)
31
31
 
32
- canvas.clip(options.windingRule)
32
+ options.windingRule ? canvas.clip(options.windingRule) : canvas.clip()
33
33
  canvas.stroke()
34
34
 
35
35
  canvas.restore()
@@ -44,7 +44,7 @@ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object):
44
44
 
45
45
  out.stroke()
46
46
 
47
- out.clip(options.windingRule)
47
+ options.windingRule ? out.clip(options.windingRule) : out.clip()
48
48
  out.clearWorld(ui.__layout.renderBounds)
49
49
 
50
50
  canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
@@ -56,6 +56,7 @@ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object):
56
56
  }
57
57
  }
58
58
 
59
+
59
60
  export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
60
61
  const options = ui.__
61
62
  const { strokeWidth, strokeAlign, __font } = options
@@ -63,7 +64,7 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
63
64
 
64
65
  if (__font) {
65
66
 
66
- strokesText(ui, canvas, strokes)
67
+ strokeText(ui, canvas, strokes)
67
68
 
68
69
  } else {
69
70
 
@@ -71,15 +72,15 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
71
72
 
72
73
  case 'center':
73
74
  canvas.setStroke(undefined, strokeWidth, options)
74
- drawStrokesStyle(strokes, canvas)
75
+ drawStrokesStyle(ui, strokes, canvas)
75
76
  break
76
77
 
77
78
  case 'inside':
78
79
  canvas.save()
79
80
  canvas.setStroke(undefined, strokeWidth * 2, options)
80
- canvas.clip(options.windingRule)
81
+ options.windingRule ? canvas.clip(options.windingRule) : canvas.clip()
81
82
 
82
- drawStrokesStyle(strokes, canvas)
83
+ drawStrokesStyle(ui, strokes, canvas)
83
84
 
84
85
  canvas.restore()
85
86
  break
@@ -91,9 +92,9 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
91
92
 
92
93
  out.setStroke(undefined, strokeWidth * 2, ui.__)
93
94
 
94
- drawStrokesStyle(strokes, out)
95
+ drawStrokesStyle(ui, strokes, out)
95
96
 
96
- out.clip(options.windingRule)
97
+ options.windingRule ? out.clip(options.windingRule) : out.clip()
97
98
  out.clearWorld(renderBounds)
98
99
 
99
100
  canvas.copyWorldToInner(out, ui.__world, renderBounds)
@@ -104,18 +105,3 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
104
105
  }
105
106
 
106
107
  }
107
-
108
- function drawStrokesStyle(strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
109
- strokes.forEach((item: ILeafStrokePaint) => {
110
- canvas.strokeStyle = item.style
111
-
112
- if (item.blendMode) {
113
- canvas.saveBlendMode(item.blendMode)
114
- canvas.stroke()
115
- canvas.restoreBlendMode()
116
- } else {
117
- canvas.stroke()
118
- }
119
-
120
- })
121
- }
package/src/StrokeText.ts CHANGED
@@ -1,93 +1,45 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
- import { IUI, ITextRowData, ILeafStrokePaint, ILeafPaint, IStrokeAlign } from '@leafer-ui/interface'
3
+ import { IUI, ITextRowData, ILeafPaint, IStrokeAlign, ILeafStrokePaint } from '@leafer-ui/interface'
4
4
 
5
- import { drawText } from './FillText'
5
+ import { fillText } from './FillText'
6
+ import { checkImage } from './paint/image'
6
7
 
7
8
 
8
- export function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
9
+ export function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | ILeafPaint[]): void {
9
10
  const { strokeAlign } = ui.__
11
+ const isStrokes = typeof stroke !== 'string'
10
12
  switch (strokeAlign) {
11
13
  case 'center':
12
- canvas.setStroke(stroke, ui.__.strokeWidth, ui.__)
13
- drawTextStroke(ui, canvas)
14
+ canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__)
15
+ isStrokes ? drawStrokesStyle(ui, stroke as ILeafPaint[], canvas, true) : drawTextStroke(ui, canvas)
14
16
  break
15
17
  case 'inside':
16
- drawAlignStroke(ui, canvas, stroke, 'inside')
18
+ drawAlignStroke(ui, canvas, stroke, 'inside', isStrokes)
17
19
  break
18
20
  case 'outside':
19
- drawAlignStroke(ui, canvas, stroke, 'outside')
21
+ drawAlignStroke(ui, canvas, stroke, 'outside', isStrokes)
20
22
  break
21
23
  }
22
24
  }
23
25
 
24
- function drawAlignStroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object, align: IStrokeAlign): void {
26
+ function drawAlignStroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | ILeafPaint[], align: IStrokeAlign, isStrokes?: boolean): void {
25
27
  const { strokeWidth, __font } = ui.__
26
28
 
27
29
  const out = canvas.getSameCanvas(true)
28
- out.setStroke(stroke, strokeWidth * 2, ui.__)
30
+ out.setStroke(isStrokes ? undefined : stroke, strokeWidth * 2, ui.__)
29
31
 
30
32
  out.font = __font
31
- drawTextStroke(ui, out)
33
+ isStrokes ? drawStrokesStyle(ui, stroke as ILeafPaint[], out, true) : drawTextStroke(ui, out)
32
34
 
33
35
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in'
34
- drawText(ui, out)
36
+ fillText(ui, out)
35
37
  out.blendMode = 'normal'
36
38
 
37
39
  canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
38
40
  out.recycle()
39
41
  }
40
42
 
41
- export function strokesText(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
42
- const { strokeAlign } = ui.__
43
- switch (strokeAlign) {
44
- case 'center':
45
- canvas.setStroke(undefined, ui.__.strokeWidth, ui.__)
46
- drawStrokesStyle(ui, strokes, canvas)
47
- break
48
- case 'inside':
49
- drawAlignStroke(ui, canvas, strokes, 'inside')
50
- break
51
- case 'outside':
52
- drawAlignStrokes(ui, canvas, strokes, 'outside')
53
- break
54
- }
55
- }
56
-
57
-
58
- function drawAlignStrokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[], align: IStrokeAlign): void {
59
- const { strokeWidth, __font } = ui.__
60
-
61
- const out = canvas.getSameCanvas(true)
62
- out.setStroke(undefined, strokeWidth * 2, ui.__)
63
-
64
- out.font = __font
65
- drawStrokesStyle(ui, strokes, out)
66
-
67
- out.blendMode = align === 'outside' ? 'destination - out' : 'destination -in '
68
- drawText(ui, out)
69
- out.blendMode = 'normal'
70
-
71
- canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
72
- out.recycle()
73
- }
74
-
75
-
76
- function drawStrokesStyle(ui: IUI, strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
77
- strokes.forEach((item: ILeafStrokePaint) => {
78
- canvas.strokeStyle = item.style
79
-
80
- if (item.blendMode) {
81
- canvas.saveBlendMode(item.blendMode)
82
- drawTextStroke(ui, canvas)
83
- canvas.restoreBlendMode()
84
- } else {
85
- drawTextStroke(ui, canvas)
86
- }
87
-
88
- })
89
- }
90
-
91
43
  export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
92
44
 
93
45
  let row: ITextRowData
@@ -108,3 +60,25 @@ export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
108
60
  }
109
61
 
110
62
  }
63
+
64
+ export function drawStrokesStyle(ui: IUI, strokes: ILeafStrokePaint[], canvas: ILeaferCanvas, isText?: boolean): void {
65
+ let item: ILeafStrokePaint
66
+ for (let i = 0, len = strokes.length; i < len; i++) {
67
+ item = strokes[i]
68
+
69
+ if (item.image && checkImage(ui, canvas, item, false)) continue
70
+
71
+ if (item.style) {
72
+
73
+ canvas.strokeStyle = item.style
74
+
75
+ if (item.blendMode) {
76
+ canvas.saveBlendMode(item.blendMode)
77
+ isText ? drawTextStroke(ui, canvas) : canvas.stroke()
78
+ canvas.restoreBlendMode()
79
+ } else {
80
+ isText ? drawTextStroke(ui, canvas) : canvas.stroke()
81
+ }
82
+ }
83
+ }
84
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { fill, fills } from './Fill'
2
- export { drawText } from './FillText'
2
+ export { fillText } from './FillText'
3
3
  export { stroke, strokes } from './Stroke'
4
- export { strokeText, strokesText, drawTextStroke } from './StrokeText'
4
+ export { strokeText, drawTextStroke } from './StrokeText'
5
5
  export { shape } from './Shape'
6
- export { computeFill, computeStroke } from './Compute'
6
+ export { compute } from './Compute'
7
+ export { recycleImage } from './paint/image'
@@ -0,0 +1,49 @@
1
+
2
+ import { ILeaferCanvas } from '@leafer/interface'
3
+ import { ImageManager } from '@leafer/core'
4
+
5
+ import { IUI, ILeafPaint } from '@leafer-ui/interface'
6
+
7
+ import { createPattern } from './pattern'
8
+
9
+
10
+ export function checkImage(ui: IUI, canvas: ILeaferCanvas, paint: ILeafPaint, allowPaint?: boolean): boolean {
11
+ const { scaleX, scaleY } = ui.__world
12
+
13
+ if (!paint.data || paint.patternId === scaleX + '-' + scaleY) {
14
+ return false
15
+ } else {
16
+
17
+ if (allowPaint) {
18
+ if (paint.image.isSVG && paint.data.mode !== 'repeat') {
19
+ let { width, height } = paint.data
20
+ width *= scaleX * canvas.pixelRatio
21
+ height *= scaleY * canvas.pixelRatio
22
+ allowPaint = width > 4096 || height > 4096
23
+ } else {
24
+ allowPaint = false
25
+ }
26
+ }
27
+
28
+ if (allowPaint) {
29
+ canvas.save()
30
+ canvas.clip()
31
+ const { data } = paint
32
+ if (paint.blendMode) canvas.blendMode = paint.blendMode
33
+ if (data.opacity) canvas.opacity *= data.opacity
34
+ if (data.transform) canvas.transform(data.transform)
35
+ canvas.drawImage(paint.image.view as any, 0, 0, data.width, data.height)
36
+ canvas.restore()
37
+ return true
38
+ } else {
39
+ if (!paint.style) {
40
+ createPattern(ui, paint, canvas.pixelRatio)
41
+ } else {
42
+ ImageManager.patternTasker.add(async () => {
43
+ if (canvas.bounds.hit(ui.__world) && createPattern(ui, paint, canvas.pixelRatio)) ui.forceUpdate('surface')
44
+ }, 300)
45
+ }
46
+ return false
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,43 @@
1
+ import { IBoundsData, ILeaferImage } from '@leafer/interface'
2
+ import { MatrixHelper } from '@leafer/core'
3
+
4
+ import { IImagePaint, ILeafPaint, ILeafPaintPatternData } from '@leafer-ui/interface'
5
+
6
+ import { clipMode, fillOrFitMode, repeatMode } from './mode'
7
+
8
+
9
+ const { get, translate } = MatrixHelper
10
+
11
+ export function createData(leafPaint: ILeafPaint, image: ILeaferImage, paint: IImagePaint, box: IBoundsData): void {
12
+ let { width, height } = image
13
+
14
+ const { opacity, mode, offset, scale, rotation, blendMode } = paint
15
+ const sameBox = box.width === width && box.height === height
16
+ if (blendMode) leafPaint.blendMode = blendMode
17
+
18
+ const data: ILeafPaintPatternData = leafPaint.data = { mode }
19
+
20
+ switch (mode) {
21
+ case 'strench':
22
+ if (!sameBox) width = box.width, height = box.height
23
+ if (box.x || box.y) {
24
+ data.transform = get()
25
+ translate(data.transform, box.x, box.y)
26
+ }
27
+ break
28
+ case 'clip':
29
+ if (offset || scale || rotation) clipMode(data, box, offset, scale, rotation)
30
+ break
31
+ case 'repeat':
32
+ if (!sameBox || scale || rotation) repeatMode(data, box, width, height, scale as number, rotation)
33
+ break
34
+ case 'fit':
35
+ case 'cover':
36
+ default:
37
+ if (!sameBox || rotation) fillOrFitMode(data, mode, box, width, height, rotation)
38
+ }
39
+
40
+ data.width = width
41
+ data.height = height
42
+ if (opacity) data.opacity = opacity
43
+ }
@@ -0,0 +1,77 @@
1
+ import { IBoundsData, IImageEvent, ISizeData } from '@leafer/interface'
2
+ import { ImageEvent, ImageManager } from '@leafer/core'
3
+
4
+ import { IUI, IImagePaint, ILeafPaint } from '@leafer-ui/interface'
5
+
6
+ import { createData } from './data'
7
+
8
+
9
+ export function image(ui: IUI, attrName: string, attrValue: IImagePaint, box: IBoundsData, firstUse: boolean): ILeafPaint {
10
+ const leafPaint: ILeafPaint = { type: attrValue.type }
11
+ const image = leafPaint.image = ImageManager.get(attrValue)
12
+
13
+ const event: IImageEvent = (firstUse || image.loading) && { target: ui, image, attrName, attrValue }
14
+
15
+ if (image.ready) {
16
+
17
+ if (hasNaturalSize(ui, attrName, image)) createData(leafPaint, image, attrValue, box)
18
+
19
+ if (firstUse) {
20
+ emit(ImageEvent.LOAD, event)
21
+ emit(ImageEvent.LOADED, event)
22
+ }
23
+
24
+ } else if (image.error) {
25
+
26
+ if (firstUse) {
27
+ ui.forceUpdate('surface')
28
+ event.error = image.error
29
+ emit(ImageEvent.ERROR, event)
30
+ }
31
+
32
+ } else {
33
+
34
+ if (firstUse) emit(ImageEvent.LOAD, event)
35
+
36
+ leafPaint.loadId = image.load(
37
+ () => {
38
+ if (!ui.destroyed) {
39
+
40
+ if (hasNaturalSize(ui, attrName, image)) {
41
+ createData(leafPaint, image, attrValue, box)
42
+ ui.forceUpdate('surface')
43
+ }
44
+
45
+ emit(ImageEvent.LOADED, event)
46
+ }
47
+ },
48
+ (error) => {
49
+ ui.forceUpdate('surface')
50
+ event.error = error
51
+ emit(ImageEvent.ERROR, event)
52
+ }
53
+ )
54
+
55
+ }
56
+
57
+ return leafPaint
58
+ }
59
+
60
+
61
+ function hasNaturalSize(ui: IUI, attrName: string, image: ISizeData): boolean {
62
+ if (attrName === 'fill' && !ui.__.__naturalWidth) {
63
+ const { __: d } = ui
64
+ d.__naturalWidth = image.width
65
+ d.__naturalHeight = image.height
66
+ if (!d.__getInput('width') || !d.__getInput('height')) {
67
+ ui.forceUpdate('width')
68
+ return false
69
+ }
70
+ }
71
+ return true
72
+ }
73
+
74
+ function emit(type: string, data: IImageEvent): void {
75
+ if (data.target.hasEvent(type)) data.target.emitEvent(new ImageEvent(type, data))
76
+ }
77
+
@@ -0,0 +1,4 @@
1
+ export { image } from './image'
2
+ export { checkImage } from './check'
3
+ export { recycleImage } from './recycle'
4
+
@@ -0,0 +1,62 @@
1
+ import { IBoundsData } from '@leafer/interface'
2
+ import { MatrixHelper } from '@leafer/core'
3
+
4
+ import { IMatrixData, IImagePaintMode, IPointData, ILeafPaintPatternData } from '@leafer-ui/interface'
5
+
6
+
7
+ const { get, rotateOfOuter, translate, scaleOfOuter, scale: scaleHelper, rotate } = MatrixHelper
8
+
9
+ export function fillOrFitMode(data: ILeafPaintPatternData, mode: IImagePaintMode, box: IBoundsData, width: number, height: number, rotation: number): void {
10
+ const transform: IMatrixData = get()
11
+ const swap = rotation && rotation !== 180
12
+ const sw = box.width / (swap ? height : width)
13
+ const sh = box.height / (swap ? width : height)
14
+ const scale = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh)
15
+ const x = box.x + (box.width - width * scale) / 2
16
+ const y = box.y + (box.height - height * scale) / 2
17
+ translate(transform, x, y)
18
+ scaleHelper(transform, scale)
19
+ if (rotation) rotateOfOuter(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation)
20
+ data.scaleX = data.scaleY = scale
21
+ data.transform = transform
22
+ }
23
+
24
+
25
+ export function clipMode(data: ILeafPaintPatternData, box: IBoundsData, offset: IPointData, scale: number | IPointData, rotation: number): void {
26
+ const transform: IMatrixData = get()
27
+ translate(transform, box.x, box.y)
28
+ if (offset) translate(transform, offset.x, offset.y)
29
+ if (scale) {
30
+ typeof scale === 'number' ? scaleHelper(transform, scale) : scaleHelper(transform, scale.x, scale.y)
31
+ data.scaleX = transform.a
32
+ data.scaleY = transform.d
33
+ }
34
+ if (rotation) rotate(transform, rotation)
35
+ data.transform = transform
36
+ }
37
+
38
+
39
+ export function repeatMode(data: ILeafPaintPatternData, box: IBoundsData, width: number, height: number, scale: number, rotation: number): void {
40
+ const transform = get()
41
+
42
+ if (rotation) {
43
+ rotate(transform, rotation)
44
+ switch (rotation) {
45
+ case 90:
46
+ translate(transform, height, 0)
47
+ break
48
+ case 180:
49
+ translate(transform, width, height)
50
+ break
51
+ case 270:
52
+ translate(transform, 0, width)
53
+ break
54
+ }
55
+ }
56
+ translate(transform, box.x, box.y)
57
+ if (scale) {
58
+ scaleOfOuter(transform, box, scale)
59
+ data.scaleX = data.scaleY = scale
60
+ }
61
+ data.transform = transform
62
+ }
@@ -0,0 +1,82 @@
1
+ import { Platform, MatrixHelper } from '@leafer/core'
2
+
3
+ import { IUI, ILeafPaint, IMatrixData } from '@leafer-ui/interface'
4
+
5
+
6
+ const { get, scale: scaleHelper, copy } = MatrixHelper
7
+
8
+ export function createPattern(ui: IUI, paint: ILeafPaint, pixelRatio: number): boolean {
9
+
10
+ let { scaleX, scaleY } = ui.__world
11
+
12
+ const id = scaleX + '-' + scaleY
13
+
14
+ if (paint.patternId !== id && !ui.destroyed) {
15
+
16
+ paint.patternId = id
17
+
18
+ scaleX = Math.abs(scaleX) // maybe -1
19
+ scaleY = Math.abs(scaleY)
20
+
21
+ const { image, data } = paint
22
+ const maxWidth = image.isSVG ? 4096 : Math.min(image.width, 4096)
23
+ const maxHeight = image.isSVG ? 4096 : Math.min(image.height, 4096)
24
+ let scale: number, matrix: IMatrixData, { width, height, scaleX: sx, scaleY: sy, opacity, transform, mode } = data
25
+
26
+ if (sx) {
27
+ matrix = get()
28
+ copy(matrix, transform)
29
+ scaleHelper(matrix, 1 / sx, 1 / sy)
30
+ scaleX *= sx
31
+ scaleY *= sy
32
+ }
33
+
34
+ scaleX *= pixelRatio
35
+ scaleY *= pixelRatio
36
+ width *= scaleX
37
+ height *= scaleY
38
+
39
+ if (width > maxWidth || height > maxHeight) {
40
+ scale = Math.max(width / maxWidth, height / maxHeight)
41
+ }
42
+
43
+ if (scale) {
44
+ scaleX /= scale
45
+ scaleY /= scale
46
+ width /= scale
47
+ height /= scale
48
+ }
49
+
50
+ if (sx) {
51
+ scaleX /= sx
52
+ scaleY /= sy
53
+ }
54
+
55
+ if (transform || scaleX !== 1 || scaleY !== 1) {
56
+ if (!matrix) {
57
+ matrix = get()
58
+ if (transform) copy(matrix, transform)
59
+ }
60
+ scaleHelper(matrix, 1 / scaleX, 1 / scaleY)
61
+ }
62
+
63
+ const style = Platform.canvas.createPattern(image.getCanvas(width < 1 ? 1 : width, height < 1 ? 1 : height, opacity) as any, mode === 'repeat' ? 'repeat' : (Platform.origin.noRepeat || 'no-repeat'))
64
+
65
+ try {
66
+ if (paint.transform) paint.transform = null
67
+ if (matrix) style.setTransform ? style.setTransform(matrix) : paint.transform = matrix
68
+ } catch {
69
+ paint.transform = matrix
70
+ }
71
+
72
+ paint.style = style
73
+
74
+ return true
75
+
76
+ } else {
77
+
78
+ return false
79
+
80
+ }
81
+
82
+ }
@@ -0,0 +1,41 @@
1
+ import { IBooleanMap, ILeaferImage } from '@leafer/interface'
2
+ import { ImageManager } from '@leafer/core'
3
+
4
+ import { IImagePaint, ILeafPaint, IUIData } from '@leafer-ui/interface'
5
+
6
+
7
+ export function recycleImage(data: IUIData, attrName: string): IBooleanMap {
8
+ const paints = (attrName === 'fill' ? data._fill : data._stroke) as ILeafPaint[]
9
+
10
+ if (paints instanceof Array) {
11
+
12
+ let image: ILeaferImage, recycleMap: IBooleanMap, input: IImagePaint[], url: string
13
+
14
+ for (let i = 0, len = paints.length; i < len; i++) {
15
+
16
+ image = paints[i].image
17
+ url = image && image.url
18
+
19
+ if (url) {
20
+ if (!recycleMap) recycleMap = {}
21
+ recycleMap[url] = true
22
+ ImageManager.recycle(image)
23
+
24
+ // stop load
25
+ if (image.loading) {
26
+ if (!input) {
27
+ input = (data.__input && data.__input[attrName]) || []
28
+ if (!(input instanceof Array)) input = [input]
29
+ }
30
+ image.unload(paints[i].loadId, !input.some((item: IImagePaint) => item.url === url))
31
+ }
32
+ }
33
+
34
+ }
35
+
36
+ return recycleMap
37
+
38
+ }
39
+
40
+ return null
41
+ }
@@ -2,7 +2,7 @@ import { IObject, IBoundsData } from '@leafer/interface'
2
2
  import { Platform } from '@leafer/core'
3
3
 
4
4
  import { IGradientPaint, ILeafPaint, IColorStop } from '@leafer-ui/interface'
5
- import { ColorConvert } from '@leafer-ui/color'
5
+ import { ColorConvert } from '@leafer-ui/core'
6
6
 
7
7
 
8
8
  const defaultFrom = { x: 0.5, y: 0 }
@@ -0,0 +1,21 @@
1
+ import { ILeaferCanvas, IRenderOptions, IBooleanMap } from '@leafer/interface';
2
+ import { IUI, ILeafPaint, ICachedShape, IUIData } from '@leafer-ui/interface';
3
+
4
+ declare function fill(ui: IUI, canvas: ILeaferCanvas, fill: string): void;
5
+ declare function fills(ui: IUI, canvas: ILeaferCanvas, fills: ILeafPaint[]): void;
6
+
7
+ declare function fillText(ui: IUI, canvas: ILeaferCanvas): void;
8
+
9
+ declare function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string): void;
10
+ declare function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void;
11
+
12
+ declare function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | ILeafPaint[]): void;
13
+ declare function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void;
14
+
15
+ declare function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions): ICachedShape;
16
+
17
+ declare function compute(ui: IUI, attrName: string): void;
18
+
19
+ declare function recycleImage(data: IUIData, attrName: string): IBooleanMap;
20
+
21
+ export { compute, drawTextStroke, fill, fillText, fills, recycleImage, shape, stroke, strokeText, strokes };
@@ -1,136 +0,0 @@
1
- import { IBoundsData, ISizeData } from '@leafer/interface'
2
- import { Platform, MatrixHelper, ImageEvent } from '@leafer/core'
3
-
4
- import { IUI, IImagePaint, ILeafPaint, IMatrixData, IImagePaintMode, IPointData } from '@leafer-ui/interface'
5
-
6
-
7
- const { get, rotateOfOuter, translate, scaleOfOuter, scale: scaleHelper, rotate } = MatrixHelper
8
-
9
- export function image(ui: IUI, attrName: string, paint: IImagePaint, box: IBoundsData): ILeafPaint {
10
- const { type, blendMode } = paint
11
- let leaferPaint: ILeafPaint = { type, style: 'rgba(255,255,255,0)' }
12
- if (blendMode) leaferPaint.blendMode = blendMode
13
-
14
- const { imageManager } = ui.leafer
15
- const image = imageManager.get(paint)
16
-
17
- if (image.ready) {
18
-
19
- if (hasSize(ui, attrName, image)) {
20
-
21
- let transform: IMatrixData
22
- let { opacity, mode, offset, scale, rotation } = paint
23
- let { width, height } = image
24
- const sameBox = box.width === width && box.height === height
25
-
26
- switch (mode) {
27
- case 'strench':
28
- if (!sameBox) width = box.width, height = box.height
29
- if (box.x || box.y) {
30
- transform = get()
31
- translate(transform, box.x, box.y)
32
- }
33
- break
34
- case 'clip':
35
- if (offset || scale || rotation) transform = getClipTransform(box, offset, scale, rotation)
36
- break
37
- case 'repeat':
38
- if (!sameBox || scale || rotation) transform = getRepeatTransform(box, width, height, scale as number, rotation)
39
- break
40
- case 'fit':
41
- case 'cover':
42
- default:
43
- if (!sameBox || rotation) transform = getFillOrFitTransform(mode, box, width, height, rotation)
44
- }
45
-
46
- createPattern(leaferPaint, image.getCanvas(width, height, opacity), transform, mode === 'repeat')
47
-
48
- }
49
-
50
- } else {
51
-
52
- imageManager.load(image,
53
- () => {
54
- if (ui.leafer) {
55
- if (hasSize(ui, attrName, image)) ui.forceUpdate('width')
56
- if (ui.hasEvent(ImageEvent.LOADED)) ui.emitEvent(new ImageEvent(ImageEvent.LOADED, ui, image, attrName, paint))
57
- }
58
- },
59
- (error) => {
60
- if (ui.hasEvent(ImageEvent.ERROR)) ui.emitEvent(new ImageEvent(ImageEvent.ERROR, ui, image, attrName, paint, error))
61
- }
62
- )
63
-
64
- }
65
-
66
- return leaferPaint
67
- }
68
-
69
- function hasSize(ui: IUI, attrName: string, image: ISizeData): boolean {
70
- const { __: data } = ui
71
-
72
- if (attrName === 'fill' && data) {
73
- if (!data.__naturalWidth || !data.__naturalHeight) {
74
- data.__naturalWidth = image.width
75
- data.__naturalHeight = image.height
76
- if (!data.__getInput('width') || !data.__getInput('height')) {
77
- ui.forceUpdate('width')
78
- return false
79
- }
80
- }
81
- }
82
-
83
- return true
84
- }
85
-
86
- function getFillOrFitTransform(mode: IImagePaintMode, box: IBoundsData, width: number, height: number, rotation: number): IMatrixData {
87
- const transform: IMatrixData = get()
88
- const swap = rotation && rotation !== 180
89
- const sw = box.width / (swap ? height : width)
90
- const sh = box.height / (swap ? width : height)
91
- const scale = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh)
92
- const x = box.x + (box.width - width * scale) / 2
93
- const y = box.y + (box.height - height * scale) / 2
94
- translate(transform, x, y)
95
- scaleHelper(transform, scale)
96
- if (rotation) rotateOfOuter(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation)
97
- return transform
98
- }
99
-
100
- function getClipTransform(box: IBoundsData, offset: IPointData, scale: number | IPointData, rotation: number): IMatrixData {
101
- const transform: IMatrixData = get()
102
- translate(transform, box.x, box.y)
103
- if (offset) translate(transform, offset.x, offset.y)
104
- if (scale) typeof scale === 'number' ? scaleHelper(transform, scale) : scaleHelper(transform, scale.x, scale.y)
105
- if (rotation) rotate(transform, rotation)
106
- return transform
107
- }
108
-
109
- function getRepeatTransform(box: IBoundsData, width: number, height: number, scale: number, rotation: number): IMatrixData {
110
- const transform = get()
111
-
112
- if (rotation) {
113
- rotate(transform, rotation)
114
- switch (rotation) {
115
- case 90:
116
- translate(transform, height, 0)
117
- break
118
- case 180:
119
- translate(transform, width, height)
120
- break
121
- case 270:
122
- translate(transform, 0, width)
123
- break
124
- }
125
- }
126
- translate(transform, box.x, box.y)
127
- if (scale) scaleOfOuter(transform, box, scale)
128
- return transform
129
- }
130
-
131
-
132
- function createPattern(paint: ILeafPaint, canvas: any, transform?: IMatrixData, repeat?: boolean): void {
133
- let style = Platform.canvas.createPattern(canvas, repeat ? 'repeat' : 'no-repeat')
134
- if (transform) style.setTransform ? style.setTransform(transform) : paint.transform = transform
135
- paint.style = style
136
- }