@leafer-ui/worker 1.9.12 → 1.10.0

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/worker.cjs CHANGED
@@ -779,6 +779,7 @@ class Picker {
779
779
  hit = child.__.hitRadius ? true : hitRadiusPoint(child.__world, point);
780
780
  if (child.isBranch) {
781
781
  if (hit || child.__ignoreHitWorld) {
782
+ if (child.isBranchLeaf && child.__.__clipAfterFill && !child.__hitWorld(point)) continue;
782
783
  if (child.topChildren) this.eachFind(child.topChildren, false);
783
784
  this.eachFind(child.children, child.__onlyHitMask);
784
785
  if (child.isBranchLeaf) this.hitChild(child, point);
@@ -859,36 +860,19 @@ core.Platform.render = function(target, canvas, options) {
859
860
  if (options.topList.length) options.topList.forEach(item => item.__render(canvas, topOptions));
860
861
  };
861
862
 
862
- function fillText(ui, canvas) {
863
- const data = ui.__, {rows: rows, decorationY: decorationY} = data.__textDrawData;
864
- if (data.__isPlacehold && data.placeholderColor) canvas.fillStyle = data.placeholderColor;
865
- let row;
866
- for (let i = 0, len = rows.length; i < len; i++) {
867
- row = rows[i];
868
- if (row.text) canvas.fillText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
869
- canvas.fillText(charData.char, charData.x, row.y);
870
- });
871
- }
872
- if (decorationY) {
873
- const {decorationColor: decorationColor, decorationHeight: decorationHeight} = data.__textDrawData;
874
- if (decorationColor) canvas.fillStyle = decorationColor;
875
- rows.forEach(row => decorationY.forEach(value => canvas.fillRect(row.x, row.y + value, row.width, decorationHeight)));
876
- }
877
- }
878
-
879
- function fill(fill, ui, canvas) {
863
+ function fill(fill, ui, canvas, renderOptions) {
880
864
  canvas.fillStyle = fill;
881
- fillPathOrText(ui, canvas);
865
+ fillPathOrText(ui, canvas, renderOptions);
882
866
  }
883
867
 
884
- function fills(fills, ui, canvas) {
868
+ function fills(fills, ui, canvas, renderOptions) {
885
869
  let item;
886
870
  for (let i = 0, len = fills.length; i < len; i++) {
887
871
  item = fills[i];
888
872
  if (item.image) {
889
- if (draw.PaintImage.checkImage(ui, canvas, item, !ui.__.__font)) continue;
873
+ if (draw.PaintImage.checkImage(item, !ui.__.__font, ui, canvas, renderOptions)) continue;
890
874
  if (!item.style) {
891
- if (!i && item.image.isPlacehold) ui.drawImagePlaceholder(canvas, item.image);
875
+ if (!i && item.image.isPlacehold) ui.drawImagePlaceholder(item.image, canvas, renderOptions);
892
876
  continue;
893
877
  }
894
878
  }
@@ -901,60 +885,137 @@ function fills(fills, ui, canvas) {
901
885
  if (item.scaleFixed === true || item.scaleFixed === "zoom-in" && scaleX > 1 && scaleY > 1) canvas.scale(1 / scaleX, 1 / scaleY);
902
886
  }
903
887
  if (item.blendMode) canvas.blendMode = item.blendMode;
904
- fillPathOrText(ui, canvas);
888
+ fillPathOrText(ui, canvas, renderOptions);
905
889
  canvas.restore();
906
890
  } else {
907
891
  if (item.blendMode) {
908
892
  canvas.saveBlendMode(item.blendMode);
909
- fillPathOrText(ui, canvas);
893
+ fillPathOrText(ui, canvas, renderOptions);
910
894
  canvas.restoreBlendMode();
911
- } else fillPathOrText(ui, canvas);
895
+ } else fillPathOrText(ui, canvas, renderOptions);
896
+ }
897
+ }
898
+ }
899
+
900
+ function fillPathOrText(ui, canvas, renderOptions) {
901
+ ui.__.__font ? draw.Paint.fillText(ui, canvas, renderOptions) : ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill();
902
+ }
903
+
904
+ function fillText(ui, canvas, _renderOptions) {
905
+ const data = ui.__, {rows: rows, decorationY: decorationY} = data.__textDrawData;
906
+ if (data.__isPlacehold && data.placeholderColor) canvas.fillStyle = data.placeholderColor;
907
+ let row;
908
+ for (let i = 0, len = rows.length; i < len; i++) {
909
+ row = rows[i];
910
+ if (row.text) canvas.fillText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
911
+ canvas.fillText(charData.char, charData.x, row.y);
912
+ });
913
+ }
914
+ if (decorationY) {
915
+ const {decorationColor: decorationColor, decorationHeight: decorationHeight} = data.__textDrawData;
916
+ if (decorationColor) canvas.fillStyle = decorationColor;
917
+ rows.forEach(row => decorationY.forEach(value => canvas.fillRect(row.x, row.y + value, row.width, decorationHeight)));
918
+ }
919
+ }
920
+
921
+ function stroke(stroke, ui, canvas, renderOptions) {
922
+ const data = ui.__;
923
+ if (!data.__strokeWidth) return;
924
+ if (data.__font) {
925
+ draw.Paint.strokeText(stroke, ui, canvas, renderOptions);
926
+ } else {
927
+ switch (data.strokeAlign) {
928
+ case "center":
929
+ drawCenter$1(stroke, 1, ui, canvas, renderOptions);
930
+ break;
931
+
932
+ case "inside":
933
+ drawInside(stroke, ui, canvas, renderOptions);
934
+ break;
935
+
936
+ case "outside":
937
+ drawOutside(stroke, ui, canvas, renderOptions);
938
+ break;
912
939
  }
913
940
  }
914
941
  }
915
942
 
916
- function fillPathOrText(ui, canvas) {
917
- ui.__.__font ? fillText(ui, canvas) : ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill();
943
+ function strokes(strokes, ui, canvas, renderOptions) {
944
+ draw.Paint.stroke(strokes, ui, canvas, renderOptions);
945
+ }
946
+
947
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas, renderOptions) {
948
+ const data = ui.__;
949
+ if (core.isObject(stroke)) {
950
+ draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, false, ui, canvas, renderOptions);
951
+ } else {
952
+ canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
953
+ canvas.stroke();
954
+ }
955
+ if (data.__useArrow) draw.Paint.strokeArrow(stroke, ui, canvas, renderOptions);
956
+ }
957
+
958
+ function drawInside(stroke, ui, canvas, renderOptions) {
959
+ canvas.save();
960
+ canvas.clipUI(ui);
961
+ drawCenter$1(stroke, 2, ui, canvas, renderOptions);
962
+ canvas.restore();
918
963
  }
