@leafer-draw/node 1.6.2 → 1.6.3

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/dist/node.esm.js CHANGED
@@ -1,9 +1,10 @@
1
- import { LeaferCanvasBase, Platform, canvasPatch, FileHelper, Creator, LeaferImage, defineKey, LeafList, DataHelper, RenderEvent, ChildEvent, WatchEvent, PropertyEvent, LeafHelper, BranchHelper, LeafBoundsHelper, Bounds, Debug, LeafLevelList, LayoutEvent, Run, ImageManager, ResizeEvent, BoundsHelper, MatrixHelper, MathHelper, AlignHelper, ImageEvent, AroundHelper, PointHelper, Direction4 } from '@leafer/core';
1
+ import { LeaferCanvasBase, Platform, canvasPatch, FileHelper, Creator, LeaferImage, defineKey, LeafList, DataHelper, RenderEvent, ChildEvent, WatchEvent, PropertyEvent, LeafHelper, BranchHelper, LeafBoundsHelper, Bounds, Debug, LeafLevelList, LayoutEvent, Run, ImageManager, ResizeEvent, BoundsHelper, MatrixHelper, MathHelper, AlignHelper, PointHelper, ImageEvent, AroundHelper, Direction4 } from '@leafer/core';
2
2
  export * from '@leafer/core';
3
3
  export { LeaferImage } from '@leafer/core';
4
4
  import { writeFileSync } from 'fs';
5
- import { PaintImage, ColorConvert, PaintGradient, Export, Group, TextConvert, Paint, Effect, TwoPointBoundsHelper, Bounds as Bounds$1, FileHelper as FileHelper$1, Platform as Platform$1, Matrix, MathHelper as MathHelper$1, Creator as Creator$1, TaskProcessor, Resource, LeaferCanvasBase as LeaferCanvasBase$1, Debug as Debug$1, Plugin, UI } from '@leafer-ui/draw';
5
+ import { PaintImage, ColorConvert, PaintGradient, Export, Group, TextConvert, Paint as Paint$1, Effect, TwoPointBoundsHelper, Bounds as Bounds$1, FileHelper as FileHelper$1, Platform as Platform$2, Matrix, MathHelper as MathHelper$1, Creator as Creator$1, TaskProcessor, Resource, LeaferCanvasBase as LeaferCanvasBase$1, Debug as Debug$1, Plugin, UI } from '@leafer-ui/draw';
6
6
  export * from '@leafer-ui/draw';
7
+ import { Platform as Platform$1 } from '@leafer-ui/core';
7
8
 
