2019-10-03 15:22:08 +00:00
|
|
|
(function(CanvasKit){
|
|
|
|
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
|
|
|
|
CanvasKit._extraInitializations.push(function() {
|
|
|
|
|
|
|
|
CanvasKit.Paragraph.prototype.getRectsForRange = function(start, end, hStyle, wStyle) {
|
|
|
|
/**
|
|
|
|
* This is bytes, but we'll want to think about them as float32s
|
|
|
|
* @type {Float32Array}
|
|
|
|
*/
|
|
|
|
var floatArray = this._getRectsForRange(start, end, hStyle, wStyle);
|
|
|
|
|
|
|
|
if (!floatArray || !floatArray.length) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
var ret = [];
|
2019-11-20 13:27:10 +00:00
|
|
|
for (var i = 0; i < floatArray.length; i+=5) {
|
|
|
|
var r = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]);
|
2020-05-29 15:06:19 +00:00
|
|
|
if (floatArray[i+4] === 0) {
|
2019-11-20 13:27:10 +00:00
|
|
|
r['direction'] = CanvasKit.TextDirection.RTL;
|
|
|
|
} else {
|
|
|
|
r['direction'] = CanvasKit.TextDirection.LTR;
|
|
|
|
}
|
|
|
|
ret.push(r);
|
2019-10-03 15:22:08 +00:00
|
|
|
}
|
|
|
|
CanvasKit._free(floatArray.byteOffset);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-08-04 20:21:09 +00:00
|
|
|
// Registers the font (provided as an arrayBuffer) with the alias `family`.
|
|
|
|
CanvasKit.TypefaceFontProvider.prototype.registerFont = function(font, family) {
|
|
|
|
var typeface = CanvasKit.SkFontMgr.RefDefault().MakeTypefaceFromData(font);
|
|
|
|
if (!typeface) {
|
|
|
|
SkDebug('Could not decode font data');
|
|
|
|
// We do not need to free the data since the C++ will do that for us
|
|
|
|
// when the font is deleted (or fails to decode);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var familyPtr = cacheOrCopyString(family);
|
|
|
|
this._registerFont(typeface, familyPtr);
|
|
|
|
}
|
|
|
|
|
2019-10-03 15:22:08 +00:00
|
|
|
// These helpers fill out all fields, because emscripten complains if we
|
|
|
|
// have undefined and it expects, for example, a float.
|
|
|
|
CanvasKit.ParagraphStyle = function(s) {
|
|
|
|
// Use [''] to tell closure not to minify the names
|
2019-10-21 14:50:26 +00:00
|
|
|
// TODO(kjlubick): strutStyle
|
|
|
|
s['disableHinting'] = s['disableHinting'] || false;
|
|
|
|
if (s['ellipsis']) {
|
|
|
|
var str = s['ellipsis'];
|
2020-05-14 12:27:53 +00:00
|
|
|
s['_ellipsisPtr'] = cacheOrCopyString(str);
|
|
|
|
s['_ellipsisLen'] = lengthBytesUTF8(str) + 1; // add 1 for the null terminator.
|
2019-10-21 14:50:26 +00:00
|
|
|
} else {
|
|
|
|
s['_ellipsisPtr'] = nullptr;
|
|
|
|
s['_ellipsisLen'] = 0;
|
|
|
|
}
|
|
|
|
|
2019-10-03 15:22:08 +00:00
|
|
|
s['heightMultiplier'] = s['heightMultiplier'] || 0;
|
|
|
|
s['maxLines'] = s['maxLines'] || 0;
|
|
|
|
s['textAlign'] = s['textAlign'] || CanvasKit.TextAlign.Start;
|
2019-10-21 14:50:26 +00:00
|
|
|
s['textDirection'] = s['textDirection'] || CanvasKit.TextDirection.LTR;
|
2019-10-03 15:22:08 +00:00
|
|
|
s['textStyle'] = CanvasKit.TextStyle(s['textStyle']);
|
|
|
|
return s;
|
2020-05-14 12:27:53 +00:00
|
|
|
};
|
2019-10-03 15:22:08 +00:00
|
|
|
|
2019-10-21 14:50:26 +00:00
|
|
|
function fontStyle(s) {
|
|
|
|
s = s || {};
|
|
|
|
// Can't check for falsey as 0 width means "invisible".
|
|
|
|
if (s['weight'] === undefined) {
|
|
|
|
s['weight'] = CanvasKit.FontWeight.Normal;
|
|
|
|
}
|
|
|
|
s['width'] = s['width'] || CanvasKit.FontWidth.Normal;
|
|
|
|
s['slant'] = s['slant'] || CanvasKit.FontSlant.Upright;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2019-10-03 15:22:08 +00:00
|
|
|
CanvasKit.TextStyle = function(s) {
|
|
|
|
// Use [''] to tell closure not to minify the names
|
2020-06-11 17:48:16 +00:00
|
|
|
if (!s['color']) {
|
2019-10-21 14:50:26 +00:00
|
|
|
s['color'] = CanvasKit.BLACK;
|
|
|
|
}
|
2020-05-04 20:46:17 +00:00
|
|
|
|
2019-10-03 15:22:08 +00:00
|
|
|
s['decoration'] = s['decoration'] || 0;
|
|
|
|
s['decorationThickness'] = s['decorationThickness'] || 0;
|
|
|
|
s['fontSize'] = s['fontSize'] || 0;
|
2019-10-21 14:50:26 +00:00
|
|
|
s['fontStyle'] = fontStyle(s['fontStyle']);
|
2019-10-03 15:22:08 +00:00
|
|
|
return s;
|
2020-05-14 12:27:53 +00:00
|
|
|
};
|
2019-10-03 15:22:08 +00:00
|
|
|
|
|
|
|
// returns a pointer to a place on the heap that has an array
|
|
|
|
// of char* (effectively a char**). For now, this does the naive thing
|
|
|
|
// and depends on the string being null-terminated. This should be used
|
|
|
|
// for simple, well-formed things (e.g. font-families), not arbitrary
|
|
|
|
// text that should be drawn. If we need this to handle more complex
|
|
|
|
// strings, it should return two pointers, a pointer of the
|
|
|
|
// string array and a pointer to an array of the strings byte lengths.
|
|
|
|
function naiveCopyStrArray(strings) {
|
|
|
|
if (!strings || !strings.length) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
var sPtrs = [];
|
|
|
|
for (var i = 0; i < strings.length; i++) {
|
2020-05-14 12:27:53 +00:00
|
|
|
var strPtr = cacheOrCopyString(strings[i]);
|
2019-10-03 15:22:08 +00:00
|
|
|
sPtrs.push(strPtr);
|
|
|
|
}
|
[canvaskit] Fix infrequent crash in SkFontMgr.FromData
The bug here is very subtle, as is the mitigation.
Quick background on WASM memory, there is an object
called wasmMemory (which might be hoisted into scope for
CanvasKit's pre-js functions), of type WebAssembly.Memory
which is a resizable ArrayBuffer. Emscripten provides the
JS code to initialize this and handle size increases.
Emscripten also provides TypedArray "views" into this buffer.
These are called CanvasKit.HEAPU8, CanvasKit.HEAPF32, etc.
When there is a call to CanvasKit._malloc, wasmMemory may
be resized. If that happens, the previous TypedArray views
become invalid. However, in the same call to _malloc,
emscripten will refresh the views [1]. So, dealing with
CanvasKit.HEAPU8 directly (quick aside, we never expect clients
to mess with these views, only us in our glue JS code
[e.g. interface.js]), should always be safe because if they
were to be invalidated in a call to _malloc, the views would
be refreshed before _malloc continues.
The problem that existed before was when we were passing
CanvasKit.HEAP* as a parameter to a function, in which the
function would call _malloc before using the typed array
parameter:
//... let us suppose wasmMemory is backed by ArrayBuffer D
copy1dArray(arr, HEAPU32);
// The HEAPU32 TypedArray (backed by ArrayBuffer D) is stored
// to a function parameter "dest"
function copy1dArray(arr, dest, ptr) {
// ...
if (!ptr) {
ptr = CanvasKit._malloc(arr.length * dest.BYTES_PER_ELEMENT);
// Suppose _malloc needs to resize wasmMemory and is
// now backed by ArrayBuffer E.
// Note: The field CanvasKit.HEAPU32 is correctly backed
// by ArrayBuffer E, but variable dest still points to a
// TypedArray backed by ArrayBuffer D.
}
// dest.set will fail with a "neutered ArrayBuffer" error
// because ArrayBuffer D is effectively gone (replaced by E).
dest.set(arr, ptr / dest.BYTES_PER_ELEMENT);
The fix here is to pass in the field name indicating the TypedArray
view we want to write our data into instead of using the
view itself as the parameter.
[1] https://github.com/emscripten-core/emscripten/blob/e4271595539cf1ca81128280cdc72f7245e700a0/src/preamble.js#L344
Change-Id: I46cfb98f8bdf928b61690a5ced034a5961356398
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/294516
Reviewed-by: Nathaniel Nifong <nifong@google.com>
2020-06-05 11:13:48 +00:00
|
|
|
return copy1dArray(sPtrs, "HEAPU32");
|
2019-10-03 15:22:08 +00:00
|
|
|
}
|
2020-05-04 20:46:17 +00:00
|
|
|
|
2020-05-14 12:27:53 +00:00
|
|
|
// maps string -> malloc'd pointer
|
|
|
|
var stringCache = {};
|
|
|
|
|
|
|
|
// cacheOrCopyString copies a string from JS into WASM on the heap and returns the pointer
|
|
|
|
// to the memory of the string. It is expected that a caller to this helper will *not* free
|
|
|
|
// that memory, so it is cached. Thus, if a future call to this function with the same string
|
|
|
|
// will return the cached pointer, preventing the memory usage from growing unbounded (in
|
|
|
|
// a normal use case).
|
|
|
|
function cacheOrCopyString(str) {
|
|
|
|
if (stringCache[str]) {
|
|
|
|
return stringCache[str];
|
|
|
|
}
|
|
|
|
// Add 1 for null terminator, which we need when copying/converting
|
|
|
|
var strLen = lengthBytesUTF8(str) + 1;
|
|
|
|
var strPtr = CanvasKit._malloc(strLen);
|
|
|
|
stringToUTF8(str, strPtr, strLen);
|
|
|
|
stringCache[str] = strPtr;
|
|
|
|
return strPtr;
|
|
|
|
}
|
|
|
|
|
2020-06-01 15:25:47 +00:00
|
|
|
// These scratch arrays are allocated once to copy the color data into, which saves us
|
|
|
|
// having to free them after every invocation.
|
|
|
|
var scratchForegroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
|
|
|
|
var scratchBackgroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
|
|
|
|
|
2020-05-14 12:27:53 +00:00
|
|
|
function copyArrays(textStyle) {
|
|
|
|
// These color fields were arrays, but will set to WASM pointers before we pass this
|
2020-05-04 20:46:17 +00:00
|
|
|
// object over the WASM interface.
|
2020-06-01 15:25:47 +00:00
|
|
|
textStyle['_colorPtr'] = copyColorToWasm(textStyle['color']);
|
2020-05-14 12:27:53 +00:00
|
|
|
textStyle['_foregroundColorPtr'] = nullptr; // nullptr is 0, from helper.js
|
|
|
|
textStyle['_backgroundColorPtr'] = nullptr;
|
2020-06-01 15:25:47 +00:00
|
|
|
if (textStyle['foregroundColor']) {
|
|
|
|
textStyle['_foregroundColorPtr'] = copyColorToWasm(textStyle['foregroundColor'], scratchForegroundColorPtr);
|
2020-05-04 20:46:17 +00:00
|
|
|
}
|
2020-06-01 15:25:47 +00:00
|
|
|
if (textStyle['backgroundColor']) {
|
|
|
|
textStyle['_backgroundColorPtr'] = copyColorToWasm(textStyle['backgroundColor'], scratchBackgroundColorPtr);
|
2020-05-14 12:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Array.isArray(textStyle['fontFamilies']) && textStyle['fontFamilies'].length) {
|
|
|
|
textStyle['_fontFamiliesPtr'] = naiveCopyStrArray(textStyle['fontFamilies']);
|
|
|
|
textStyle['_fontFamiliesLen'] = textStyle['fontFamilies'].length;
|
|
|
|
} else {
|
|
|
|
textStyle['_fontFamiliesPtr'] = nullptr;
|
|
|
|
textStyle['_fontFamiliesLen'] = 0;
|
|
|
|
SkDebug('no font families provided, text may draw wrong or not at all');
|
2020-05-04 20:46:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 12:27:53 +00:00
|
|
|
function freeArrays(textStyle) {
|
|
|
|
// The font family strings will get copied to a vector on the C++ side, which is owned by
|
|
|
|
// the text style.
|
|
|
|
CanvasKit._free(textStyle['_fontFamiliesPtr']);
|
2020-05-04 20:46:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CanvasKit.ParagraphBuilder.Make = function(paragraphStyle, fontManager) {
|
2020-05-14 12:27:53 +00:00
|
|
|
copyArrays(paragraphStyle['textStyle']);
|
2020-05-04 20:46:17 +00:00
|
|
|
|
|
|
|
var result = CanvasKit.ParagraphBuilder._Make(paragraphStyle, fontManager);
|
2020-05-14 12:27:53 +00:00
|
|
|
freeArrays(paragraphStyle['textStyle']);
|
2020-05-04 20:46:17 +00:00
|
|
|
return result;
|
2020-05-14 12:27:53 +00:00
|
|
|
};
|
2020-05-04 20:46:17 +00:00
|
|
|
|
2020-08-04 20:21:09 +00:00
|
|
|
CanvasKit.ParagraphBuilder.MakeFromFontProvider = function(paragraphStyle, fontProvider) {
|
|
|
|
copyArrays(paragraphStyle['textStyle']);
|
|
|
|
|
|
|
|
var result = CanvasKit.ParagraphBuilder._MakeFromFontProvider(paragraphStyle, fontProvider);
|
|
|
|
freeArrays(paragraphStyle['textStyle']);
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
2020-05-04 20:46:17 +00:00
|
|
|
CanvasKit.ParagraphBuilder.prototype.pushStyle = function(textStyle) {
|
2020-05-14 12:27:53 +00:00
|
|
|
copyArrays(textStyle);
|
|
|
|
this._pushStyle(textStyle);
|
|
|
|
freeArrays(textStyle);
|
2020-05-04 20:46:17 +00:00
|
|
|
}
|
2020-08-04 13:06:54 +00:00
|
|
|
|
|
|
|
CanvasKit.ParagraphBuilder.prototype.pushPaintStyle = function(textStyle, fg, bg) {
|
|
|
|
copyArrays(textStyle);
|
|
|
|
this._pushPaintStyle(textStyle, fg, bg);
|
|
|
|
freeArrays(textStyle);
|
|
|
|
}
|
2019-10-03 15:22:08 +00:00
|
|
|
});
|
2020-05-14 12:27:53 +00:00
|
|
|
}(Module)); // When this file is loaded in, the high level object is "Module";
|