919
964
 
920
- function strokeText(stroke, ui, canvas) {
965
+ function drawOutside(stroke, ui, canvas, renderOptions) {
966
+ const data = ui.__;
967
+ if (data.__fillAfterStroke) {
968
+ drawCenter$1(stroke, 2, ui, canvas, renderOptions);
969
+ } else {
970
+ const {renderBounds: renderBounds} = ui.__layout;
971
+ const out = canvas.getSameCanvas(true, true);
972
+ ui.__drawRenderPath(out);
973
+ drawCenter$1(stroke, 2, ui, out, renderOptions);
974
+ out.clipUI(data);
975
+ out.clearWorld(renderBounds);
976
+ core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
977
+ out.recycle(ui.__nowWorld);
978
+ }
979
+ }
980
+
981
+ function strokeText(stroke, ui, canvas, renderOptions) {
921
982
  switch (ui.__.strokeAlign) {
922
983
  case "center":
923
- drawCenter$1(stroke, 1, ui, canvas);
984
+ drawCenter(stroke, 1, ui, canvas, renderOptions);
924
985
  break;
925
986
 
926
987
  case "inside":
927
- drawAlign(stroke, "inside", ui, canvas);
988
+ drawAlign(stroke, "inside", ui, canvas, renderOptions);
928
989
  break;
929
990
 
930
991
  case "outside":
931
- ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, "outside", ui, canvas);
992
+ ui.__.__fillAfterStroke ? drawCenter(stroke, 2, ui, canvas, renderOptions) : drawAlign(stroke, "outside", ui, canvas, renderOptions);
932
993
  break;
933
994
  }
934
995
  }
935
996
 
936
- function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
997
+ function drawCenter(stroke, strokeWidthScale, ui, canvas, renderOptions) {
937
998
  const data = ui.__;
938
999
  if (core.isObject(stroke)) {
939
- drawStrokesStyle(stroke, strokeWidthScale, true, ui, canvas);
1000
+ draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, true, ui, canvas, renderOptions);
940
1001
  } else {
941
1002
  canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
942
- drawTextStroke(ui, canvas);
1003
+ draw.Paint.drawTextStroke(ui, canvas, renderOptions);
943
1004
  }
944
1005
  }
945
1006
 
