@leafer-draw/worker 1.6.2 → 1.6.4

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.
@@ -1,7 +1,7 @@
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
- import { PaintImage, ColorConvert, PaintGradient, Export, Group, TextConvert, Paint, Effect } from '@leafer-ui/draw';
4
+ import { PaintImage, Paint, ColorConvert, PaintGradient, Export, Group, TextConvert, Effect } from '@leafer-ui/draw';
5
5
  export * from '@leafer-ui/draw';
6
6
 
7
7
  class LeaferCanvas extends LeaferCanvasBase {
@@ -161,17 +161,15 @@ class Watcher {
161
161
  this.target.emitEvent(new WatchEvent(WatchEvent.DATA, { updatedList: this.updatedList }));
162
162
  this.__updatedList = new LeafList();
163
163
  this.totalTimes++;
164
- this.changed = false;
165
- this.hasVisible = false;
166
- this.hasRemove = false;
167
- this.hasAdd = false;
164
+ this.changed = this.hasVisible = this.hasRemove = this.hasAdd = false;
168
165
  }
169
166
  __listenEvents() {
170
- const { target } = this;
171
167
  this.__eventIds = [
172
- target.on_(PropertyEvent.CHANGE, this.__onAttrChange, this),
173
- target.on_([ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this),
174
- target.on_(WatchEvent.REQUEST, this.__onRquestData, this)
168
+ this.target.on_([
169
+ [PropertyEvent.CHANGE, this.__onAttrChange, this],
170
+ [[ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this],
171
+ [WatchEvent.REQUEST, this.__onRquestData, this]
172
+ ])
175
173
  ];
176
174
  }
177
175
  __removeListenEvents() {
@@ -181,8 +179,7 @@ class Watcher {
181
179
  if (this.target) {
182
180
  this.stop();
183
181
  this.__removeListenEvents();
184
- this.target = null;
185
- this.__updatedList = null;
182
+ this.target = this.__updatedList = null;
186
183
  }
187
184
  }
188
185
  }
@@ -287,7 +284,7 @@ class Layouter {
287
284
  this.disabled = true;
288
285
  }
289
286
  layout() {
290
- if (!this.running)
287
+ if (this.layouting || !this.running)
291
288
  return;
292
289
  const { target } = this;
293
290
  this.times = 0;
@@ -370,12 +367,10 @@ class Layouter {
370
367
  }
371
368
  static fullLayout(target) {
372
369
  updateAllMatrix(target, true);
373
- if (target.isBranch) {
370
+ if (target.isBranch)
374
371
  BranchHelper.updateBounds(target);
375
- }
376
- else {
372
+ else
377
373
  LeafHelper.updateBounds(target);
378
- }
379
374
  updateAllChange(target);
380
375
  }
381
376
  addExtra(leaf) {
@@ -398,11 +393,12 @@ class Layouter {
398
393
  this.__updatedList = event.data.updatedList;
399
394
  }
400
395
  __listenEvents() {
401
- const { target } = this;
402
396
  this.__eventIds = [
403
- target.on_(LayoutEvent.REQUEST, this.layout, this),
404
- target.on_(LayoutEvent.AGAIN, this.layoutAgain, this),
405
- target.on_(WatchEvent.DATA, this.__onReceiveWatchData, this)
397
+ this.target.on_([
398
+ [LayoutEvent.REQUEST, this.layout, this],
399
+ [LayoutEvent.AGAIN, this.layoutAgain, this],
400
+ [WatchEvent.DATA, this.__onReceiveWatchData, this]
401
+ ])
406
402
  ];
407
403
  }
408
404
  __removeListenEvents() {
@@ -633,12 +629,13 @@ class Renderer {
633
629
  this.target.emitEvent(new RenderEvent(type, this.times, bounds, options));
634
630
  }
635
631
  __listenEvents() {
636
- const { target } = this;
637
632
  this.__eventIds = [
638
- target.on_(RenderEvent.REQUEST, this.update, this),
639
- target.on_(LayoutEvent.END, this.__onLayoutEnd, this),
640
- target.on_(RenderEvent.AGAIN, this.renderAgain, this),
641
- target.on_(ResizeEvent.RESIZE, this.__onResize, this)
633
+ this.target.on_([
634
+ [RenderEvent.REQUEST, this.update, this],
635
+ [LayoutEvent.END, this.__onLayoutEnd, this],
636
+ [RenderEvent.AGAIN, this.renderAgain, this],
637
+ [ResizeEvent.RESIZE, this.__onResize, this]
638
+ ])
642
639
  ];
643
640
  }
644
641
  __removeListenEvents() {
@@ -725,35 +722,38 @@ function fillPathOrText(ui, canvas) {
725
722
  }
726
723
 
727
724
  function strokeText(stroke, ui, canvas) {
728
- const { strokeAlign } = ui.__;
729
- const isStrokes = typeof stroke !== 'string';
730
- switch (strokeAlign) {
725
+ switch (ui.__.strokeAlign) {
731
726
  case 'center':
732
- canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
733
- isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
727
+ drawCenter$1(stroke, 1, ui, canvas);
734
728
  break;
735
729
  case 'inside':
736
- drawAlignStroke('inside', stroke, isStrokes, ui, canvas);
730
+ drawAlign(stroke, 'inside', ui, canvas);
737
731
  break;
738
732
  case 'outside':
739
- drawAlignStroke('outside', stroke, isStrokes, ui, canvas);
733
+ ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, 'outside', ui, canvas);
740
734
  break;
741
735
  }
742
736
  }
743
- function drawAlignStroke(align, stroke, isStrokes, ui, canvas) {
744
- const { __strokeWidth, __font } = ui.__;
737
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
738
+ const data = ui.__;
739
+ canvas.setStroke(!data.__isStrokes && stroke, data.strokeWidth * strokeWidthScale, data);
740
+ data.__isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
741
+ }
742
+ function drawAlign(stroke, align, ui, canvas) {
745
743
  const out = canvas.getSameCanvas(true, true);
746
- out.setStroke(isStrokes ? undefined : stroke, __strokeWidth * 2, ui.__);
747
- out.font = __font;
748
- isStrokes ? drawStrokesStyle(stroke, true, ui, out) : drawTextStroke(ui, out);
744
+ out.font = ui.__.__font;
745
+ drawCenter$1(stroke, 2, ui, out);
749
746
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
750
747
  fillText(ui, out);
751
748
  out.blendMode = 'normal';
752
- if (ui.__worldFlipped)
749
+ copyWorld(canvas, out, ui);
750
+ out.recycle(ui.__nowWorld);
751
+ }
752
+ function copyWorld(canvas, out, ui) {
753
+ if (ui.__worldFlipped || Platform.fullImageShadow)
753
754
  canvas.copyWorldByReset(out, ui.__nowWorld);
754
755
  else
755
756
  canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
756
- out.recycle(ui.__nowWorld);
757
757
  }
758
758
  function drawTextStroke(ui, canvas) {
759
759
  let row, data = ui.__.__textDrawData;
@@ -791,90 +791,56 @@ function drawStrokesStyle(strokes, isText, ui, canvas) {
791
791
  }
792
792
 
793
793
  function stroke(stroke, ui, canvas) {
794
- const options = ui.__;
795
- const { __strokeWidth, strokeAlign, __font } = options;
796
- if (!__strokeWidth)
794
+ const data = ui.__;
795
+ if (!data.__strokeWidth)
797
796
  return;
798
- if (__font) {
797
+ if (data.__font) {
799
798
  strokeText(stroke, ui, canvas);
800
799
  }
801
800
  else {
802
- switch (strokeAlign) {
801
+ switch (data.strokeAlign) {
803
802
  case 'center':
804
- canvas.setStroke(stroke, __strokeWidth, options);
805
- canvas.stroke();
806
- if (options.__useArrow)
807
- strokeArrow(ui, canvas);
803
+ drawCenter(stroke, 1, ui, canvas);
808
804
  break;
809
805
  case 'inside':
810
- canvas.save();
811
- canvas.setStroke(stroke, __strokeWidth * 2, options);
812
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
813
- canvas.stroke();
814
- canvas.restore();
806
+ drawInside(stroke, ui, canvas);
815
807
  break;
816
808
  case 'outside':
817
- const out = canvas.getSameCanvas(true, true);
818
- out.setStroke(stroke, __strokeWidth * 2, options);
819
- ui.__drawRenderPath(out);
820
- out.stroke();
821
- options.windingRule ? out.clip(options.windingRule) : out.clip();
822
- out.clearWorld(ui.__layout.renderBounds);
823
- if (ui.__worldFlipped)
824
- canvas.copyWorldByReset(out, ui.__nowWorld);
825
- else
826
- canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
827
- out.recycle(ui.__nowWorld);
809
+ drawOutside(stroke, ui, canvas);
828
810
  break;
829
811
  }
830
812
  }
831
813
  }
832
814
  function strokes(strokes, ui, canvas) {
833
- const options = ui.__;
834
- const { __strokeWidth, strokeAlign, __font } = options;
835
- if (!__strokeWidth)
836
- return;
837
- if (__font) {
838
- strokeText(strokes, ui, canvas);
815
+ stroke(strokes, ui, canvas);
816
+ }
817
+ function drawCenter(stroke, strokeWidthScale, ui, canvas) {
818
+ const data = ui.__;
819
+ canvas.setStroke(!data.__isStrokes && stroke, data.__strokeWidth * strokeWidthScale, data);
820
+ data.__isStrokes ? drawStrokesStyle(stroke, false, ui, canvas) : canvas.stroke();
821
+ if (data.__useArrow)
822
+ Paint.strokeArrow(stroke, ui, canvas);
823
+ }
824
+ function drawInside(stroke, ui, canvas) {
825
+ canvas.save();
826
+ canvas.clipUI(ui);
827
+ drawCenter(stroke, 2, ui, canvas);
828
+ canvas.restore();
829
+ }
830
+ function drawOutside(stroke, ui, canvas) {
831
+ const data = ui.__;
832
+ if (data.__fillAfterStroke) {
833
+ drawCenter(stroke, 2, ui, canvas);
839
834
  }
840
835
  else {
841
- switch (strokeAlign) {
842
- case 'center':
843
- canvas.setStroke(undefined, __strokeWidth, options);
844
- drawStrokesStyle(strokes, false, ui, canvas);
845
- if (options.__useArrow)
846
- strokeArrow(ui, canvas);
847
- break;
848
- case 'inside':
849
- canvas.save();
850
- canvas.setStroke(undefined, __strokeWidth * 2, options);
851
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
852
- drawStrokesStyle(strokes, false, ui, canvas);
853
- canvas.restore();
854
- break;
855
- case 'outside':
856
- const { renderBounds } = ui.__layout;
857
- const out = canvas.getSameCanvas(true, true);
858
- ui.__drawRenderPath(out);
859
- out.setStroke(undefined, __strokeWidth * 2, options);
860
- drawStrokesStyle(strokes, false, ui, out);
861
- options.windingRule ? out.clip(options.windingRule) : out.clip();
862
- out.clearWorld(renderBounds);
863
- if (ui.__worldFlipped)
864
- canvas.copyWorldByReset(out, ui.__nowWorld);
865
- else
866
- canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
867
- out.recycle(ui.__nowWorld);
868
- break;
869
- }
870
- }
871
- }
872
- function strokeArrow(ui, canvas) {
873
- if (ui.__.dashPattern) {
874
- canvas.beginPath();
875
- ui.__drawPathByData(canvas, ui.__.__pathForArrow);
876
- canvas.dashPattern = null;
877
- canvas.stroke();
836
+ const { renderBounds } = ui.__layout;
837
+ const out = canvas.getSameCanvas(true, true);
838
+ ui.__drawRenderPath(out);
839
+ drawCenter(stroke, 2, ui, out);
840
+ out.clipUI(data);
841
+ out.clearWorld(renderBounds);
842
+ copyWorld(canvas, out, ui);
843
+ out.recycle(ui.__nowWorld);
878
844
  }
879
845
  }
880
846
 
@@ -921,41 +887,66 @@ function shape(ui, current, options) {
921
887
  }
922
888
 
923
889
  let recycleMap;
890
+ const { stintSet } = DataHelper, { hasTransparent: hasTransparent$1 } = ColorConvert;
924
891
  function compute(attrName, ui) {
925
892
  const data = ui.__, leafPaints = [];
926
- let paints = data.__input[attrName], hasOpacityPixel;
893
+ let paints = data.__input[attrName], isAlphaPixel, isTransparent;
927
894
  if (!(paints instanceof Array))
928
895
  paints = [paints];
929
896
  recycleMap = PaintImage.recycleImage(attrName, data);
930
897
  for (let i = 0, len = paints.length, item; i < len; i++) {
931
- item = getLeafPaint(attrName, paints[i], ui);
932
- if (item)
933
- leafPaints.push(item);
898
+ (item = getLeafPaint(attrName, paints[i], ui)) && leafPaints.push(item);
934
899
  }
935
900
  data['_' + attrName] = leafPaints.length ? leafPaints : undefined;
936
- if (leafPaints.length && leafPaints[0].image)
937
- hasOpacityPixel = leafPaints[0].image.hasOpacityPixel;
938
- attrName === 'fill' ? data.__pixelFill = hasOpacityPixel : data.__pixelStroke = hasOpacityPixel;
901
+ if (leafPaints.length) {
902
+ if (leafPaints.every(item => item.isTransparent)) {
903
+ if (leafPaints.some(item => item.image))
904
+ isAlphaPixel = true;
905
+ isTransparent = true;
906
+ }
907
+ }
908
+ if (attrName === 'fill') {
909
+ stintSet(data, '__isAlphaPixelFill', isAlphaPixel);
910
+ stintSet(data, '__isTransparentFill', isTransparent);
911
+ }
912
+ else {
913
+ stintSet(data, '__isAlphaPixelStroke', isAlphaPixel);
914
+ stintSet(data, '__isTransparentStroke', isTransparent);
915
+ }
939
916
  }
940
917
  function getLeafPaint(attrName, paint, ui) {
941
918
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
942
919
  return undefined;
920
+ let data;
943
921
  const { boxBounds } = ui.__layout;
944
922
  switch (paint.type) {
945
- case 'solid':
946
- let { type, blendMode, color, opacity } = paint;
947
- return { type, blendMode, style: ColorConvert.string(color, opacity) };
948
923
  case 'image':
949
- return PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
924
+ data = PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
925
+ break;
950
926
  case 'linear':
951
- return PaintGradient.linearGradient(paint, boxBounds);
927
+ data = PaintGradient.linearGradient(paint, boxBounds);
928
+ break;
952
929
  case 'radial':
953
- return PaintGradient.radialGradient(paint, boxBounds);
930
+ data = PaintGradient.radialGradient(paint, boxBounds);
931
+ break;
954
932
  case 'angular':
955
- return PaintGradient.conicGradient(paint, boxBounds);
933
+ data = PaintGradient.conicGradient(paint, boxBounds);
934
+ break;
935
+ case 'solid':
936
+ const { type, color, opacity } = paint;
937
+ data = { type, style: ColorConvert.string(color, opacity) };
938
+ break;
956
939
  default:
957
- return paint.r !== undefined ? { type: 'solid', style: ColorConvert.string(paint) } : undefined;
940
+ if (paint.r !== undefined)
941
+ data = { type: 'solid', style: ColorConvert.string(paint) };
942
+ }
943
+ if (data) {
944
+ if (typeof data.style === 'string' && hasTransparent$1(data.style))
945
+ data.isTransparent = true;
946
+ if (paint.blendMode)
947
+ data.blendMode = paint.blendMode;
958
948
  }
949
+ return data;
959
950
  }
960
951
 
961
952
  const PaintModule = {
@@ -1021,12 +1012,10 @@ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, al
1021
1012
 
1022
1013
  const { get: get$2, translate } = MatrixHelper;
1023
1014
  const tempBox = new Bounds();
1024
- const tempPoint = {};
1025
1015
  const tempScaleData = {};
1016
+ const tempImage = {};
1026
1017
  function createData(leafPaint, image, paint, box) {
1027
- const { blendMode, changeful, sync } = paint;
1028
- if (blendMode)
1029
- leafPaint.blendMode = blendMode;
1018
+ const { changeful, sync } = paint;
1030
1019
  if (changeful)
1031
1020
  leafPaint.changeful = changeful;
1032
1021
  if (sync)
@@ -1034,38 +1023,38 @@ function createData(leafPaint, image, paint, box) {
1034
1023
  leafPaint.data = getPatternData(paint, box, image);
1035
1024
  }
1036
1025
  function getPatternData(paint, box, image) {
1037
- let { width, height } = image;
1038
1026
  if (paint.padding)
1039
1027
  box = tempBox.set(box).shrink(paint.padding);
1040
1028
  if (paint.mode === 'strench')
1041
1029
  paint.mode = 'stretch';
1030
+ let { width, height } = image;
1042
1031
  const { opacity, mode, align, offset, scale, size, rotation, repeat, filters } = paint;
1043
1032
  const sameBox = box.width === width && box.height === height;
1044
1033
  const data = { mode };
1045
1034
  const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1046
- const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1047
- let x = 0, y = 0, scaleX, scaleY;
1035
+ BoundsHelper.set(tempImage, 0, 0, swapSize ? height : width, swapSize ? width : height);
1036
+ let scaleX, scaleY;
1048
1037
  if (!mode || mode === 'cover' || mode === 'fit') {
1049
1038
  if (!sameBox || rotation) {
1050
- const sw = box.width / swapWidth, sh = box.height / swapHeight;
1051
- scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1052
- x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1039
+ scaleX = scaleY = BoundsHelper.getFitScale(box, tempImage, mode !== 'fit');
1040
+ BoundsHelper.put(box, image, align, scaleX, false, tempImage);
1041
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1053
1042
  }
1054
1043
  }
1055
- else if (scale || size) {
1056
- MathHelper.getScaleData(scale, size, image, tempScaleData);
1057
- scaleX = tempScaleData.scaleX;
1058
- scaleY = tempScaleData.scaleY;
1059
- }
1060
- if (align) {
1061
- const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1062
- if (scaleX)
1063
- imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1064
- AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1065
- x += tempPoint.x, y += tempPoint.y;
1044
+ else {
1045
+ if (scale || size) {
1046
+ MathHelper.getScaleData(scale, size, image, tempScaleData);
1047
+ scaleX = tempScaleData.scaleX;
1048
+ scaleY = tempScaleData.scaleY;
1049
+ }
1050
+ if (align) {
1051
+ if (scaleX)
1052
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1053
+ AlignHelper.toPoint(align, tempImage, box, tempImage, true, true);
1054
+ }
1066
1055
  }
1067
1056
  if (offset)
1068
- x += offset.x, y += offset.y;
1057
+ PointHelper.move(tempImage, offset);
1069
1058
  switch (mode) {
1070
1059
  case 'stretch':
1071
1060
  if (!sameBox)
@@ -1073,12 +1062,12 @@ function getPatternData(paint, box, image) {
1073
1062
  break;
1074
1063
  case 'normal':
1075
1064
  case 'clip':
1076
- if (x || y || scaleX || rotation)
1077
- clipMode(data, box, x, y, scaleX, scaleY, rotation);
1065
+ if (tempImage.x || tempImage.y || scaleX || rotation)
1066
+ clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1078
1067
  break;
1079
1068
  case 'repeat':
1080
1069
  if (!sameBox || scaleX || rotation)
1081
- repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1070
+ repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, align);
1082
1071
  if (!repeat)
1083
1072
  data.repeat = 'repeat';
1084
1073
  break;
@@ -1086,7 +1075,7 @@ function getPatternData(paint, box, image) {
1086
1075
  case 'cover':
1087
1076
  default:
1088
1077
  if (scaleX)
1089
- fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1078
+ fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1090
1079
  }
1091
1080
  if (!data.transform) {
1092
1081
  if (box.x || box.y) {
@@ -1119,6 +1108,8 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1119
1108
  }
1120
1109
  else {
1121
1110
  leafPaint = { type: paint.type, image };
1111
+ if (image.hasAlphaPixel)
1112
+ leafPaint.isTransparent = true;
1122
1113
  cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1123
1114
  }
1124
1115
  if (firstUse || image.loading)
@@ -1143,7 +1134,7 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1143
1134
  ignoreRender(ui, false);
1144
1135
  if (!ui.destroyed) {
1145
1136
  if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1146
- if (image.hasOpacityPixel)
1137
+ if (image.hasAlphaPixel)
1147
1138
  ui.__layout.hitCanvasChanged = true;
1148
1139
  ui.forceUpdate('surface');
1149
1140
  }
@@ -1155,13 +1146,17 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1155
1146
  onLoadError(ui, event, error);
1156
1147
  leafPaint.loadId = null;
1157
1148
  });
1158
- if (ui.placeholderColor)
1159
- setTimeout(() => {
1160
- if (!(image.ready || image.isPlacehold)) {
1161
- image.isPlacehold = true;
1162
- ui.forceUpdate('surface');
1163
- }
1164
- }, 100);
1149
+ if (ui.placeholderColor) {
1150
+ if (!ui.placeholderDelay)
1151
+ image.isPlacehold = true;
1152
+ else
1153
+ setTimeout(() => {
1154
+ if (!image.ready) {
1155
+ image.isPlacehold = true;
1156
+ ui.forceUpdate('surface');
1157
+ }
1158
+ }, ui.placeholderDelay);
1159
+ }
1165
1160
  }
1166
1161
  return leafPaint;
1167
1162
  }
@@ -1348,7 +1343,7 @@ function checkImage(ui, canvas, paint, allowDraw) {
1348
1343
  }
1349
1344
  function drawImage(ui, canvas, paint, data) {
1350
1345
  canvas.save();
1351
- ui.windingRule ? canvas.clip(ui.windingRule) : canvas.clip();
1346
+ canvas.clipUI(ui);
1352
1347
  if (paint.blendMode)
1353
1348
  canvas.blendMode = paint.blendMode;
1354
1349
  if (data.opacity)
@@ -1399,32 +1394,33 @@ const PaintImageModule = {
1399
1394
  repeatMode
1400
1395
  };
1401
1396
 
1402
- const { toPoint: toPoint$2 } = AroundHelper;
1397
+ const { toPoint: toPoint$2 } = AroundHelper, { hasTransparent } = ColorConvert;
1403
1398
  const realFrom$2 = {};
1404
1399
  const realTo$2 = {};
1405
1400
  function linearGradient(paint, box) {
1406
- let { from, to, type, blendMode, opacity } = paint;
1401
+ let { from, to, type, opacity } = paint;
1407
1402
  toPoint$2(from || 'top', box, realFrom$2);
1408
1403
  toPoint$2(to || 'bottom', box, realTo$2);
1409
1404
  const style = Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
1410
- applyStops(style, paint.stops, opacity);
1411
1405
  const data = { type, style };
1412
- if (blendMode)
1413
- data.blendMode = blendMode;
1406
+ applyStops(data, style, paint.stops, opacity);
1414
1407
  return data;
1415
1408
  }
1416
- function applyStops(gradient, stops, opacity) {
1409
+ function applyStops(data, gradient, stops, opacity) {
1417
1410
  if (stops) {
1418
- let stop;
1411
+ let stop, color, offset, isTransparent;
1419
1412
  for (let i = 0, len = stops.length; i < len; i++) {
1420
1413
  stop = stops[i];
1421
- if (typeof stop === 'string') {
1422
- gradient.addColorStop(i / (len - 1), ColorConvert.string(stop, opacity));
1423
- }
1424
- else {
1425
- gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity));
1426
- }
1414
+ if (typeof stop === 'string')
1415
+ offset = i / (len - 1), color = ColorConvert.string(stop, opacity);
1416
+ else
1417
+ offset = stop.offset, color = ColorConvert.string(stop.color, opacity);
1418
+ gradient.addColorStop(offset, color);
1419
+ if (!isTransparent && hasTransparent(color))
1420
+ isTransparent = true;
1427
1421
  }
1422
+ if (isTransparent)
1423
+ data.isTransparent = true;
1428
1424
  }
1429
1425
  }
1430
1426
 
@@ -1434,17 +1430,15 @@ const { toPoint: toPoint$1 } = AroundHelper;
1434
1430
  const realFrom$1 = {};
1435
1431
  const realTo$1 = {};
1436
1432
  function radialGradient(paint, box) {
1437
- let { from, to, type, opacity, blendMode, stretch } = paint;
1433
+ let { from, to, type, opacity, stretch } = paint;
1438
1434
  toPoint$1(from || 'center', box, realFrom$1);
1439
1435
  toPoint$1(to || 'bottom', box, realTo$1);
1440
1436
  const style = Platform.canvas.createRadialGradient(realFrom$1.x, realFrom$1.y, 0, realFrom$1.x, realFrom$1.y, getDistance$1(realFrom$1, realTo$1));
1441
- applyStops(style, paint.stops, opacity);
1442
1437
  const data = { type, style };
1438
+ applyStops(data, style, paint.stops, opacity);
1443
1439
  const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
1444
1440
  if (transform)
1445
1441
  data.transform = transform;
1446
- if (blendMode)
1447
- data.blendMode = blendMode;
1448
1442
  return data;
1449
1443
  }
1450
1444
  function getTransform(box, from, to, stretch, rotate90) {
@@ -1470,17 +1464,15 @@ const { toPoint } = AroundHelper;
1470
1464
  const realFrom = {};
1471
1465
  const realTo = {};
1472
1466
  function conicGradient(paint, box) {
1473
- let { from, to, type, opacity, blendMode, stretch } = paint;
1467
+ let { from, to, type, opacity, stretch } = paint;
1474
1468
  toPoint(from || 'center', box, realFrom);
1475
1469
  toPoint(to || 'bottom', box, realTo);
1476
1470
  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));
1477
- applyStops(style, paint.stops, opacity);
1478
1471
  const data = { type, style };
1472
+ applyStops(data, style, paint.stops, opacity);
1479
1473
  const transform = getTransform(box, realFrom, realTo, stretch || 1, Platform.conicGradientRotate90);
1480
1474
  if (transform)
1481
1475
  data.transform = transform;
1482
- if (blendMode)
1483
- data.blendMode = blendMode;
1484
1476
  return data;
1485
1477
  }
1486
1478
 
@@ -1516,12 +1508,10 @@ function shadow(ui, current, shape) {
1516
1508
  }
1517
1509
  worldCanvas ? other.copyWorld(worldCanvas, nowWorld, nowWorld, 'destination-out') : other.copyWorld(shape.canvas, shapeBounds, bounds, 'destination-out');
1518
1510
  }
1519
- if (ui.__worldFlipped) {
1511
+ if (ui.__worldFlipped)
1520
1512
  current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
1521
- }
1522
- else {
1513
+ else
1523
1514
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
1524
- }
1525
1515
  if (end && index < end)
1526
1516
  other.clearWorld(copyBounds, true);
1527
1517
  });
@@ -1580,12 +1570,10 @@ function innerShadow(ui, current, shape) {
1580
1570
  copyBounds = bounds;
1581
1571
  }
1582
1572
  other.fillWorld(copyBounds, ColorConvert.string(item.color), 'source-in');
1583
- if (ui.__worldFlipped) {
1573
+ if (ui.__worldFlipped)
1584
1574
  current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
1585
- }
1586
- else {
1575
+ else
1587
1576
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
1588
- }
1589
1577
  if (end && index < end)
1590
1578
  other.clearWorld(copyBounds, true);
1591
1579
  });
@@ -1813,6 +1801,8 @@ function createRows(drawData, content, style) {
1813
1801
  lastCharType = null;
1814
1802
  startCharSize = charWidth = charSize = wordWidth = rowWidth = 0;
1815
1803
  word = { data: [] }, row = { words: [] };
1804
+ if (__letterSpacing)
1805
+ content = [...content];
1816
1806
  for (let i = 0, len = content.length; i < len; i++) {
1817
1807
  char = content[i];
1818
1808
  if (char === '\n') {