8
9
  /******************************************************************************
9
10
  Copyright (c) Microsoft Corporation.
@@ -174,17 +175,15 @@ class Watcher {
174
175
  this.target.emitEvent(new WatchEvent(WatchEvent.DATA, { updatedList: this.updatedList }));
175
176
  this.__updatedList = new LeafList();
176
177
  this.totalTimes++;
177
- this.changed = false;
178
- this.hasVisible = false;
179
- this.hasRemove = false;
180
- this.hasAdd = false;
178
+ this.changed = this.hasVisible = this.hasRemove = this.hasAdd = false;
181
179
  }
182
180
  __listenEvents() {
183
- const { target } = this;
184
181
  this.__eventIds = [
185
- target.on_(PropertyEvent.CHANGE, this.__onAttrChange, this),
186
- target.on_([ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this),
187
- target.on_(WatchEvent.REQUEST, this.__onRquestData, this)
182
+ this.target.on_([
183
+ [PropertyEvent.CHANGE, this.__onAttrChange, this],
184
+ [[ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this],
185
+ [WatchEvent.REQUEST, this.__onRquestData, this]
186
+ ])
188
187
  ];
189
188
  }
190
189
  __removeListenEvents() {
@@ -194,8 +193,7 @@ class Watcher {
194
193
  if (this.target) {
195
194
  this.stop();
196
195
  this.__removeListenEvents();
197
- this.target = null;
198
- this.__updatedList = null;
196
+ this.target = this.__updatedList = null;
199
197
  }
200
198
  }
201
199
  }
@@ -300,7 +298,7 @@ class Layouter {
300
298
  this.disabled = true;
301
299
  }
302
300
  layout() {
303
- if (!this.running)
301
+ if (this.layouting || !this.running)
304
302
  return;
305
303
  const { target } = this;
306
304
  this.times = 0;
@@ -383,12 +381,10 @@ class Layouter {
383
381
  }
384
382
  static fullLayout(target) {
385
383
  updateAllMatrix(target, true);
386
- if (target.isBranch) {
384
+ if (target.isBranch)
387
385
  BranchHelper.updateBounds(target);
388
- }
389
- else {
386
+ else
390
387
  LeafHelper.updateBounds(target);
391
- }
392
388
  updateAllChange(target);
393
389
  }
394
390
  addExtra(leaf) {
@@ -411,11 +407,12 @@ class Layouter {
411
407
  this.__updatedList = event.data.updatedList;
412
408
  }
413
409
  __listenEvents() {
414
- const { target } = this;
415
410
  this.__eventIds = [
416
- target.on_(LayoutEvent.REQUEST, this.layout, this),
417
- target.on_(LayoutEvent.AGAIN, this.layoutAgain, this),
418
- target.on_(WatchEvent.DATA, this.__onReceiveWatchData, this)
411
+ this.target.on_([
412
+ [LayoutEvent.REQUEST, this.layout, this],
413
+ [LayoutEvent.AGAIN, this.layoutAgain, this],
414
+ [WatchEvent.DATA, this.__onReceiveWatchData, this]
415
+ ])
419
416
  ];
420
417
  }
421
418
  __removeListenEvents() {
@@ -646,12 +643,13 @@ class Renderer {
646
643
  this.target.emitEvent(new RenderEvent(type, this.times, bounds, options));
647
644
  }
648
645
  __listenEvents() {
649
- const { target } = this;
650
646
  this.__eventIds = [
651
- target.on_(RenderEvent.REQUEST, this.update, this),
652
- target.on_(LayoutEvent.END, this.__onLayoutEnd, this),
653
- target.on_(RenderEvent.AGAIN, this.renderAgain, this),
654
- target.on_(ResizeEvent.RESIZE, this.__onResize, this)
647
+ this.target.on_([
648
+ [RenderEvent.REQUEST, this.update, this],
649
+ [LayoutEvent.END, this.__onLayoutEnd, this],
650
+ [RenderEvent.AGAIN, this.renderAgain, this],
651
+ [ResizeEvent.RESIZE, this.__onResize, this]
652
+ ])
655
653
  ];
656
654
  }
657
655
  __removeListenEvents() {
@@ -737,32 +735,34 @@ function fillPathOrText(ui, canvas) {
737
735
  ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill());
738
736
  }
739
737
 
738
+ const Paint = {};
739
+
740
740
  function strokeText(stroke, ui, canvas) {
741
- const { strokeAlign } = ui.__;
742
- const isStrokes = typeof stroke !== 'string';
743
- switch (strokeAlign) {
741
+ switch (ui.__.strokeAlign) {
744
742
  case 'center':
745
- canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
746
- isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
743
+ drawCenter$1(stroke, 1, ui, canvas);
747
744
  break;
748
745
  case 'inside':
749
- drawAlignStroke('inside', stroke, isStrokes, ui, canvas);
746
+ drawAlign(stroke, 'inside', ui, canvas);
750
747
  break;
751
748
  case 'outside':
752
- drawAlignStroke('outside', stroke, isStrokes, ui, canvas);
749
+ ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, 'outside', ui, canvas);
753
750
  break;
754
751
  }
755
752
  }
756
- function drawAlignStroke(align, stroke, isStrokes, ui, canvas) {
757
- const { __strokeWidth, __font } = ui.__;
753
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
754
+ const data = ui.__;
755
+ canvas.setStroke(!data.__isStrokes && stroke, data.strokeWidth * strokeWidthScale, data);
756
+ data.__isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
757
+ }
758
+ function drawAlign(stroke, align, ui, canvas) {
758
759
  const out = canvas.getSameCanvas(true, true);
759
- out.setStroke(isStrokes ? undefined : stroke, __strokeWidth * 2, ui.__);
760
- out.font = __font;
761
- isStrokes ? drawStrokesStyle(stroke, true, ui, out) : drawTextStroke(ui, out);
760
+ out.font = ui.__.__font;
761
+ drawCenter$1(stroke, 2, ui, out);
762
762
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
763
763
  fillText(ui, out);
764
764
  out.blendMode = 'normal';
765
- if (ui.__worldFlipped)
765
+ if (ui.__worldFlipped || Platform$1.fullImageShadow)
766
766
  canvas.copyWorldByReset(out, ui.__nowWorld);
767
767
  else
768
768
  canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
@@ -804,90 +804,60 @@ function drawStrokesStyle(strokes, isText, ui, canvas) {
804
804
  }
805
805
 
806
806
  function stroke(stroke, ui, canvas) {
807
- const options = ui.__;
808
- const { __strokeWidth, strokeAlign, __font } = options;
809
- if (!__strokeWidth)
807
+ const data = ui.__;
808
+ if (!data.__strokeWidth)
810
809
  return;
811
- if (__font) {
810
+ if (data.__font) {
812
811
  strokeText(stroke, ui, canvas);
813
812
  }
814
813
  else {
815
- switch (strokeAlign) {
814
+ switch (data.strokeAlign) {
816
815
  case 'center':
817
- canvas.setStroke(stroke, __strokeWidth, options);
818
- canvas.stroke();
819
- if (options.__useArrow)
820
- strokeArrow(ui, canvas);
816
+ drawCenter(stroke, 1, ui, canvas);
821
817
  break;
822
818
  case 'inside':
823
- canvas.save();
824
- canvas.setStroke(stroke, __strokeWidth * 2, options);
825
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
826
- canvas.stroke();
827
- canvas.restore();
819
+ drawInside(stroke, ui, canvas);
828
820
  break;
829
821
  case 'outside':
830
- const out = canvas.getSameCanvas(true, true);
831
- out.setStroke(stroke, __strokeWidth * 2, options);
832
- ui.__drawRenderPath(out);
833
- out.stroke();
834
- options.windingRule ? out.clip(options.windingRule) : out.clip();
835
- out.clearWorld(ui.__layout.renderBounds);
836
- if (ui.__worldFlipped)
837
- canvas.copyWorldByReset(out, ui.__nowWorld);
838
- else
839
- canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
840
- out.recycle(ui.__nowWorld);
822
+ drawOutside(stroke, ui, canvas);
841
823
  break;
842
824
  }
843
825
  }
844
826
  }
845
827
  function strokes(strokes, ui, canvas) {
846
- const options = ui.__;
847
- const { __strokeWidth, strokeAlign, __font } = options;
848
- if (!__strokeWidth)
849
- return;
850
- if (__font) {
851
- strokeText(strokes, ui, canvas);
828
+ stroke(strokes, ui, canvas);
829
+ }
830
+ function drawCenter(stroke, strokeWidthScale, ui, canvas) {
831
+ const data = ui.__;
832
+ canvas.setStroke(!data.__isStrokes && stroke, data.__strokeWidth * strokeWidthScale, data);
833
+ data.__isStrokes ? drawStrokesStyle(stroke, false, ui, canvas) : canvas.stroke();
834
+ if (data.__useArrow)
835
+ Paint.strokeArrow(stroke, ui, canvas);
836
+ }
837
+ function drawInside(stroke, ui, canvas) {
838
+ const data = ui.__;
839
+ canvas.save();
840
+ data.windingRule ? canvas.clip(data.windingRule) : canvas.clip();
841
+ drawCenter(stroke, 2, ui, canvas);
842
+ canvas.restore();
843
+ }
844
+ function drawOutside(stroke, ui, canvas) {
845
+ const data = ui.__;
846
+ if (data.__fillAfterStroke) {
847
+ drawCenter(stroke, 2, ui, canvas);
852
848
  }
853
849
  else {
854
- switch (strokeAlign) {
855
- case 'center':
856
- canvas.setStroke(undefined, __strokeWidth, options);
857
- drawStrokesStyle(strokes, false, ui, canvas);
858
- if (options.__useArrow)
859
- strokeArrow(ui, canvas);
860
- break;
861
- case 'inside':
862
- canvas.save();
863
- canvas.setStroke(undefined, __strokeWidth * 2, options);
864
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
865
- drawStrokesStyle(strokes, false, ui, canvas);
866
- canvas.restore();
867
- break;
868
- case 'outside':
869
- const { renderBounds } = ui.__layout;
870
- const out = canvas.getSameCanvas(true, true);
871
- ui.__drawRenderPath(out);
872
- out.setStroke(undefined, __strokeWidth * 2, options);
873
- drawStrokesStyle(strokes, false, ui, out);
874
- options.windingRule ? out.clip(options.windingRule) : out.clip();
875
- out.clearWorld(renderBounds);
876
- if (ui.__worldFlipped)
877
- canvas.copyWorldByReset(out, ui.__nowWorld);
878
- else
879
- canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
880
- out.recycle(ui.__nowWorld);
881
- break;
882
- }
883
- }
884
- }
885
- function strokeArrow(ui, canvas) {
886
- if (ui.__.dashPattern) {
887
- canvas.beginPath();
888
- ui.__drawPathByData(canvas, ui.__.__pathForArrow);
889
- canvas.dashPattern = null;
890
- canvas.stroke();
850
+ const { renderBounds } = ui.__layout;
851
+ const out = canvas.getSameCanvas(true, true);
852
+ ui.__drawRenderPath(out);
853
+ drawCenter(stroke, 2, ui, out);
854
+ data.windingRule ? out.clip(data.windingRule) : out.clip();
855
+ out.clearWorld(renderBounds);
856
+ if (ui.__worldFlipped || Platform$1.fullImageShadow)
857
+ canvas.copyWorldByReset(out, ui.__nowWorld);
858
+ else
859
+ canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
860
+ out.recycle(ui.__nowWorld);
891
861
  }
892
862
  }
893
863
 
@@ -934,9 +904,10 @@ function shape(ui, current, options) {
934
904
  }
935
905
 
936
906
  let recycleMap;
907
+ const { stintSet } = DataHelper, { hasTransparent: hasTransparent$1 } = ColorConvert;
937
908
  function compute(attrName, ui) {
938
909
  const data = ui.__, leafPaints = [];
939
- let paints = data.__input[attrName], hasOpacityPixel;
910
+ let paints = data.__input[attrName], isAlphaPixel, isTransparent;
940
911
  if (!(paints instanceof Array))
941
912
  paints = [paints];
942
913
  recycleMap = PaintImage.recycleImage(attrName, data);
@@ -946,29 +917,55 @@ function compute(attrName, ui) {
946
917
  leafPaints.push(item);
947
918
  }
948
919
  data['_' + attrName] = leafPaints.length ? leafPaints : undefined;
949
- if (leafPaints.length && leafPaints[0].image)
950
- hasOpacityPixel = leafPaints[0].image.hasOpacityPixel;
951
- attrName === 'fill' ? data.__pixelFill = hasOpacityPixel : data.__pixelStroke = hasOpacityPixel;
920
+ if (leafPaints.length) {
921
+ if (leafPaints.every(item => item.isTransparent)) {
922
+ if (leafPaints.some(item => item.image))
923
+ isAlphaPixel = true;
924
+ isTransparent = true;
925
+ }
926
+ }
927
+ if (attrName === 'fill') {
928
+ stintSet(data, '__isAlphaPixelFill', isAlphaPixel);
929
+ stintSet(data, '__isTransparentFill', isTransparent);
930
+ }
931
+ else {
932
+ stintSet(data, '__isAlphaPixelStroke', isAlphaPixel);
933
+ stintSet(data, '__isTransparentStroke', isTransparent);
934
+ }
952
935
  }
953
936
  function getLeafPaint(attrName, paint, ui) {
954
937
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
955
938
  return undefined;
939
+ let data;
956
940
  const { boxBounds } = ui.__layout;
957
941
  switch (paint.type) {
958
- case 'solid':
959
- let { type, blendMode, color, opacity } = paint;
960
- return { type, blendMode, style: ColorConvert.string(color, opacity) };
961
942
  case 'image':
962
- return PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
943
+ data = PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
944
+ break;
963
945
  case 'linear':
964
- return PaintGradient.linearGradient(paint, boxBounds);
946
+ data = PaintGradient.linearGradient(paint, boxBounds);
947
+ break;
965
948
  case 'radial':
966
- return PaintGradient.radialGradient(paint, boxBounds);
949
+ data = PaintGradient.radialGradient(paint, boxBounds);
950
+ break;
967
951
  case 'angular':
968
- return PaintGradient.conicGradient(paint, boxBounds);
952
+ data = PaintGradient.conicGradient(paint, boxBounds);
953
+ break;
954
+ case 'solid':
955
+ const { type, blendMode, color, opacity } = paint;
956
+ data = { type, blendMode, style: ColorConvert.string(color, opacity) };
957
+ break;
969
958
  default:
970
- return paint.r !== undefined ? { type: 'solid', style: ColorConvert.string(paint) } : undefined;
959
+ if (paint.r !== undefined)
960
+ data = { type: 'solid', style: ColorConvert.string(paint) };
961
+ }
962
+ if (data) {
963
+ if (typeof data.style === 'string' && hasTransparent$1(data.style))
964
+ data.isTransparent = true;
965
+ if (paint.blendMode)
966
+ data.blendMode = paint.blendMode;
971
967
  }
968
+ return data;
972
969
  }
973
970
 
974
971
  const PaintModule = {
@@ -1034,12 +1031,10 @@ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, al
1034
1031
 
1035
1032
  const { get: get$2, translate } = MatrixHelper;
1036
1033
  const tempBox = new Bounds();
1037
- const tempPoint = {};
1038
1034
  const tempScaleData = {};
1035
+ const tempImage = {};
1039
1036
  function createData(leafPaint, image, paint, box) {
1040
- const { blendMode, changeful, sync } = paint;
1041
- if (blendMode)
1042
- leafPaint.blendMode = blendMode;
1037
+ const { changeful, sync } = paint;
1043
1038
  if (changeful)
1044
1039
  leafPaint.changeful = changeful;
1045
1040
  if (sync)
@@ -1047,38 +1042,38 @@ function createData(leafPaint, image, paint, box) {
1047
1042
  leafPaint.data = getPatternData(paint, box, image);
1048
1043
  }
1049
1044
  function getPatternData(paint, box, image) {
1050
- let { width, height } = image;
1051
1045
  if (paint.padding)
1052
1046
  box = tempBox.set(box).shrink(paint.padding);
1053
1047
  if (paint.mode === 'strench')
1054
1048
  paint.mode = 'stretch';
1049
+ let { width, height } = image;
1055
1050
  const { opacity, mode, align, offset, scale, size, rotation, repeat, filters } = paint;
1056
1051
  const sameBox = box.width === width && box.height === height;
1057
1052
  const data = { mode };
1058
1053
  const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1059
- const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1060
- let x = 0, y = 0, scaleX, scaleY;
1054
+ BoundsHelper.set(tempImage, 0, 0, swapSize ? height : width, swapSize ? width : height);
1055
+ let scaleX, scaleY;
1061
1056
  if (!mode || mode === 'cover' || mode === 'fit') {
1062
1057
  if (!sameBox || rotation) {
1063
- const sw = box.width / swapWidth, sh = box.height / swapHeight;
1064
- scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1065
- x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1058
+ scaleX = scaleY = BoundsHelper.getFitScale(box, tempImage, mode !== 'fit');
1059
+ BoundsHelper.put(box, image, align, scaleX, false, tempImage);
1060
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1066
1061
  }
1067
1062
  }
1068
- else if (scale || size) {
1069
- MathHelper.getScaleData(scale, size, image, tempScaleData);
1070
- scaleX = tempScaleData.scaleX;
1071
- scaleY = tempScaleData.scaleY;
1072
- }
1073
- if (align) {
1074
- const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1075
- if (scaleX)
1076
- imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1077
- AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1078
- x += tempPoint.x, y += tempPoint.y;
1063
+ else {
1064
+ if (scale || size) {
1065
+ MathHelper.getScaleData(scale, size, image, tempScaleData);
1066
+ scaleX = tempScaleData.scaleX;
1067
+ scaleY = tempScaleData.scaleY;
1068
+ }
1069
+ if (align) {
1070
+ if (scaleX)
1071
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1072
+ AlignHelper.toPoint(align, tempImage, box, tempImage, true, true);
1073
+ }
1079
1074
  }
1080
1075
  if (offset)
1081
- x += offset.x, y += offset.y;
1076
+ PointHelper.move(tempImage, offset);
1082
1077
  switch (mode) {
1083
1078
  case 'stretch':
1084
1079
  if (!sameBox)
@@ -1086,12 +1081,12 @@ function getPatternData(paint, box, image) {
1086
1081
  break;
1087
1082
  case 'normal':
1088
1083
  case 'clip':
1089
- if (x || y || scaleX || rotation)
1090
- clipMode(data, box, x, y, scaleX, scaleY, rotation);
1084
+ if (tempImage.x || tempImage.y || scaleX || rotation)
1085
+ clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1091
1086
  break;
1092
1087
  case 'repeat':
1093
1088
  if (!sameBox || scaleX || rotation)
1094
- repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1089
+ repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, align);
1095
1090
  if (!repeat)
1096
1091
  data.repeat = 'repeat';
1097
1092
  break;
@@ -1099,7 +1094,7 @@ function getPatternData(paint, box, image) {
1099
1094
  case 'cover':
1100
1095
  default:
1101
1096
  if (scaleX)
1102
- fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1097
+ fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1103
1098
  }
1104
1099
  if (!data.transform) {
1105
1100
  if (box.x || box.y) {
@@ -1132,6 +1127,8 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1132
1127
  }
1133
1128
  else {
1134
1129
  leafPaint = { type: paint.type, image };
1130
+ if (image.hasAlphaPixel)
1131
+ leafPaint.isTransparent = true;
1135
1132
  cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1136
1133
  }
1137
1134
  if (firstUse || image.loading)
@@ -1156,7 +1153,7 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1156
1153
  ignoreRender(ui, false);
1157
1154
  if (!ui.destroyed) {
1158
1155
  if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1159
- if (image.hasOpacityPixel)
1156
+ if (image.hasAlphaPixel)
1160
1157
  ui.__layout.hitCanvasChanged = true;
1161
1158
  ui.forceUpdate('surface');
1162
1159
  }
@@ -1168,13 +1165,17 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1168
1165
  onLoadError(ui, event, error);
1169
1166
  leafPaint.loadId = null;
1170
1167
  });
1171
- if (ui.placeholderColor)
1172
- setTimeout(() => {
1173
- if (!(image.ready || image.isPlacehold)) {
1174
- image.isPlacehold = true;
1175
- ui.forceUpdate('surface');
1176
- }
1177
- }, 100);
1168
+ if (ui.placeholderColor) {
1169
+ if (!ui.placeholderDelay)
1170
+ image.isPlacehold = true;
1171
+ else
1172
+ setTimeout(() => {
1173
+ if (!image.ready) {
1174
+ image.isPlacehold = true;
1175
+ ui.forceUpdate('surface');
1176
+ }
1177
+ }, ui.placeholderDelay);
1178
+ }
1178
1179
  }
1179
1180
  return leafPaint;
1180
1181
  }
@@ -1380,32 +1381,33 @@ const PaintImageModule = {
1380
1381
  repeatMode
1381
1382
  };
1382
1383
 
1383
- const { toPoint: toPoint$2 } = AroundHelper;
1384
+ const { toPoint: toPoint$2 } = AroundHelper, { hasTransparent } = ColorConvert;
1384
1385
  const realFrom$2 = {};
1385
1386
  const realTo$2 = {};
1386
1387
  function linearGradient(paint, box) {
1387
- let { from, to, type, blendMode, opacity } = paint;
1388
+ let { from, to, type, opacity } = paint;
1388
1389
  toPoint$2(from || 'top', box, realFrom$2);
1389
1390
  toPoint$2(to || 'bottom', box, realTo$2);
1390
1391
  const style = Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
1391
- applyStops(style, paint.stops, opacity);
1392
1392
  const data = { type, style };
1393
- if (blendMode)
1394
- data.blendMode = blendMode;
1393
+ applyStops(data, style, paint.stops, opacity);
1395
1394
  return data;
1396
1395
  }
1397
- function applyStops(gradient, stops, opacity) {
1396
+ function applyStops(data, gradient, stops, opacity) {
1398
1397
  if (stops) {
1399
- let stop;
1398
+ let stop, color, offset, isTransparent;
1400
1399
  for (let i = 0, len = stops.length; i < len; i++) {
1401
1400
  stop = stops[i];
1402
- if (typeof stop === 'string') {
1403
- gradient.addColorStop(i / (len - 1), ColorConvert.string(stop, opacity));
1404
- }
1405
- else {
1406
- gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity));
1407
- }
1401
+ if (typeof stop === 'string')
1402
+ offset = i / (len - 1), color = ColorConvert.string(stop, opacity);
1403
+ else
1404
+ offset = stop.offset, color = ColorConvert.string(stop.color, opacity);
1405
+ gradient.addColorStop(offset, color);
1406
+ if (!isTransparent && hasTransparent(color))
1407
+ isTransparent = true;
1408
1408
  }
1409
+ if (isTransparent)
1410
+ data.isTransparent = true;
1409
1411
  }
1410
1412
  }
1411
1413
 
@@ -1415,17 +1417,15 @@ const { toPoint: toPoint$1 } = AroundHelper;
1415
1417
  const realFrom$1 = {};
1416
1418
  const realTo$1 = {};
1417
1419
  function radialGradient(paint, box) {
1418
- let { from, to, type, opacity, blendMode, stretch } = paint;
1420
+ let { from, to, type, opacity, stretch } = paint;
1419
1421
  toPoint$1(from || 'center', box, realFrom$1);
1420
1422
  toPoint$1(to || 'bottom', box, realTo$1);
1421
1423
  const style = Platform.canvas.createRadialGradient(realFrom$1.x, realFrom$1.y, 0, realFrom$1.x, realFrom$1.y, getDistance$1(realFrom$1, realTo$1));
1422
- applyStops(style, paint.stops, opacity);
1423
1424
  const data = { type, style };
1425
+ applyStops(data, style, paint.stops, opacity);
1424
1426
  const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
1425
1427
  if (transform)
1426
1428
  data.transform = transform;
1427
- if (blendMode)
1428
- data.blendMode = blendMode;
1429
1429
  return data;
1430
1430
  }
1431
1431
  function getTransform(box, from, to, stretch, rotate90) {
@@ -1451,17 +1451,15 @@ const { toPoint } = AroundHelper;
1451
1451
  const realFrom = {};
1452
1452
  const realTo = {};
1453
1453
  function conicGradient(paint, box) {
1454
- let { from, to, type, opacity, blendMode, stretch } = paint;
1454
+ let { from, to, type, opacity, stretch } = paint;
1455
1455
  toPoint(from || 'center', box, realFrom);
1456
1456
  toPoint(to || 'bottom', box, realTo);
1457
1457
  const style = Platform.conicGradientSupport ? Platform.canvas.createConicGradient(0, realFrom.x, realFrom.y) : Platform.canvas.createRadialGradient(realFrom.x, realFrom.y, 0, realFrom.x, realFrom.y, getDistance(realFrom, realTo));
1458
- applyStops(style, paint.stops, opacity);
1459
1458
  const data = { type, style };
1459
+ applyStops(data, style, paint.stops, opacity);
1460
1460
  const transform = getTransform(box, realFrom, realTo, stretch || 1, Platform.conicGradientRotate90);
1461
1461
  if (transform)
1462
1462
  data.transform = transform;
1463
- if (blendMode)
1464
- data.blendMode = blendMode;
1465
1463
  return data;
1466
1464
  }
1467
1465
 
@@ -1794,6 +1792,8 @@ function createRows(drawData, content, style) {
1794
1792
  lastCharType = null;
1795
1793
  startCharSize = charWidth = charSize = wordWidth = rowWidth = 0;
1796
1794
  word = { data: [] }, row = { words: [] };
1795
+ if (__letterSpacing)
1796
+ content = [...content];
1797
1797
  for (let i = 0, len = content.length; i < len; i++) {
1798
1798
  char = content[i];
1799
1799
  if (char === '\n') {
@@ -2200,7 +2200,7 @@ const ColorConvertModule = {
2200
2200
 
2201
2201
  Object.assign(TextConvert, TextConvertModule);
2202
2202
  Object.assign(ColorConvert, ColorConvertModule);
2203
- Object.assign(Paint, PaintModule);
2203
+ Object.assign(Paint$1, PaintModule);
2204
2204
  Object.assign(PaintImage, PaintImageModule);
2205
2205
  Object.assign(PaintGradient, PaintGradientModule);
2206
2206
  Object.assign(Effect, EffectModule);
@@ -2230,8 +2230,8 @@ const ExportModule = {
2230
2230
  const fileType = FileHelper$1.fileType(filename);
2231
2231
  const isDownload = filename.includes('.');
2232
2232
  options = FileHelper$1.getExportOptions(options);
2233
- const { toURL } = Platform$1;
2234
- const { download } = Platform$1.origin;
2233
+ const { toURL } = Platform$2;
2234
+ const { download } = Platform$2.origin;
2235
2235
  if (fileType === 'json') {
2236
2236
  isDownload && download(toURL(JSON.stringify(leaf.toJSON(options.json)), 'text'), filename);
2237
2237
  result = { data: isDownload ? true : leaf.toJSON(options.json) };
@@ -2243,10 +2243,9 @@ const ExportModule = {
2243
2243
  else {
2244
2244
  let renderBounds, trimBounds, scaleX = 1, scaleY = 1;
2245
2245
  const { worldTransform, isLeafer, leafer, isFrame } = leaf;
2246
- const { slice, clip, trim, padding, onCanvas } = options;
2246
+ const { slice, clip, trim, screenshot, padding, onCanvas } = options;
2247
2247
  const smooth = options.smooth === undefined ? (leafer ? leafer.config.smooth : true) : options.smooth;
2248
2248
  const contextSettings = options.contextSettings || (leafer ? leafer.config.contextSettings : undefined);
2249
- const screenshot = options.screenshot || leaf.isApp;
2250
2249
  const fill = (isLeafer && screenshot) ? (options.fill === undefined ? leaf.fill : options.fill) : options.fill;
2251
2250
  const needFill = FileHelper$1.isOpaqueImage(filename) || fill, matrix = new Matrix();
2252
2251
  if (screenshot) {
@@ -2282,11 +2281,6 @@ const ExportModule = {
2282
2281
  const scaleData = { scaleX: 1, scaleY: 1 };
2283
2282
  MathHelper$1.getScaleData(options.scale, options.size, renderBounds, scaleData);
2284
2283
  let pixelRatio = options.pixelRatio || 1;
2285
- if (leaf.isApp) {
2286
- scaleData.scaleX *= pixelRatio;
2287
- scaleData.scaleY *= pixelRatio;
2288
- pixelRatio = leaf.app.pixelRatio;
2289
- }
2290
2284
  let { x, y, width, height } = new Bounds$1(renderBounds).scale(scaleData.scaleX, scaleData.scaleY);
2291
2285
  if (clip)
2292
2286
  x += clip.x, y += clip.y, width = clip.width, height = clip.height;
@@ -2340,7 +2334,7 @@ const ExportModule = {
2340
2334
  return addTask((success) => new Promise((resolve) => {
2341
2335
  const getResult = () => __awaiter(this, void 0, void 0, function* () {
2342
2336
  if (!Resource.isComplete)
2343
- return Platform$1.requestRender(getResult);
2337
+ return Platform$2.requestRender(getResult);
2344
2338
  const result = ExportModule.syncExport(leaf, filename, options);
2345
2339
  if (result.data instanceof Promise)
2346
2340
  result.data = yield result.data;
@@ -2385,7 +2379,7 @@ canvas.export = function (filename, options) {
2385
2379
  };
2386
2380
  canvas.toBlob = function (type, quality) {
2387
2381
  return new Promise((resolve) => {
2388
- Platform$1.origin.canvasToBolb(this.view, type, quality).then((blob) => {
2382
+ Platform$2.origin.canvasToBolb(this.view, type, quality).then((blob) => {
2389
2383
  resolve(blob);
2390
2384
  }).catch((e) => {
2391
2385
  debug.error(e);
@@ -2394,11 +2388,11 @@ canvas.toBlob = function (type, quality) {
2394
2388
  });
2395
2389
  };
2396
2390
  canvas.toDataURL = function (type, quality) {
2397
- return Platform$1.origin.canvasToDataURL(this.view, type, quality);
2391
+ return Platform$2.origin.canvasToDataURL(this.view, type, quality);
2398
2392
  };
2399
2393
  canvas.saveAs = function (filename, quality) {
2400
2394
  return new Promise((resolve) => {
2401
- Platform$1.origin.canvasSaveAs(this.view, filename, quality).then(() => {
2395
+ Platform$2.origin.canvasSaveAs(this.view, filename, quality).then(() => {
2402
2396
  resolve(true);
2403
2397
  }).catch((e) => {
2404
2398
  debug.error(e);