946
- function drawAlign(stroke, align, ui, canvas) {
1007
+ function drawAlign(stroke, align, ui, canvas, renderOptions) {
947
1008
  const out = canvas.getSameCanvas(true, true);
948
1009
  out.font = ui.__.__font;
949
- drawCenter$1(stroke, 2, ui, out);
1010
+ drawCenter(stroke, 2, ui, out, renderOptions);
950
1011
  out.blendMode = align === "outside" ? "destination-out" : "destination-in";
951
- fillText(ui, out);
1012
+ draw.Paint.fillText(ui, out, renderOptions);
952
1013
  out.blendMode = "normal";
953
1014
  core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
954
1015
  out.recycle(ui.__nowWorld);
955
1016
  }
956
1017
 
957
- function drawTextStroke(ui, canvas) {
1018
+ function drawTextStroke(ui, canvas, _renderOptions) {
958
1019
  let row, data = ui.__.__textDrawData;
959
1020
  const {rows: rows, decorationY: decorationY} = data;
960
1021
  for (let i = 0, len = rows.length; i < len; i++) {
@@ -969,13 +1030,13 @@ function drawTextStroke(ui, canvas) {
969
1030
  }
970
1031
  }
971
1032
 
972
- function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas) {
1033
+ function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas, renderOptions) {
973
1034
  let item;
974
1035
  const data = ui.__, {__hasMultiStrokeStyle: __hasMultiStrokeStyle} = data;
975
1036
  __hasMultiStrokeStyle || canvas.setStroke(undefined, data.__strokeWidth * strokeWidthScale, data);
976
1037
  for (let i = 0, len = strokes.length; i < len; i++) {
977
1038
  item = strokes[i];
978
- if (item.image && draw.PaintImage.checkImage(ui, canvas, item, false)) continue;
1039
+ if (item.image && draw.PaintImage.checkImage(item, false, ui, canvas, renderOptions)) continue;
979
1040
  if (item.style) {
980
1041
  if (__hasMultiStrokeStyle) {
981
1042
  const {strokeStyle: strokeStyle} = item;
@@ -983,75 +1044,15 @@ function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas) {
983
1044
  } else canvas.strokeStyle = item.style;
984
1045
  if (item.blendMode) {
985
1046
  canvas.saveBlendMode(item.blendMode);
986
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
1047
+ isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
987
1048
  canvas.restoreBlendMode();
988
1049
  } else {
989
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
1050
+ isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
990
1051
  }
991
1052
  }
992
1053
  }
993
1054
  }
994
1055
 
995
- function stroke(stroke, ui, canvas) {
996
- const data = ui.__;
997
- if (!data.__strokeWidth) return;
998
- if (data.__font) {
999
- strokeText(stroke, ui, canvas);
1000
- } else {
1001
- switch (data.strokeAlign) {
1002
- case "center":
1003
- drawCenter(stroke, 1, ui, canvas);
1004
- break;
1005
-
1006
- case "inside":
1007
- drawInside(stroke, ui, canvas);
1008
- break;
1009
-
1010
- case "outside":
1011
- drawOutside(stroke, ui, canvas);
1012
- break;
1013
- }
1014
- }
1015
- }
1016
-
1017
- function strokes(strokes, ui, canvas) {
1018
- stroke(strokes, ui, canvas);
1019
- }
1020
-
1021
- function drawCenter(stroke, strokeWidthScale, ui, canvas) {
1022
- const data = ui.__;
1023
- if (core.isObject(stroke)) {
1024
- drawStrokesStyle(stroke, strokeWidthScale, false, ui, canvas);
1025
- } else {
1026
- canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
1027
- canvas.stroke();
1028
- }
1029
- if (data.__useArrow) draw.Paint.strokeArrow(stroke, ui, canvas);
1030
- }
1031
-
1032
- function drawInside(stroke, ui, canvas) {
1033
- canvas.save();
1034
- canvas.clipUI(ui);
1035
- drawCenter(stroke, 2, ui, canvas);
1036
- canvas.restore();
1037
- }
1038
-
1039
- function drawOutside(stroke, ui, canvas) {
1040
- const data = ui.__;
1041
- if (data.__fillAfterStroke) {
1042
- drawCenter(stroke, 2, ui, canvas);
1043
- } else {
1044
- const {renderBounds: renderBounds} = ui.__layout;
1045
- const out = canvas.getSameCanvas(true, true);
1046
- ui.__drawRenderPath(out);
1047
- drawCenter(stroke, 2, ui, out);
1048
- out.clipUI(data);
1049
- out.clearWorld(renderBounds);
1050
- core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
1051
- out.recycle(ui.__nowWorld);
1052
- }
1053
- }
1054
-
1055
1056
  const {getSpread: getSpread, copyAndSpread: copyAndSpread, toOuterOf: toOuterOf, getOuterOf: getOuterOf, getByMove: getByMove, move: move$1, getIntersectData: getIntersectData} = core.BoundsHelper;
1056
1057
 
1057
1058
  const tempBounds$1 = {};
@@ -1207,88 +1208,118 @@ const PaintModule = {
1207
1208
  strokes: strokes,
1208
1209
  strokeText: strokeText,
1209
1210
  drawTextStroke: drawTextStroke,
1211
+ drawStrokesStyle: drawStrokesStyle,
1210
1212
  shape: shape
1211
1213
  };
1212
1214
 
1213
- let origin = {}, tempMatrix$1 = core.getMatrixData();
1215
+ let cache, box = new core.Bounds;
1214
1216
 
1215
- const {get: get$3, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
1217
+ const {isSame: isSame} = core.BoundsHelper;
1216
1218
 
1217
- function stretchMode(data, box, scaleX, scaleY) {
1218
- const transform = get$3();
1219
- translate$1(transform, box.x, box.y);
1220
- if (scaleX) scaleHelper(transform, scaleX, scaleY);
1221
- data.transform = transform;
1219
+ function image(ui, attrName, paint, boxBounds, firstUse) {
1220
+ let leafPaint, event;
1221
+ const image = core.ImageManager.get(paint);
1222
+ if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
1223
+ leafPaint = cache.leafPaint;
1224
+ } else {
1225
+ leafPaint = {
1226
+ type: paint.type,
1227
+ image: image
1228
+ };
1229
+ if (image.hasAlphaPixel) leafPaint.isTransparent = true;
1230
+ cache = image.use > 1 ? {
1231
+ leafPaint: leafPaint,
1232
+ paint: paint,
1233
+ boxBounds: box.set(boxBounds)
1234
+ } : null;
1235
+ }
1236
+ if (firstUse || image.loading) event = {
1237
+ image: image,
1238
+ attrName: attrName,
1239
+ attrValue: paint
1240
+ };
1241
+ if (image.ready) {
1242
+ checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
1243
+ if (firstUse) {
1244
+ onLoad(ui, event);
1245
+ onLoadSuccess(ui, event);
1246
+ }
1247
+ } else if (image.error) {
1248
+ if (firstUse) onLoadError(ui, event, image.error);
1249
+ } else {
1250
+ if (firstUse) {
1251
+ ignoreRender(ui, true);
1252
+ onLoad(ui, event);
1253
+ }
1254
+ leafPaint.loadId = image.load(() => {
1255
+ ignoreRender(ui, false);
1256
+ if (!ui.destroyed) {
1257
+ if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1258
+ if (image.hasAlphaPixel) ui.__layout.hitCanvasChanged = true;
1259
+ ui.forceUpdate("surface");
1260
+ }
1261
+ onLoadSuccess(ui, event);
1262
+ }
1263
+ leafPaint.loadId = undefined;
1264
+ }, error => {
1265
+ ignoreRender(ui, false);
1266
+ onLoadError(ui, event, error);
1267
+ leafPaint.loadId = undefined;
1268
+ });
1269
+ if (ui.placeholderColor) {
1270
+ if (!ui.placeholderDelay) image.isPlacehold = true; else setTimeout(() => {
1271
+ if (!image.ready) {
1272
+ image.isPlacehold = true;
1273
+ ui.forceUpdate("surface");
1274
+ }
1275
+ }, ui.placeholderDelay);
1276
+ }
1277
+ }
1278
+ return leafPaint;
1222
1279
  }
1223
1280
 
1224
- function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1225
- const transform = get$3();
1226
- translate$1(transform, box.x + x, box.y + y);
1227
- scaleHelper(transform, scaleX, scaleY);
1228
- if (rotation) rotateOfOuter$1(transform, {
1229
- x: box.x + box.width / 2,
1230
- y: box.y + box.height / 2
1231
- }, rotation);
1232
- data.transform = transform;
1281
+ function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1282
+ if (attrName === "fill" && !ui.__.__naturalWidth) {
1283
+ const data = ui.__;
1284
+ data.__naturalWidth = image.width / data.pixelRatio;
1285
+ data.__naturalHeight = image.height / data.pixelRatio;
1286
+ if (data.__autoSide) {
1287
+ ui.forceUpdate("width");
1288
+ if (ui.__proxyData) {
1289
+ ui.setProxyAttr("width", data.width);
1290
+ ui.setProxyAttr("height", data.height);
1291
+ }
1292
+ return false;
1293
+ }
1294
+ }
1295
+ if (!leafPaint.data) draw.PaintImage.createData(leafPaint, image, paint, boxBounds);
1296
+ return true;
1233
1297
  }
1234
1298
 
1235
- function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY) {
1236
- const transform = get$3();
1237
- layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1238
- if (clipScaleX) {
1239
- if (rotation || skew) {
1240
- set(tempMatrix$1);
1241
- scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1242
- multiplyParent(transform, tempMatrix$1);
1243
- } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1244
- }
1245
- data.transform = transform;
1299
+ function onLoad(ui, event) {
1300
+ emit(ui, core.ImageEvent.LOAD, event);
1246
1301
  }
1247
1302
 
1248
- function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, skew, align, freeTransform) {
1249
- const transform = get$3();
1250
- if (freeTransform) {
1251
- layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1252
- } else {
1253
- if (rotation) {
1254
- if (align === "center") {
1255
- rotateOfOuter$1(transform, {
1256
- x: width / 2,
1257
- y: height / 2
1258
- }, rotation);
1259
- } else {
1260
- rotate(transform, rotation);
1261
- switch (rotation) {
1262
- case 90:
1263
- translate$1(transform, height, 0);
1264
- break;
1303
+ function onLoadSuccess(ui, event) {
1304
+ emit(ui, core.ImageEvent.LOADED, event);
1305
+ }
1265
1306
 
1266
- case 180:
1267
- translate$1(transform, width, height);
1268
- break;
1307
+ function onLoadError(ui, event, error) {
1308
+ event.error = error;
1309
+ ui.forceUpdate("surface");
1310
+ emit(ui, core.ImageEvent.ERROR, event);
1311
+ }
1269
1312
 
1270
- case 270:
1271
- translate$1(transform, 0, width);
1272
- break;
1273
- }
1274
- }
1275
- }
1276
- origin.x = box.x + x;
1277
- origin.y = box.y + y;
1278
- translate$1(transform, origin.x, origin.y);
1279
- if (scaleX) scaleOfOuter$1(transform, origin, scaleX, scaleY);
1280
- }
1281
- data.transform = transform;
1313
+ function emit(ui, type, data) {
1314
+ if (ui.hasEvent(type)) ui.emitEvent(new core.ImageEvent(type, data));
1282
1315
  }
1283
1316
 
1284
- function layout(transform, box, x, y, scaleX, scaleY, rotation, skew) {
1285
- if (rotation) rotate(transform, rotation);
1286
- if (skew) skewHelper(transform, skew.x, skew.y);
1287
- if (scaleX) scaleHelper(transform, scaleX, scaleY);
1288
- translate$1(transform, box.x + x, box.y + y);
1317
+ function ignoreRender(ui, value) {
1318
+ const {leafer: leafer} = ui;
1319
+ if (leafer && leafer.viewReady) leafer.renderer.ignore = value;
1289
1320
  }
1290
1321
 
1291
- const {get: get$2, translate: translate} = core.MatrixHelper;
1322
+ const {get: get$3, translate: translate$1} = core.MatrixHelper;
1292
1323
 
1293
1324
  const tempBox = new core.Bounds;
1294
1325
 
@@ -1301,13 +1332,13 @@ function createData(leafPaint, image, paint, box) {
1301
1332
  if (changeful) leafPaint.changeful = changeful;
1302
1333
  if (sync) leafPaint.sync = sync;
1303
1334
  if (scaleFixed) leafPaint.scaleFixed = scaleFixed;
1304
- leafPaint.data = getPatternData(paint, box, image);
1335
+ leafPaint.data = draw.PaintImage.getPatternData(paint, box, image);
1305
1336
  }
1306
1337
 
1307
1338
  function getPatternData(paint, box, image) {
1308
1339
  if (paint.padding) box = tempBox.set(box).shrink(paint.padding);
1309
1340
  if (paint.mode === "strench") paint.mode = "stretch";
1310
- let {width: width, height: height} = image;
1341
+ const {width: width, height: height} = image;
1311
1342
  const {opacity: opacity, mode: mode, align: align, offset: offset, scale: scale, size: size, rotation: rotation, skew: skew, clipSize: clipSize, repeat: repeat, gap: gap, filters: filters} = paint;
1312
1343
  const sameBox = box.width === width && box.height === height;
1313
1344
  const data = {
@@ -1338,8 +1369,8 @@ function getPatternData(paint, box, image) {
1338
1369
  case "stretch":
1339
1370
  if (!sameBox) {
1340
1371
  scaleX = box.width / width, scaleY = box.height / height;
1341
- stretchMode(data, box, scaleX, scaleY);
1342
- }
1372
+ draw.PaintImage.stretchMode(data, box, scaleX, scaleY);
1373
+ } else if (scaleX) scaleX = scaleY = undefined;
1343
1374
  break;
1344
1375
 
1345
1376
  case "normal":
@@ -1347,13 +1378,13 @@ function getPatternData(paint, box, image) {
1347
1378
  if (tempImage.x || tempImage.y || scaleX || clipSize || rotation || skew) {
1348
1379
  let clipScaleX, clipScaleY;
1349
1380
  if (clipSize) clipScaleX = box.width / clipSize.width, clipScaleY = box.height / clipSize.height;
1350
- clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1381
+ draw.PaintImage.clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1351
1382
  if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : clipScaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
1352
1383
  }
1353
1384
  break;
1354
1385
 
1355
1386
  case "repeat":
1356
- if (!sameBox || scaleX || rotation || skew) repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, align, paint.freeTransform);
1387
+ if (!sameBox || scaleX || rotation || skew) draw.PaintImage.repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, align, paint.freeTransform);
1357
1388
  if (!repeat) data.repeat = "repeat";
1358
1389
  const count = core.isObject(repeat);
1359
1390
  if (gap || count) data.gap = getGapData(gap, count && repeat, tempImage.width, tempImage.height, box);
@@ -1362,18 +1393,16 @@ function getPatternData(paint, box, image) {
1362
1393
  case "fit":
1363
1394
  case "cover":
1364
1395
  default:
1365
- if (scaleX) fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1396
+ if (scaleX) draw.PaintImage.fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1366
1397
  }
1367
1398
  if (!data.transform) {
1368
- if (box.x || box.y) translate(data.transform = get$2(), box.x, box.y);
1399
+ if (box.x || box.y) translate$1(data.transform = get$3(), box.x, box.y);
1369
1400
  }
1370
- data.width = width;
1371
- data.height = height;
1372
1401
  if (scaleX) {
1373
1402
  data.scaleX = scaleX;
1374
1403
  data.scaleY = scaleY;
1375
1404
  }
1376
- if (opacity) data.opacity = opacity;
1405
+ if (opacity && opacity < 1) data.opacity = opacity;
1377
1406
  if (filters) data.filters = filters;
1378
1407
  if (repeat) data.repeat = core.isString(repeat) ? repeat === "x" ? "repeat-x" : "repeat-y" : "repeat";
1379
1408
  return data;
@@ -1395,180 +1424,82 @@ function getGapValue(gap, size, totalSize, rows) {
1395
1424
  return gap === "auto" ? value < 0 ? 0 : value : value;
1396
1425
  }
1397
1426
 
1398
- let cache, box = new core.Bounds;
1399
-
1400
- const {isSame: isSame} = core.BoundsHelper;
1401
-
1402
- function image(ui, attrName, paint, boxBounds, firstUse) {
1403
- let leafPaint, event;
1404
- const image = core.ImageManager.get(paint);
1405
- if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
1406
- leafPaint = cache.leafPaint;
1407
- } else {
1408
- leafPaint = {
1409
- type: paint.type,
1410
- image: image
1411
- };
1412
- if (image.hasAlphaPixel) leafPaint.isTransparent = true;
1413
- cache = image.use > 1 ? {
1414
- leafPaint: leafPaint,
1415
- paint: paint,
1416
- boxBounds: box.set(boxBounds)
1417
- } : null;
1418
- }
1419
- if (firstUse || image.loading) event = {
1420
- image: image,
1421
- attrName: attrName,
1422
- attrValue: paint
1423
- };
1424
- if (image.ready) {
1425
- checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
1426
- if (firstUse) {
1427
- onLoad(ui, event);
1428
- onLoadSuccess(ui, event);
1429
- }
1430
- } else if (image.error) {
1431
- if (firstUse) onLoadError(ui, event, image.error);
1432
- } else {
1433
- if (firstUse) {
1434
- ignoreRender(ui, true);
1435
- onLoad(ui, event);
1436
- }
1437
- leafPaint.loadId = image.load(() => {
1438
- ignoreRender(ui, false);
1439
- if (!ui.destroyed) {
1440
- if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1441
- if (image.hasAlphaPixel) ui.__layout.hitCanvasChanged = true;
1442
- ui.forceUpdate("surface");
1443
- }
1444
- onLoadSuccess(ui, event);
1445
- }
1446
- leafPaint.loadId = undefined;
1447
- }, error => {
1448
- ignoreRender(ui, false);
1449
- onLoadError(ui, event, error);
1450
- leafPaint.loadId = undefined;
1451
- });
1452
- if (ui.placeholderColor) {
1453
- if (!ui.placeholderDelay) image.isPlacehold = true; else setTimeout(() => {
1454
- if (!image.ready) {
1455
- image.isPlacehold = true;
1456
- ui.forceUpdate("surface");
1457
- }
1458
- }, ui.placeholderDelay);
1459
- }
1460
- }
1461
- return leafPaint;
1462
- }
1463
-
1464
- function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1465
- if (attrName === "fill" && !ui.__.__naturalWidth) {
1466
- const data = ui.__;
1467
- data.__naturalWidth = image.width / data.pixelRatio;
1468
- data.__naturalHeight = image.height / data.pixelRatio;
1469
- if (data.__autoSide) {
1470
- ui.forceUpdate("width");
1471
- if (ui.__proxyData) {
1472
- ui.setProxyAttr("width", data.width);
1473
- ui.setProxyAttr("height", data.height);
1474
- }
1475
- return false;
1476
- }
1477
- }
1478
- if (!leafPaint.data) createData(leafPaint, image, paint, boxBounds);
1479
- return true;
1480
- }
1427
+ let origin = {}, tempMatrix$1 = core.getMatrixData();
1481
1428
 
