From c9b6dda8042dcdd478a3218474fcd1656bb87076 Mon Sep 17 00:00:00 2001 From: Hal Canary Date: Tue, 19 Jun 2018 09:53:26 -0400 Subject: SkUtils: safe SkUTF16_NextUnichar Change-Id: Ief70f3055d9612c8934ede967c1048dd7fcb102a Reviewed-on: https://skia-review.googlesource.com/135705 Commit-Queue: Hal Canary Auto-Submit: Hal Canary Reviewed-by: Herb Derby --- src/core/SkDevice.cpp | 47 +++++++++++++++++++++------------------------- src/core/SkUtils.cpp | 52 ++++++++++++++++++++++++++++++++++++++++----------- src/core/SkUtils.h | 2 ++ 3 files changed, 64 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 23560986bc..46404b2887 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -483,33 +483,11 @@ void SkBaseDevice::drawTextOnPath(const void* text, size_t byteLength, } #include "SkUtils.h" -typedef int (*CountTextProc)(const char* text); -static int count_utf16(const char* text) { - const uint16_t* prev = (uint16_t*)text; - (void)SkUTF16_NextUnichar(&prev); - return SkToInt((const char*)prev - text); -} -static int return_4(const char* text) { return 4; } -static int return_2(const char* text) { return 2; } void SkBaseDevice::drawTextRSXform(const void* text, size_t len, const SkRSXform xform[], const SkPaint& paint) { - CountTextProc proc = nullptr; - switch (paint.getTextEncoding()) { - case SkPaint::kUTF8_TextEncoding: - proc = SkUTF8_CountUTF8Bytes; - break; - case SkPaint::kUTF16_TextEncoding: - proc = count_utf16; - break; - case SkPaint::kUTF32_TextEncoding: - proc = return_4; - break; - case SkPaint::kGlyphID_TextEncoding: - proc = return_2; - break; - } - + SkPaint::TextEncoding textEncoding = paint.getTextEncoding(); + const char* end = (const char*)text + len; SkPaint localPaint(paint); SkShader* shader = paint.getShader(); SkScalar pos[2] = {0.0f, 0.0f}; @@ -533,8 +511,25 @@ void SkBaseDevice::drawTextRSXform(const void* text, size_t len, localPaint.setShader(nullptr); // can't handle this xform } } - - int subLen = proc((const char*)text); + int subLen = 0; + switch (textEncoding) { + case SkPaint::kUTF8_TextEncoding: + subLen = SkUTF8_CountUTF8Bytes((const char*)text); + break; + case SkPaint::kUTF16_TextEncoding: + { + const uint16_t* ptr = (const uint16_t*)text; + (void)SkUTF16_NextUnichar(&ptr, (const uint16_t*)end); + subLen = SkToInt((const char*)ptr - (const char*)text); + }; + break; + case SkPaint::kUTF32_TextEncoding: + subLen = 4; + break; + case SkPaint::kGlyphID_TextEncoding: + subLen = 2; + break; + } this->drawPosText(text, subLen, pos, 2, origin, localPaint); text = (const char*)text + subLen; } diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp index e6fafa0b07..c12bac50ff 100644 --- a/src/core/SkUtils.cpp +++ b/src/core/SkUtils.cpp @@ -289,22 +289,52 @@ int SkUTF16_CountUnichars(const void* text, size_t byteLength) { return count; } -SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr) { - SkASSERT(srcPtr && *srcPtr); - +SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr, const uint16_t* endPtr) { + if (!srcPtr || !endPtr) { + return -1; + } const uint16_t* src = *srcPtr; - SkUnichar c = *src++; + if (src >= endPtr) { + return -1; + } + uint16_t c = *src++; + SkUnichar result = c; - SkASSERT(!SkUTF16_IsLowSurrogate(c)); + if (SkUTF16_IsLowSurrogate(c)) { + return -1; // srcPtr should never point at low surrogate. + } if (SkUTF16_IsHighSurrogate(c)) { - unsigned c2 = *src++; - SkASSERT(SkUTF16_IsLowSurrogate(c2)); - - // c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000 - // c = (((c & 0x3FF) + 64) << 10) + (c2 & 0x3FF) - c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00); + if (src == endPtr) { + return -1; // Truncated string. + } + uint16_t low = *src++; + if (!SkUTF16_IsLowSurrogate(low)) { + return -1; + } + /* + [paraphrased from wikipedia] + Take the high surrogate and subtract 0xD800, then multiply by 0x400. + Take the low surrogate and subtract 0xDC00. Add these two results + together, and finally add 0x10000 to get the final decoded codepoint. + + unicode = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000 + unicode = (high * 0x400) - (0xD800 * 0x400) + low - 0xDC00 + 0x10000 + unicode = (high << 10) - (0xD800 << 10) + low - 0xDC00 + 0x10000 + unicode = (high << 10) + low - ((0xD800 << 10) + 0xDC00 - 0x10000) + */ + result = (result << 10) + (SkUnichar)low - ((0xD800 << 10) + 0xDC00 - 0x10000); } *srcPtr = src; + return result; +} + +SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr) { + SkUnichar c = SkUTF16_NextUnichar(srcPtr, *srcPtr + 2); + if (c == -1) { + SkASSERT(false); + ++(*srcPtr); + return 0xFFFD; // REPLACEMENT CHARACTER. + } return c; } diff --git a/src/core/SkUtils.h b/src/core/SkUtils.h index 6037b60c3c..795b47a0c4 100644 --- a/src/core/SkUtils.h +++ b/src/core/SkUtils.h @@ -84,6 +84,8 @@ size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[] = nullptr); int SkUTF16_CountUnichars(const uint16_t utf16[]); // returns the current unichar and then moves past it (*p++) SkUnichar SkUTF16_NextUnichar(const uint16_t**); +SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr, const uint16_t* end); + // this guy backs up to the previus unichar value, and returns it (*--p) SkUnichar SkUTF16_PrevUnichar(const uint16_t**); size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t utf16[] = nullptr); -- cgit v1.2.3