@leafer-ui/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,9 +1,9 @@
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, Plugin, 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, Plugin, 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 { HitCanvasManager, InteractionBase } from '@leafer-ui/core';
5
5
  export * from '@leafer-ui/core';
6
- import { PaintImage, ColorConvert, PaintGradient, Export, Group, TextConvert, Paint, Effect } from '@leafer-ui/draw';
6
+ import { PaintImage, Paint, ColorConvert, PaintGradient, Export, Group, TextConvert, Effect } from '@leafer-ui/draw';
7
7
 
8
8
  class LeaferCanvas extends LeaferCanvasBase {
9
9
  get allowBackgroundColor() { return true; }
@@ -162,17 +162,15 @@ class Watcher {
162
162
  this.target.emitEvent(new WatchEvent(WatchEvent.DATA, { updatedList: this.updatedList }));
163
163
  this.__updatedList = new LeafList();
164
164
  this.totalTimes++;
165
- this.changed = false;
166
- this.hasVisible = false;
167
- this.hasRemove = false;
168
- this.hasAdd = false;
165
+ this.changed = this.hasVisible = this.hasRemove = this.hasAdd = false;
169
166
  }
170
167
  __listenEvents() {
171
- const { target } = this;
172
168
  this.__eventIds = [
173
- target.on_(PropertyEvent.CHANGE, this.__onAttrChange, this),
174
- target.on_([ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this),
175
- target.on_(WatchEvent.REQUEST, this.__onRquestData, this)
169
+ this.target.on_([
170
+ [PropertyEvent.CHANGE, this.__onAttrChange, this],
171
+ [[ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this],
172
+ [WatchEvent.REQUEST, this.__onRquestData, this]
173
+ ])
176
174
  ];
177
175
  }
178
176
  __removeListenEvents() {
@@ -182,8 +180,7 @@ class Watcher {
182
180
  if (this.target) {
183
181
  this.stop();
184
182
  this.__removeListenEvents();
185
- this.target = null;
186
- this.__updatedList = null;
183
+ this.target = this.__updatedList = null;
187
184
  }
188
185
  }
189
186
  }
@@ -288,7 +285,7 @@ class Layouter {
288
285
  this.disabled = true;
289
286
  }
290
287
  layout() {
291
- if (!this.running)
288
+ if (this.layouting || !this.running)
292
289
  return;
293
290
  const { target } = this;
294
291
  this.times = 0;
@@ -371,12 +368,10 @@ class Layouter {
371
368
  }
372
369
  static fullLayout(target) {
373
370
  updateAllMatrix(target, true);
374
- if (target.isBranch) {
371
+ if (target.isBranch)
375
372
  BranchHelper.updateBounds(target);
376
- }
377
- else {
373
+ else
378
374
  LeafHelper.updateBounds(target);
379
- }
380
375
  updateAllChange(target);
381
376
  }
382
377
  addExtra(leaf) {
@@ -399,11 +394,12 @@ class Layouter {
399
394
  this.__updatedList = event.data.updatedList;
400
395
  }
401
396
  __listenEvents() {
402
- const { target } = this;
403
397
  this.__eventIds = [
404
- target.on_(LayoutEvent.REQUEST, this.layout, this),
405
- target.on_(LayoutEvent.AGAIN, this.layoutAgain, this),
406
- target.on_(WatchEvent.DATA, this.__onReceiveWatchData, this)
398
+ this.target.on_([
399
+ [LayoutEvent.REQUEST, this.layout, this],
400
+ [LayoutEvent.AGAIN, this.layoutAgain, this],
401
+ [WatchEvent.DATA, this.__onReceiveWatchData, this]
402
+ ])
407
403
  ];
408
404
  }
409
405
  __removeListenEvents() {
@@ -634,12 +630,13 @@ class Renderer {
634
630
  this.target.emitEvent(new RenderEvent(type, this.times, bounds, options));
635
631
  }
636
632
  __listenEvents() {
637
- const { target } = this;
638
633
  this.__eventIds = [
639
- target.on_(RenderEvent.REQUEST, this.update, this),
640
- target.on_(LayoutEvent.END, this.__onLayoutEnd, this),
641
- target.on_(RenderEvent.AGAIN, this.renderAgain, this),
642
- target.on_(ResizeEvent.RESIZE, this.__onResize, this)
634
+ this.target.on_([
635
+ [RenderEvent.REQUEST, this.update, this],
636
+ [LayoutEvent.END, this.__onLayoutEnd, this],
637
+ [RenderEvent.AGAIN, this.renderAgain, this],
638
+ [ResizeEvent.RESIZE, this.__onResize, this]
639
+ ])
643
640
  ];
644
641
  }
645
642
  __removeListenEvents() {
@@ -883,35 +880,38 @@ function fillPathOrText(ui, canvas) {
883
880
  }
884
881
 
885
882
  function strokeText(stroke, ui, canvas) {
886
- const { strokeAlign } = ui.__;
887
- const isStrokes = typeof stroke !== 'string';
888
- switch (strokeAlign) {
883
+ switch (ui.__.strokeAlign) {
889
884
  case 'center':
890
- canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
891
- isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
885
+ drawCenter$1(stroke, 1, ui, canvas);
892
886
  break;
893
887
  case 'inside':
894
- drawAlignStroke('inside', stroke, isStrokes, ui, canvas);
888
+ drawAlign(stroke, 'inside', ui, canvas);
895
889
  break;
896
890
  case 'outside':
897
- drawAlignStroke('outside', stroke, isStrokes, ui, canvas);
891
+ ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, 'outside', ui, canvas);
898
892
  break;
899
893
  }
900
894
  }
901
- function drawAlignStroke(align, stroke, isStrokes, ui, canvas) {
902
- const { __strokeWidth, __font } = ui.__;
895
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
896
+ const data = ui.__;
897
+ canvas.setStroke(!data.__isStrokes && stroke, data.strokeWidth * strokeWidthScale, data);
898
+ data.__isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
899
+ }
900
+ function drawAlign(stroke, align, ui, canvas) {
903
901
  const out = canvas.getSameCanvas(true, true);
904
- out.setStroke(isStrokes ? undefined : stroke, __strokeWidth * 2, ui.__);
905
- out.font = __font;
906
- isStrokes ? drawStrokesStyle(stroke, true, ui, out) : drawTextStroke(ui, out);
902
+ out.font = ui.__.__font;
903
+ drawCenter$1(stroke, 2, ui, out);
907
904
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
908
905
  fillText(ui, out);
909
906
  out.blendMode = 'normal';
910
- if (ui.__worldFlipped)
907
+ copyWorld(canvas, out, ui);
908
+ out.recycle(ui.__nowWorld);
909
+ }
910
+ function copyWorld(canvas, out, ui) {
911
+ if (ui.__worldFlipped || Platform.fullImageShadow)
911
912
  canvas.copyWorldByReset(out, ui.__nowWorld);
912
913
  else
913
914
  canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
914
- out.recycle(ui.__nowWorld);
915
915
  }
916
916
  function drawTextStroke(ui, canvas) {
917
917
  let row, data = ui.__.__textDrawData;
@@ -949,90 +949,56 @@ function drawStrokesStyle(strokes, isText, ui, canvas) {
949
949
  }
950
950
 
951
951
  function stroke(stroke, ui, canvas) {
952
- const options = ui.__;
953
- const { __strokeWidth, strokeAlign, __font } = options;
954
- if (!__strokeWidth)
952
+ const data = ui.__;
953
+ if (!data.__strokeWidth)
955
954
  return;
956
- if (__font) {
955
+ if (data.__font) {
957
956
  strokeText(stroke, ui, canvas);
958
957
  }
959
958
  else {
960
- switch (strokeAlign) {
959
+ switch (data.strokeAlign) {
961
960
  case 'center':
962
- canvas.setStroke(stroke, __strokeWidth, options);
963
- canvas.stroke();
964
- if (options.__useArrow)
965
- strokeArrow(ui, canvas);
961
+ drawCenter(stroke, 1, ui, canvas);
966
962
  break;
967
963
  case 'inside':
968
- canvas.save();
969
- canvas.setStroke(stroke, __strokeWidth * 2, options);
970
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
971
- canvas.stroke();
972
- canvas.restore();
964
+ drawInside(stroke, ui, canvas);
973
965
  break;
974
966
  case 'outside':
975
- const out = canvas.getSameCanvas(true, true);
976
- out.setStroke(stroke, __strokeWidth * 2, options);
977
- ui.__drawRenderPath(out);
978
- out.stroke();
979
- options.windingRule ? out.clip(options.windingRule) : out.clip();
980
- out.clearWorld(ui.__layout.renderBounds);
981
- if (ui.__worldFlipped)
982
- canvas.copyWorldByReset(out, ui.__nowWorld);
983
- else
984
- canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
985
- out.recycle(ui.__nowWorld);
967
+ drawOutside(stroke, ui, canvas);
986
968
  break;
987
969
  }
988
970
  }
989
971
  }
990
972
  function strokes(strokes, ui, canvas) {
991
- const options = ui.__;
992
- const { __strokeWidth, strokeAlign, __font } = options;
993
- if (!__strokeWidth)
994
- return;
995
- if (__font) {
996
- strokeText(strokes, ui, canvas);
973
+ stroke(strokes, ui, canvas);
974
+ }
975
+ function drawCenter(stroke, strokeWidthScale, ui, canvas) {
976
+ const data = ui.__;
977
+ canvas.setStroke(!data.__isStrokes && stroke, data.__strokeWidth * strokeWidthScale, data);
978
+ data.__isStrokes ? drawStrokesStyle(stroke, false, ui, canvas) : canvas.stroke();
979
+ if (data.__useArrow)
980
+ Paint.strokeArrow(stroke, ui, canvas);
981
+ }
982
+ function drawInside(stroke, ui, canvas) {
983
+ canvas.save();
984
+ canvas.clipUI(ui);
985
+ drawCenter(stroke, 2, ui, canvas);
986
+ canvas.restore();
987
+ }
988
+ function drawOutside(stroke, ui, canvas) {
989
+ const data = ui.__;
990
+ if (data.__fillAfterStroke) {
991
+ drawCenter(stroke, 2, ui, canvas);
997
992
  }
998
993
  else {
999
- switch (strokeAlign) {
1000
- case 'center':
1001
- canvas.setStroke(undefined, __strokeWidth, options);
1002
- drawStrokesStyle(strokes, false, ui, canvas);
1003
- if (options.__useArrow)
1004
- strokeArrow(ui, canvas);
1005
- break;
1006
- case 'inside':
1007
- canvas.save();
1008
- canvas.setStroke(undefined, __strokeWidth * 2, options);
1009
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1010
- drawStrokesStyle(strokes, false, ui, canvas);
1011
- canvas.restore();
1012
- break;
1013
- case 'outside':
1014
- const { renderBounds } = ui.__layout;
1015
- const out = canvas.getSameCanvas(true, true);
1016
- ui.__drawRenderPath(out);
1017
- out.setStroke(undefined, __strokeWidth * 2, options);
1018
- drawStrokesStyle(strokes, false, ui, out);
1019
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1020
- out.clearWorld(renderBounds);
1021
- if (ui.__worldFlipped)
1022
- canvas.copyWorldByReset(out, ui.__nowWorld);
1023
- else
1024
- canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
1025
- out.recycle(ui.__nowWorld);
1026
- break;
1027
- }
1028
- }
1029
- }
1030
- function strokeArrow(ui, canvas) {
1031
- if (ui.__.dashPattern) {
1032
- canvas.beginPath();
1033
- ui.__drawPathByData(canvas, ui.__.__pathForArrow);
1034
- canvas.dashPattern = null;
1035
- canvas.stroke();
994
+ const { renderBounds } = ui.__layout;
995
+ const out = canvas.getSameCanvas(true, true);
996
+ ui.__drawRenderPath(out);
997
+ drawCenter(stroke, 2, ui, out);
998
+ out.clipUI(data);
999
+ out.clearWorld(renderBounds);
1000
+ copyWorld(canvas, out, ui);
1001
+ out.recycle(ui.__nowWorld);
1036
1002
  }
1037
1003
  }
1038
1004
 
@@ -1079,41 +1045,66 @@ function shape(ui, current, options) {
1079
1045
  }
1080
1046
 
1081
1047
  let recycleMap;
1048
+ const { stintSet } = DataHelper, { hasTransparent: hasTransparent$1 } = ColorConvert;
1082
1049
  function compute(attrName, ui) {
1083
1050
  const data = ui.__, leafPaints = [];
1084
- let paints = data.__input[attrName], hasOpacityPixel;
1051
+ let paints = data.__input[attrName], isAlphaPixel, isTransparent;
1085
1052
  if (!(paints instanceof Array))
1086
1053
  paints = [paints];
1087
1054
  recycleMap = PaintImage.recycleImage(attrName, data);
1088
1055
  for (let i = 0, len = paints.length, item; i < len; i++) {
1089
- item = getLeafPaint(attrName, paints[i], ui);
1090
- if (item)
1091
- leafPaints.push(item);
1056
+ (item = getLeafPaint(attrName, paints[i], ui)) && leafPaints.push(item);
1092
1057
  }
1093
1058
  data['_' + attrName] = leafPaints.length ? leafPaints : undefined;
1094
- if (leafPaints.length && leafPaints[0].image)
1095
- hasOpacityPixel = leafPaints[0].image.hasOpacityPixel;
1096
- attrName === 'fill' ? data.__pixelFill = hasOpacityPixel : data.__pixelStroke = hasOpacityPixel;
1059
+ if (leafPaints.length) {
1060
+ if (leafPaints.every(item => item.isTransparent)) {
1061
+ if (leafPaints.some(item => item.image))
1062
+ isAlphaPixel = true;
1063
+ isTransparent = true;
1064
+ }
1065
+ }
1066
+ if (attrName === 'fill') {
1067
+ stintSet(data, '__isAlphaPixelFill', isAlphaPixel);
1068
+ stintSet(data, '__isTransparentFill', isTransparent);
1069
+ }
1070
+ else {
1071
+ stintSet(data, '__isAlphaPixelStroke', isAlphaPixel);
1072
+ stintSet(data, '__isTransparentStroke', isTransparent);
1073
+ }
1097
1074
  }
1098
1075
  function getLeafPaint(attrName, paint, ui) {
1099
1076
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
1100
1077
  return undefined;
1078
+ let data;
1101
1079
  const { boxBounds } = ui.__layout;
1102
1080
  switch (paint.type) {
1103
- case 'solid':
1104
- let { type, blendMode, color, opacity } = paint;
1105
- return { type, blendMode, style: ColorConvert.string(color, opacity) };
1106
1081
  case 'image':
1107
- return PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1082
+ data = PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1083
+ break;
1108
1084
  case 'linear':
1109
- return PaintGradient.linearGradient(paint, boxBounds);
1085
+ data = PaintGradient.linearGradient(paint, boxBounds);
1086
+ break;
1110
1087
  case 'radial':
1111
- return PaintGradient.radialGradient(paint, boxBounds);
1088
+ data = PaintGradient.radialGradient(paint, boxBounds);
1089
+ break;
1112
1090
  case 'angular':
1113
- return PaintGradient.conicGradient(paint, boxBounds);
1091
+ data = PaintGradient.conicGradient(paint, boxBounds);
1092
+ break;
1093
+ case 'solid':
1094
+ const { type, color, opacity } = paint;
1095
+ data = { type, style: ColorConvert.string(color, opacity) };
1096
+ break;
1114
1097
  default:
1115
- return paint.r !== undefined ? { type: 'solid', style: ColorConvert.string(paint) } : undefined;
1098
+ if (paint.r !== undefined)
1099
+ data = { type: 'solid', style: ColorConvert.string(paint) };
1100
+ }
1101
+ if (data) {
1102
+ if (typeof data.style === 'string' && hasTransparent$1(data.style))
1103
+ data.isTransparent = true;
1104
+ if (paint.blendMode)
1105
+ data.blendMode = paint.blendMode;
1116
1106
  }
1107
+ return data;
1117
1108
  }
1118
1109
 
1119
1110
  const PaintModule = {
@@ -1179,12 +1170,10 @@ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, al
1179
1170
 
1180
1171
  const { get: get$2, translate } = MatrixHelper;
1181
1172
  const tempBox = new Bounds();
1182
- const tempPoint = {};
1183
1173
  const tempScaleData = {};
1174
+ const tempImage = {};
1184
1175
  function createData(leafPaint, image, paint, box) {
1185
- const { blendMode, changeful, sync } = paint;
1186
- if (blendMode)
1187
- leafPaint.blendMode = blendMode;
1176
+ const { changeful, sync } = paint;
1188
1177
  if (changeful)
1189
1178
  leafPaint.changeful = changeful;
1190
1179
  if (sync)
@@ -1192,38 +1181,38 @@ function createData(leafPaint, image, paint, box) {
1192
1181
  leafPaint.data = getPatternData(paint, box, image);
1193
1182
  }
1194
1183
  function getPatternData(paint, box, image) {
1195
- let { width, height } = image;
1196
1184
  if (paint.padding)
1197
1185
  box = tempBox.set(box).shrink(paint.padding);
1198
1186
  if (paint.mode === 'strench')
1199
1187
  paint.mode = 'stretch';
1188
+ let { width, height } = image;
1200
1189
  const { opacity, mode, align, offset, scale, size, rotation, repeat, filters } = paint;
1201
1190
  const sameBox = box.width === width && box.height === height;
1202
1191
  const data = { mode };
1203
1192
  const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1204
- const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1205
- let x = 0, y = 0, scaleX, scaleY;
1193
+ BoundsHelper.set(tempImage, 0, 0, swapSize ? height : width, swapSize ? width : height);
1194
+ let scaleX, scaleY;
1206
1195
  if (!mode || mode === 'cover' || mode === 'fit') {
1207
1196
  if (!sameBox || rotation) {
1208
- const sw = box.width / swapWidth, sh = box.height / swapHeight;
1209
- scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1210
- x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1197
+ scaleX = scaleY = BoundsHelper.getFitScale(box, tempImage, mode !== 'fit');
1198
+ BoundsHelper.put(box, image, align, scaleX, false, tempImage);
1199
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1211
1200
  }
1212
1201
  }
1213
- else if (scale || size) {
1214
- MathHelper.getScaleData(scale, size, image, tempScaleData);
1215
- scaleX = tempScaleData.scaleX;
1216
- scaleY = tempScaleData.scaleY;
1217
- }
1218
- if (align) {
1219
- const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1220
- if (scaleX)
1221
- imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1222
- AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1223
- x += tempPoint.x, y += tempPoint.y;
1202
+ else {
1203
+ if (scale || size) {
1204
+ MathHelper.getScaleData(scale, size, image, tempScaleData);
1205
+ scaleX = tempScaleData.scaleX;
1206
+ scaleY = tempScaleData.scaleY;
1207
+ }
1208
+ if (align) {
1209
+ if (scaleX)
1210
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1211
+ AlignHelper.toPoint(align, tempImage, box, tempImage, true, true);
1212
+ }
1224
1213
  }
1225
1214
  if (offset)
1226
- x += offset.x, y += offset.y;
1215
+ PointHelper.move(tempImage, offset);
1227
1216
  switch (mode) {
1228
1217
  case 'stretch':
1229
1218
  if (!sameBox)
@@ -1231,12 +1220,12 @@ function getPatternData(paint, box, image) {
1231
1220
  break;
1232
1221
  case 'normal':
1233
1222
  case 'clip':
1234
- if (x || y || scaleX || rotation)
1235
- clipMode(data, box, x, y, scaleX, scaleY, rotation);
1223
+ if (tempImage.x || tempImage.y || scaleX || rotation)
1224
+ clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1236
1225
  break;
1237
1226
  case 'repeat':
1238
1227
  if (!sameBox || scaleX || rotation)
1239
- repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1228
+ repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, align);
1240
1229
  if (!repeat)
1241
1230
  data.repeat = 'repeat';
1242
1231
  break;
@@ -1244,7 +1233,7 @@ function getPatternData(paint, box, image) {
1244
1233
  case 'cover':
1245
1234
  default:
1246
1235
  if (scaleX)
1247
- fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1236
+ fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1248
1237
  }
1249
1238
  if (!data.transform) {
1250
1239
  if (box.x || box.y) {
@@ -1277,6 +1266,8 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1277
1266
  }
1278
1267
  else {
1279
1268
  leafPaint = { type: paint.type, image };
1269
+ if (image.hasAlphaPixel)
1270
+ leafPaint.isTransparent = true;
1280
1271
  cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1281
1272
  }
1282
1273
  if (firstUse || image.loading)
@@ -1301,7 +1292,7 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1301
1292
  ignoreRender(ui, false);
1302
1293
  if (!ui.destroyed) {
1303
1294
  if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1304
- if (image.hasOpacityPixel)
1295
+ if (image.hasAlphaPixel)
1305
1296
  ui.__layout.hitCanvasChanged = true;
1306
1297
  ui.forceUpdate('surface');
1307
1298
  }
@@ -1313,13 +1304,17 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1313
1304
  onLoadError(ui, event, error);
1314
1305
  leafPaint.loadId = null;
1315
1306
  });
1316
- if (ui.placeholderColor)
1317
- setTimeout(() => {
1318
- if (!(image.ready || image.isPlacehold)) {
1319
- image.isPlacehold = true;
1320
- ui.forceUpdate('surface');
1321
- }
1322
- }, 100);
1307
+ if (ui.placeholderColor) {
1308
+ if (!ui.placeholderDelay)
1309
+ image.isPlacehold = true;
1310
+ else
1311
+ setTimeout(() => {
1312
+ if (!image.ready) {
1313
+ image.isPlacehold = true;
1314
+ ui.forceUpdate('surface');
1315
+ }
1316
+ }, ui.placeholderDelay);
1317
+ }
1323
1318
  }
1324
1319
  return leafPaint;
1325
1320
  }
@@ -1506,7 +1501,7 @@ function checkImage(ui, canvas, paint, allowDraw) {
1506
1501
  }
1507
1502
  function drawImage(ui, canvas, paint, data) {
1508
1503
  canvas.save();
1509
- ui.windingRule ? canvas.clip(ui.windingRule) : canvas.clip();
1504
+ canvas.clipUI(ui);
1510
1505
  if (paint.blendMode)
1511
1506
  canvas.blendMode = paint.blendMode;
1512
1507
  if (data.opacity)
@@ -1557,32 +1552,33 @@ const PaintImageModule = {
1557
1552
  repeatMode
1558
1553
  };
1559
1554
 
1560
- const { toPoint: toPoint$2 } = AroundHelper;
1555
+ const { toPoint: toPoint$2 } = AroundHelper, { hasTransparent } = ColorConvert;
1561
1556
  const realFrom$2 = {};
1562
1557
  const realTo$2 = {};
1563
1558
  function linearGradient(paint, box) {
1564
- let { from, to, type, blendMode, opacity } = paint;
1559
+ let { from, to, type, opacity } = paint;
1565
1560
  toPoint$2(from || 'top', box, realFrom$2);
1566
1561
  toPoint$2(to || 'bottom', box, realTo$2);
1567
1562
  const style = Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
1568
- applyStops(style, paint.stops, opacity);
1569
1563
  const data = { type, style };
1570
- if (blendMode)
1571
- data.blendMode = blendMode;
1564
+ applyStops(data, style, paint.stops, opacity);
1572
1565
  return data;
1573
1566
  }
1574
- function applyStops(gradient, stops, opacity) {
1567
+ function applyStops(data, gradient, stops, opacity) {
1575
1568
  if (stops) {
1576
- let stop;
1569
+ let stop, color, offset, isTransparent;
1577
1570
  for (let i = 0, len = stops.length; i < len; i++) {
1578
1571
  stop = stops[i];
1579
- if (typeof stop === 'string') {
1580
- gradient.addColorStop(i / (len - 1), ColorConvert.string(stop, opacity));
1581
- }
1582
- else {
1583
- gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity));
1584
- }
1572
+ if (typeof stop === 'string')
1573
+ offset = i / (len - 1), color = ColorConvert.string(stop, opacity);
1574
+ else
1575
+ offset = stop.offset, color = ColorConvert.string(stop.color, opacity);
1576
+ gradient.addColorStop(offset, color);
1577
+ if (!isTransparent && hasTransparent(color))
1578
+ isTransparent = true;
1585
1579
  }
1580
+ if (isTransparent)
1581
+ data.isTransparent = true;
1586
1582
  }
1587
1583
  }
1588
1584
 
@@ -1592,17 +1588,15 @@ const { toPoint: toPoint$1 } = AroundHelper;
1592
1588
  const realFrom$1 = {};
1593
1589
  const realTo$1 = {};
1594
1590
  function radialGradient(paint, box) {
1595
- let { from, to, type, opacity, blendMode, stretch } = paint;
1591
+ let { from, to, type, opacity, stretch } = paint;
1596
1592
  toPoint$1(from || 'center', box, realFrom$1);
1597
1593
  toPoint$1(to || 'bottom', box, realTo$1);
1598
1594
  const style = Platform.canvas.createRadialGradient(realFrom$1.x, realFrom$1.y, 0, realFrom$1.x, realFrom$1.y, getDistance$1(realFrom$1, realTo$1));
1599
- applyStops(style, paint.stops, opacity);
1600
1595
  const data = { type, style };
1596
+ applyStops(data, style, paint.stops, opacity);
1601
1597
  const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
1602
1598
  if (transform)
1603
1599
  data.transform = transform;
1604
- if (blendMode)
1605
- data.blendMode = blendMode;
1606
1600
  return data;
1607
1601
  }
1608
1602
  function getTransform(box, from, to, stretch, rotate90) {
@@ -1628,17 +1622,15 @@ const { toPoint } = AroundHelper;
1628
1622
  const realFrom = {};
1629
1623
  const realTo = {};
1630
1624
  function conicGradient(paint, box) {
1631
- let { from, to, type, opacity, blendMode, stretch } = paint;
1625
+ let { from, to, type, opacity, stretch } = paint;
1632
1626
  toPoint(from || 'center', box, realFrom);
1633
1627
  toPoint(to || 'bottom', box, realTo);
1634
1628
  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));
1635
- applyStops(style, paint.stops, opacity);
1636
1629
  const data = { type, style };
1630
+ applyStops(data, style, paint.stops, opacity);
1637
1631
  const transform = getTransform(box, realFrom, realTo, stretch || 1, Platform.conicGradientRotate90);
1638
1632
  if (transform)
1639
1633
  data.transform = transform;
1640
- if (blendMode)
1641
- data.blendMode = blendMode;
1642
1634
  return data;
1643
1635
  }
1644
1636
 
@@ -1674,12 +1666,10 @@ function shadow(ui, current, shape) {
1674
1666
  }
1675
1667
  worldCanvas ? other.copyWorld(worldCanvas, nowWorld, nowWorld, 'destination-out') : other.copyWorld(shape.canvas, shapeBounds, bounds, 'destination-out');
1676
1668
  }
1677
- if (ui.__worldFlipped) {
1669
+ if (ui.__worldFlipped)
1678
1670
  current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
1679
- }
1680
- else {
1671
+ else
1681
1672
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
1682
- }
1683
1673
  if (end && index < end)
1684
1674
  other.clearWorld(copyBounds, true);
1685
1675
  });
@@ -1738,12 +1728,10 @@ function innerShadow(ui, current, shape) {
1738
1728
  copyBounds = bounds;
1739
1729
  }
1740
1730
  other.fillWorld(copyBounds, ColorConvert.string(item.color), 'source-in');
1741
- if (ui.__worldFlipped) {
1731
+ if (ui.__worldFlipped)
1742
1732
  current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
1743
- }
1744
- else {
1733
+ else
1745
1734
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
1746
- }
1747
1735
  if (end && index < end)
1748
1736
  other.clearWorld(copyBounds, true);
1749
1737
  });
@@ -1971,6 +1959,8 @@ function createRows(drawData, content, style) {
1971
1959
  lastCharType = null;
1972
1960
  startCharSize = charWidth = charSize = wordWidth = rowWidth = 0;
1973
1961
  word = { data: [] }, row = { words: [] };
1962
+ if (__letterSpacing)
1963
+ content = [...content];
1974
1964
  for (let i = 0, len = content.length; i < len; i++) {
1975
1965
  char = content[i];
1976
1966
  if (char === '\n') {