1482
- function onLoad(ui, event) {
1483
- emit(ui, core.ImageEvent.LOAD, event);
1484
- }
1429
+ const {get: get$2, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
1485
1430
 
1486
- function onLoadSuccess(ui, event) {
1487
- emit(ui, core.ImageEvent.LOADED, event);
1488
- }
1489
-
1490
- function onLoadError(ui, event, error) {
1491
- event.error = error;
1492
- ui.forceUpdate("surface");
1493
- emit(ui, core.ImageEvent.ERROR, event);
1431
+ function stretchMode(data, box, scaleX, scaleY) {
1432
+ const transform = get$2(), {x: x, y: y} = box;
1433
+ if (x || y) translate(transform, x, y); else transform.onlyScale = true;
1434
+ scaleHelper(transform, scaleX, scaleY);
1435
+ data.transform = transform;
1494
1436
  }
1495
1437
 
1496
- function emit(ui, type, data) {
1497
- if (ui.hasEvent(type)) ui.emitEvent(new core.ImageEvent(type, data));
1438
+ function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1439
+ const transform = get$2();
1440
+ translate(transform, box.x + x, box.y + y);
1441
+ scaleHelper(transform, scaleX, scaleY);
1442
+ if (rotation) rotateOfOuter$1(transform, {
1443
+ x: box.x + box.width / 2,
1444
+ y: box.y + box.height / 2
1445
+ }, rotation);
1446
+ data.transform = transform;
1498
1447
  }
1499
1448
 
1500
- function ignoreRender(ui, value) {
1501
- const {leafer: leafer} = ui;
1502
- if (leafer && leafer.viewReady) leafer.renderer.ignore = value;
1449
+ function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY) {
1450
+ const transform = get$2();
1451
+ layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1452
+ if (clipScaleX) {
1453
+ if (rotation || skew) {
1454
+ set(tempMatrix$1);
1455
+ scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1456
+ multiplyParent(transform, tempMatrix$1);
1457
+ } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1458
+ }
1459
+ data.transform = transform;
1503
1460
  }
1504
1461
 
1505
- const {get: get$1, scale: scale, copy: copy$1} = core.MatrixHelper;
1462
+ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, skew, align, freeTransform) {
1463
+ const transform = get$2();
1464
+ if (freeTransform) {
1465
+ layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1466
+ } else {
1467
+ if (rotation) {
1468
+ if (align === "center") {
1469
+ rotateOfOuter$1(transform, {
1470
+ x: width / 2,
1471
+ y: height / 2
1472
+ }, rotation);
1473
+ } else {
1474
+ rotate(transform, rotation);
1475
+ switch (rotation) {
1476
+ case 90:
1477
+ translate(transform, height, 0);
1478
+ break;
1506
1479
 
1507
- const {floor: floor, ceil: ceil, max: max$1, abs: abs$1} = Math;
1480
+ case 180:
1481
+ translate(transform, width, height);
1482
+ break;
1508
1483
 
1509
- function createPattern(ui, paint, pixelRatio) {
1510
- let {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
1511
- const id = scaleX + "-" + scaleY + "-" + pixelRatio;
1512
- if (paint.patternId !== id && !ui.destroyed) {
1513
- const {image: image, data: data} = paint;
1514
- let imageScale, imageMatrix, {width: width, height: height, scaleX: sx, scaleY: sy, transform: transform, repeat: repeat, gap: gap} = data;
1515
- scaleX *= pixelRatio;
1516
- scaleY *= pixelRatio;
1517
- if (sx) {
1518
- sx = abs$1(sx);
1519
- sy = abs$1(sy);
1520
- imageMatrix = get$1();
1521
- copy$1(imageMatrix, transform);
1522
- scale(imageMatrix, 1 / sx, 1 / sy);
1523
- scaleX *= sx;
1524
- scaleY *= sy;
1525
- }
1526
- width *= scaleX;
1527
- height *= scaleY;
1528
- const size = width * height;
1529
- if (!repeat) {
1530
- if (size > core.Platform.image.maxCacheSize) return false;
1531
- }
1532
- let maxSize = core.Platform.image.maxPatternSize;
1533
- if (image.isSVG) {
1534
- const ws = width / image.width;
1535
- if (ws > 1) imageScale = ws / ceil(ws);
1536
- } else {
1537
- const imageSize = image.width * image.height;
1538
- if (maxSize > imageSize) maxSize = imageSize;
1539
- }
1540
- if (size > maxSize) imageScale = Math.sqrt(size / maxSize);
1541
- if (imageScale) {
1542
- scaleX /= imageScale;
1543
- scaleY /= imageScale;
1544
- width /= imageScale;
1545
- height /= imageScale;
1546
- }
1547
- if (sx) {
1548
- scaleX /= sx;
1549
- scaleY /= sy;
1550
- }
1551
- const xGap = gap && gap.x * scaleX;
1552
- const yGap = gap && gap.y * scaleY;
1553
- if (transform || scaleX !== 1 || scaleY !== 1) {
1554
- const canvasWidth = width + (xGap || 0);
1555
- const canvasHeight = height + (yGap || 0);
1556
- scaleX /= canvasWidth / max$1(floor(canvasWidth), 1);
1557
- scaleY /= canvasHeight / max$1(floor(canvasHeight), 1);
1558
- if (!imageMatrix) {
1559
- imageMatrix = get$1();
1560
- if (transform) copy$1(imageMatrix, transform);
1484
+ case 270:
1485
+ translate(transform, 0, width);
1486
+ break;
1487
+ }
1561
1488
  }
1562
- scale(imageMatrix, 1 / scaleX, 1 / scaleY);
1563
1489
  }
1564
- const canvas = image.getCanvas(width, height, data.opacity, data.filters, xGap, yGap, ui.leafer && ui.leafer.config.smooth);
1565
- const pattern = image.getPattern(canvas, repeat || (core.Platform.origin.noRepeat || "no-repeat"), imageMatrix, paint);
1566
- paint.style = pattern;
1567
- paint.patternId = id;
1568
- return true;
1569
- } else {
1570
- return false;
1490
+ origin.x = box.x + x;
1491
+ origin.y = box.y + y;
1492
+ translate(transform, origin.x, origin.y);
1493
+ if (scaleX) scaleOfOuter$1(transform, origin, scaleX, scaleY);
1571
1494
  }
1495
+ data.transform = transform;
1496
+ }
1497
+
1498
+ function layout(transform, box, x, y, scaleX, scaleY, rotation, skew) {
1499
+ if (rotation) rotate(transform, rotation);
1500
+ if (skew) skewHelper(transform, skew.x, skew.y);
1501
+ if (scaleX) scaleHelper(transform, scaleX, scaleY);
1502
+ translate(transform, box.x + x, box.y + y);
1572
1503
  }
1573
1504
 
1574
1505
  function __awaiter(thisArg, _arguments, P, generator) {
@@ -1604,58 +1535,116 @@ typeof SuppressedError === "function" ? SuppressedError : function(error, suppre
1604
1535
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
1605
1536
  };
1606
1537
 
1607
- function checkImage(ui, canvas, paint, allowDraw) {
1608
- const {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
1609
- const {pixelRatio: pixelRatio} = canvas, {data: data} = paint;
1610
- if (!data || paint.patternId === scaleX + "-" + scaleY + "-" + pixelRatio && !draw.Export.running) {
1538
+ const {get: get$1, scale: scale, copy: copy$1} = core.MatrixHelper;
1539
+
1540
+ const {getFloorScale: getFloorScale} = core.MathHelper, {abs: abs$1} = Math;
1541
+
1542
+ function createPatternTask(paint, ui, canvas, renderOptions) {
1543
+ if (!paint.patternTask) {
1544
+ paint.patternTask = core.ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function*() {
1545
+ paint.patternTask = null;
1546
+ if (canvas.bounds.hit(ui.__nowWorld)) draw.PaintImage.createPattern(paint, ui, canvas, renderOptions);
1547
+ ui.forceUpdate("surface");
1548
+ }), 300);
1549
+ }
1550
+ }
1551
+
1552
+ function createPattern(paint, ui, canvas, renderOptions) {
1553
+ let {scaleX: scaleX, scaleY: scaleY} = draw.PaintImage.getImageRenderScaleData(paint, ui, canvas, renderOptions), id = scaleX + "-" + scaleY;
1554
+ if (paint.patternId !== id && !ui.destroyed) {
1555
+ if (!(core.Platform.image.isLarge(paint.image, scaleX, scaleY) && !paint.data.repeat)) {
1556
+ const {image: image, data: data} = paint, {transform: transform, gap: gap} = data, fixScale = draw.PaintImage.getPatternFixScale(paint, scaleX, scaleY);
1557
+ let imageMatrix, xGap, yGap, {width: width, height: height} = image;
1558
+ if (fixScale) scaleX *= fixScale, scaleY *= fixScale;
1559
+ width *= scaleX;
1560
+ height *= scaleY;
1561
+ if (gap) {
1562
+ xGap = gap.x * scaleX / abs$1(data.scaleX || 1);
1563
+ yGap = gap.y * scaleY / abs$1(data.scaleY || 1);
1564
+ }
1565
+ if (transform || scaleX !== 1 || scaleY !== 1) {
1566
+ scaleX *= getFloorScale(width + (xGap || 0));
1567
+ scaleY *= getFloorScale(height + (yGap || 0));
1568
+ imageMatrix = get$1();
1569
+ if (transform) copy$1(imageMatrix, transform);
1570
+ scale(imageMatrix, 1 / scaleX, 1 / scaleY);
1571
+ }
1572
+ const imageCanvas = image.getCanvas(width, height, data.opacity, data.filters, xGap, yGap, ui.leafer && ui.leafer.config.smooth);
1573
+ const pattern = image.getPattern(imageCanvas, data.repeat || (core.Platform.origin.noRepeat || "no-repeat"), imageMatrix, paint);
1574
+ paint.style = pattern;
1575
+ paint.patternId = id;
1576
+ }
1577
+ }
1578
+ }
1579
+
1580
+ function getPatternFixScale(paint, imageScaleX, imageScaleY) {
1581
+ const {image: image} = paint;
1582
+ let fixScale, maxSize = core.Platform.image.maxPatternSize, imageSize = image.width * image.height;
1583
+ if (image.isSVG) {
1584
+ if (imageScaleX > 1) fixScale = Math.ceil(imageScaleX) / imageScaleX;
1585
+ } else {
1586
+ if (maxSize > imageSize) maxSize = imageSize;
1587
+ }
1588
+ if ((imageSize *= imageScaleX * imageScaleY) > maxSize) fixScale = Math.sqrt(maxSize / imageSize);
1589
+ return fixScale;
1590
+ }
1591
+
1592
+ function checkImage(paint, drawImage, ui, canvas, renderOptions) {
1593
+ const {scaleX: scaleX, scaleY: scaleY} = draw.PaintImage.getImageRenderScaleData(paint, ui, canvas, renderOptions);
1594
+ const {image: image, data: data} = paint, {exporting: exporting} = renderOptions;
1595
+ if (!data || paint.patternId === scaleX + "-" + scaleY && !exporting) {
1611
1596
  return false;
1612
1597
  } else {
1613
- if (allowDraw) {
1598
+ if (drawImage) {
1614
1599
  if (data.repeat) {
1615
- allowDraw = false;
1616
- } else if (!(paint.changeful || core.Platform.name === "miniapp" && core.ResizeEvent.isResizing(ui) || draw.Export.running)) {
1617
- let {width: width, height: height} = data;
1618
- width *= scaleX * pixelRatio;
1619
- height *= scaleY * pixelRatio;
1620
- if (data.scaleX) {
1621
- width *= data.scaleX;
1622
- height *= data.scaleY;
1623
- }
1624
- allowDraw = width * height > core.Platform.image.maxCacheSize;
1600
+ drawImage = false;
1601
+ } else if (!(paint.changeful || core.Platform.name === "miniapp" && core.ResizeEvent.isResizing(ui) || exporting)) {
1602
+ drawImage = core.Platform.image.isLarge(image, scaleX, scaleY);
1625
1603
  }
1626
1604
  }
1627
- if (allowDraw) {
1605
+ if (drawImage) {
1628
1606
  if (ui.__.__isFastShadow) {
1629
1607
  canvas.fillStyle = paint.style || "#000";
1630
1608
  canvas.fill();
1631
1609
  }
1632
- drawImage(ui, canvas, paint, data);
1610
+ draw.PaintImage.drawImage(paint, scaleX, scaleY, ui, canvas, renderOptions);
1633
1611
  return true;
1634
1612
  } else {
1635
- if (!paint.style || paint.sync || draw.Export.running) {
1636
- createPattern(ui, paint, pixelRatio);
1637
- } else {
1638
- if (!paint.patternTask) {
1639
- paint.patternTask = core.ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function*() {
1640
- paint.patternTask = null;
1641
- if (canvas.bounds.hit(ui.__nowWorld)) createPattern(ui, paint, pixelRatio);
1642
- ui.forceUpdate("surface");
1643
- }), 300);
1644
- }
1645
- }
1613
+ if (!paint.style || paint.sync || exporting) draw.PaintImage.createPattern(paint, ui, canvas, renderOptions); else draw.PaintImage.createPatternTask(paint, ui, canvas, renderOptions);
1646
1614
  return false;
1647
1615
  }
1648
1616
  }
1649
1617
  }
1650
1618
 
1651
- function drawImage(ui, canvas, paint, data) {
1652
- canvas.save();
1653
- canvas.clipUI(ui);
1654
- if (paint.blendMode) canvas.blendMode = paint.blendMode;
1655
- if (data.opacity) canvas.opacity *= data.opacity;
1656
- if (data.transform) canvas.transform(data.transform);
1657
- canvas.drawImage(paint.image.getFull(data.filters), 0, 0, data.width, data.height);
1658
- canvas.restore();
1619
+ function drawImage(paint, _imageScaleX, _imageScaleY, ui, canvas, _renderOptions) {
1620
+ const {data: data, image: image, blendMode: blendMode} = paint, {opacity: opacity, transform: transform} = data, view = image.getFull(data.filters), u = ui.__;
1621
+ let {width: width, height: height} = image, clipUI;
1622
+ if (transform && !transform.onlyScale || (clipUI = u.path || u.cornerRadius) || opacity || blendMode) {
1623
+ canvas.save();
1624
+ clipUI && canvas.clipUI(ui);
1625
+ blendMode && (canvas.blendMode = blendMode);
1626
+ opacity && (canvas.opacity *= opacity);
1627
+ transform && canvas.transform(transform);
1628
+ canvas.drawImage(view, 0, 0, width, height);
1629
+ canvas.restore();
1630
+ } else {
1631
+ if (data.scaleX) width *= data.scaleX, height *= data.scaleY;
1632
+ canvas.drawImage(view, 0, 0, width, height);
1633
+ }
1634
+ }
1635
+
1636
+ function getImageRenderScaleData(paint, ui, canvas, _renderOptions) {
1637
+ const scaleData = ui.getRenderScaleData(true, paint.scaleFixed), {data: data} = paint;
1638
+ if (canvas) {
1639
+ const {pixelRatio: pixelRatio} = canvas;
1640
+ scaleData.scaleX *= pixelRatio;
1641
+ scaleData.scaleY *= pixelRatio;
1642
+ }
1643
+ if (data && data.scaleX) {
1644
+ scaleData.scaleX *= Math.abs(data.scaleX);
1645
+ scaleData.scaleY *= Math.abs(data.scaleY);
1646
+ }
1647
+ return scaleData;
1659
1648
  }
1660
1649
 
1661
1650
  function recycleImage(attrName, data) {
@@ -1687,8 +1676,12 @@ function recycleImage(attrName, data) {
1687
1676
  const PaintImageModule = {
1688
1677
  image: image,
1689
1678
  checkImage: checkImage,
1690
- createPattern: createPattern,
1679
+ drawImage: drawImage,
1680
+ getImageRenderScaleData: getImageRenderScaleData,
1691
1681
  recycleImage: recycleImage,
1682
+ createPatternTask: createPatternTask,
1683
+ createPattern: createPattern,
1684
+ getPatternFixScale: getPatternFixScale,
1692
1685
  createData: createData,
1693
1686
  getPatternData: getPatternData,
1694
1687
  stretchMode: stretchMode,
@@ -2144,10 +2137,8 @@ function createRows(drawData, content, style) {
2144
2137
  bounds = drawData.bounds;
2145
2138
  findMaxWidth = !bounds.width && !style.autoSizeAlign;
2146
2139
  const {__letterSpacing: __letterSpacing, paraIndent: paraIndent, textCase: textCase} = style;
2147
- const {canvas: canvas} = core.Platform;
2148
- const {width: width, height: height} = bounds;
2149
- const charMode = width || height || __letterSpacing || textCase !== "none";
2150
- if (charMode) {
2140
+ const {canvas: canvas} = core.Platform, {width: width} = bounds;
2141
+ if (style.__isCharMode) {
2151
2142
  const wrap = style.textWrap !== "none";
2152
2143
  const breakAll = style.textWrap === "break";
2153
2144
  paraStart = true;
@@ -2276,12 +2267,19 @@ const TextMode = 2;
2276
2267
  function layoutChar(drawData, style, width, _height) {
2277
2268
  const {rows: rows} = drawData;
2278
2269
  const {textAlign: textAlign, paraIndent: paraIndent, letterSpacing: letterSpacing} = style;
2279
- let charX, addWordWidth, indentWidth, mode, wordChar, wordsLength;
2270
+ const justifyLast = width && textAlign.includes("both");
2271
+ const justify = justifyLast || width && textAlign.includes("justify");
2272
+ const justifyLetter = justify && textAlign.includes("letter");
2273
+ let charX, remainingWidth, addWordWidth, addLetterWidth, indentWidth, mode, wordChar, wordsLength, isLastWord, canJustify;
2280
2274
  rows.forEach(row => {
2281
2275
  if (row.words) {
2282
2276
  indentWidth = paraIndent && row.paraStart ? paraIndent : 0, wordsLength = row.words.length;
2283
- addWordWidth = width && (textAlign === "justify" || textAlign === "both") && wordsLength > 1 ? (width - row.width - indentWidth) / (wordsLength - 1) : 0;
2284
- mode = letterSpacing || row.isOverflow ? CharMode : addWordWidth > .01 ? WordMode : TextMode;
2277
+ if (justify) {
2278
+ canJustify = !row.paraEnd || justifyLast;
2279
+ remainingWidth = width - row.width - indentWidth;
2280
+ if (justifyLetter) addLetterWidth = remainingWidth / (row.words.reduce((total, item) => total + item.data.length, 0) - 1); else addWordWidth = wordsLength > 1 ? remainingWidth / (wordsLength - 1) : 0;
2281
+ }
2282
+ mode = letterSpacing || row.isOverflow || justifyLetter ? CharMode : addWordWidth ? WordMode : TextMode;
2285
2283
  if (row.isOverflow && !letterSpacing) row.textMode = true;
2286
2284
  if (mode === TextMode) {
2287
2285
  row.x += indentWidth;
@@ -2299,11 +2297,15 @@ function layoutChar(drawData, style, width, _height) {
2299
2297
  charX = toWordChar(word.data, charX, wordChar);
2300
2298
  if (row.isOverflow || wordChar.char !== " ") row.data.push(wordChar);
2301
2299
  } else {
2302
- charX = toChar(word.data, charX, row.data, row.isOverflow);
2300
+ charX = toChar(word.data, charX, row.data, row.isOverflow, canJustify && addLetterWidth);
2303
2301
  }
2304
- if (addWordWidth && (!row.paraEnd || textAlign === "both") && index !== wordsLength - 1) {
2305
- charX += addWordWidth;
2306
- row.width += addWordWidth;
2302
+ if (canJustify) {
2303
+ isLastWord = index === wordsLength - 1;
2304
+ if (addWordWidth) {
2305
+ if (!isLastWord) charX += addWordWidth, row.width += addWordWidth;
2306
+ } else if (addLetterWidth) {
2307
+ row.width += addLetterWidth * (word.data.length - (isLastWord ? 1 : 0));
2308
+ }
2307
2309
  }
2308
2310
  });
2309
2311
  }
@@ -2329,13 +2331,14 @@ function toWordChar(data, charX, wordChar) {
2329
2331
  return charX;
2330
2332
  }
2331
2333
 
2332
- function toChar(data, charX, rowData, isOverflow) {
2334
+ function toChar(data, charX, rowData, isOverflow, addLetterWidth) {
2333
2335
  data.forEach(char => {
2334
2336
  if (isOverflow || char.char !== " ") {
2335
2337
  char.x = charX;
2336
2338
  rowData.push(char);
2337
2339
  }
2338
2340
  charX += char.width;
2341
+ addLetterWidth && (charX += addLetterWidth);
2339
2342
  });
2340
2343
  return charX;
2341
2344
  }
@@ -2477,10 +2480,10 @@ function getDrawData(content, style) {
2477
2480
  let x = 0, y = 0;
2478
2481
  let width = style.__getInput("width") || 0;
2479
2482
  let height = style.__getInput("height") || 0;
2480
- const {textDecoration: textDecoration, __font: __font, __padding: padding} = style;
2483
+ const {__padding: padding} = style;
2481
2484
  if (padding) {
2482
- if (width) x = padding[left], width -= padding[right] + padding[left]; else if (!style.autoSizeAlign) x = padding[left];
2483
- if (height) y = padding[top], height -= padding[top] + padding[bottom]; else if (!style.autoSizeAlign) y = padding[top];
2485
+ if (width) x = padding[left], width -= padding[right] + padding[left], !width && (width = .01); else if (!style.autoSizeAlign) x = padding[left];
2486
+ if (height) y = padding[top], height -= padding[top] + padding[bottom], !height && (height = .01); else if (!style.autoSizeAlign) y = padding[top];
2484
2487
  }
2485
2488
  const drawData = {
2486
2489
  bounds: {
@@ -2491,14 +2494,14 @@ function getDrawData(content, style) {
2491
2494
  },
2492
2495
  rows: [],
2493
2496
  paraNumber: 0,
2494
- font: core.Platform.canvas.font = __font
2497
+ font: core.Platform.canvas.font = style.__font
2495
2498
  };
2496
2499
  createRows(drawData, content, style);
2497
2500
  if (padding) padAutoText(padding, drawData, style, width, height);
2498
2501
  layoutText(drawData, style);
2499
- layoutChar(drawData, style, width);
2502
+ if (style.__isCharMode) layoutChar(drawData, style, width);
2500
2503
  if (drawData.overflow) clipText(drawData, style, x, width);
2501
- if (textDecoration !== "none") decorationText(drawData, style);
2504
+ if (style.textDecoration !== "none") decorationText(drawData, style);
2502
2505
  return drawData;
2503
2506
  }
2504
2507