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