Skip to content

Commit

Permalink
Add a drawSimpleText() method to the Canvas class. (#39)
Browse files Browse the repository at this point in the history
* Add a drawSimpleText() method to the Canvas class.

* Fix build error.
  • Loading branch information
domchen authored Nov 21, 2023
1 parent f8e0d6d commit 8f4ef0f
Show file tree
Hide file tree
Showing 17 changed files with 221 additions and 28 deletions.
9 changes: 9 additions & 0 deletions include/tgfx/core/Canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ class Canvas {
void drawImage(std::shared_ptr<Image> image, SamplingOptions sampling,
const Paint* paint = nullptr);

/**
* Draws text, with origin at (x, y), using clip, matrix, font, and paint. The text must be in
* utf-8 encoding. This function uses the default character-to-glyph mapping from the Typeface in
* font. It does not perform typeface fallback for characters not found in the Typeface. Glyphs
* are positioned based on their default advances.
*/
void drawSimpleText(const std::string& text, float x, float y, const Font& font,
const Paint& paint);

/**
* Draw an array of glyphs with specified font, using current alpha, blend mode, clip and Matrix.
*/
Expand Down
10 changes: 9 additions & 1 deletion include/tgfx/core/Font.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,20 @@ class Font {

/**
* Returns the glyph ID corresponds to the specified glyph name. The glyph name must be in utf-8
* encoding. Returns 0 if the glyph name is not associated with this typeface.
* encoding. Returns 0 if the glyph name is not in this Font.
*/
GlyphID getGlyphID(const std::string& name) const {
return typeface->getGlyphID(name);
}

/**
* Returns the glyph ID corresponds to the specified unicode code point. Returns 0 if the code
* point is not in this Font.
*/
GlyphID getGlyphID(Unichar unichar) const {
return typeface->getGlyphID(unichar);
}

/**
* Returns the bounding box of the specified glyph.
*/
Expand Down
8 changes: 8 additions & 0 deletions include/tgfx/core/TextBlob.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ namespace tgfx {
*/
class TextBlob {
public:
/**
* Creates a new TextBlob from the given text. The text must be in utf-8 encoding. This function
* uses the default character-to-glyph mapping from the Typeface in font. It does not perform
* typeface fallback for characters not found in the Typeface. Glyphs are positioned based on
* their default advances.
*/
static std::shared_ptr<TextBlob> MakeFrom(const std::string& text, const Font& font);

/**
* Creates a new TextBlob from the given glyphs, positions and text font.
*/
Expand Down
17 changes: 14 additions & 3 deletions include/tgfx/core/Typeface.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@

namespace tgfx {
/**
* 16 bit unsigned integer to hold a glyph index
* 16 bit unsigned integer to hold a glyph index.
*/
typedef uint16_t GlyphID;

/**
* 32-bit signed integer to hold a UTF-32 code unit.
*/
typedef int32_t Unichar;

typedef uint32_t FontTableTag;

/**
Expand Down Expand Up @@ -101,9 +106,15 @@ class Typeface {

/**
* Returns the glyph ID corresponds to the specified glyph name. The glyph name must be in utf-8
* encoding. Returns 0 if the glyph name is not associated with this typeface.
* encoding. Returns 0 if the glyph name is not in this typeface.
*/
GlyphID getGlyphID(const std::string& name) const;

/**
* Returns the glyph ID corresponds to the specified unicode code point. Returns 0 if the code
* point is not in this typeface.
*/
virtual GlyphID getGlyphID(const std::string& name) const = 0;
virtual GlyphID getGlyphID(Unichar unichar) const = 0;

virtual std::shared_ptr<Data> getBytes() const = 0;

Expand Down
6 changes: 6 additions & 0 deletions include/tgfx/utils/UTF.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <cstddef>
#include <cstdint>
#include <string>

namespace tgfx {
class UTF {
Expand All @@ -36,6 +37,11 @@ class UTF {
* to end and return -1.
*/
static int32_t NextUTF8(const char** ptr, const char* end);

/**
* Given a unicode codepoint, return the UTF-8 string.
*/
static std::string ToUTF8(int32_t unichar);
};

} // namespace tgfx
11 changes: 11 additions & 0 deletions src/core/Canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
#include "tgfx/core/PathEffect.h"
#include "tgfx/core/TextBlob.h"
#include "tgfx/gpu/Surface.h"
#include "tgfx/utils/UTF.h"
#include "utils/MathExtra.h"
#include "utils/SimpleTextShaper.h"

namespace tgfx {
static uint32_t NextClipID() {
Expand Down Expand Up @@ -547,6 +549,15 @@ void Canvas::drawMask(const Rect& bounds, std::shared_ptr<Texture> mask, GpuPain
setMatrix(oldMatrix);
}

void Canvas::drawSimpleText(const std::string& text, float x, float y, const tgfx::Font& font,
const tgfx::Paint& paint) {
auto [glyphIDs, positions] = SimpleTextShaper::Shape(text, font);
for (auto& position : positions) {
position.offset(x, y);
}
drawGlyphs(glyphIDs.data(), positions.data(), glyphIDs.size(), font, paint);
}

void Canvas::drawGlyphs(const GlyphID glyphIDs[], const Point positions[], size_t glyphCount,
const Font& font, const Paint& paint) {
if (nothingToDraw(paint) || glyphCount == 0) {
Expand Down
13 changes: 13 additions & 0 deletions src/core/SimpleTextBlob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,21 @@

#include "core/SimpleTextBlob.h"
#include "tgfx/core/PathEffect.h"
#include "utils/SimpleTextShaper.h"

namespace tgfx {
std::shared_ptr<TextBlob> TextBlob::MakeFrom(const std::string& text, const tgfx::Font& font) {
auto [glyphIDs, positions] = SimpleTextShaper::Shape(text, font);
if (glyphIDs.empty()) {
return nullptr;
}
auto textBlob = std::make_shared<SimpleTextBlob>();
textBlob->glyphIDs = std::move(glyphIDs);
textBlob->positions = std::move(positions);
textBlob->font = font;
return textBlob;
}

std::shared_ptr<TextBlob> TextBlob::MakeFrom(const GlyphID glyphIDs[], const Point positions[],
size_t glyphCount, const Font& font) {
if (glyphCount == 0) {
Expand Down
35 changes: 35 additions & 0 deletions src/core/Typeface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tencent is pleased to support the open source community by making tgfx available.
//
// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// unless required by applicable law or agreed to in writing, software distributed under the
// license is distributed on an "as is" basis, without warranties or conditions of any kind,
// either express or implied. see the license for the specific language governing permissions
// and limitations under the license.
//
/////////////////////////////////////////////////////////////////////////////////////////////////

#include "tgfx/core/Typeface.h"
#include "tgfx/utils/UTF.h"

namespace tgfx {
GlyphID Typeface::getGlyphID(const std::string& name) const {
if (name.empty()) {
return 0;
}
auto count = UTF::CountUTF8(name.c_str(), name.size());
if (count <= 0) {
return 0;
}
const char* start = name.data();
auto unichar = UTF::NextUTF8(&start, start + name.size());
return getGlyphID(unichar);
}
} // namespace tgfx
46 changes: 46 additions & 0 deletions src/utils/SimpleTextShaper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tencent is pleased to support the open source community by making tgfx available.
//
// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// unless required by applicable law or agreed to in writing, software distributed under the
// license is distributed on an "as is" basis, without warranties or conditions of any kind,
// either express or implied. see the license for the specific language governing permissions
// and limitations under the license.
//
/////////////////////////////////////////////////////////////////////////////////////////////////

#include "SimpleTextShaper.h"
#include "tgfx/utils/UTF.h"

namespace tgfx {
std::pair<std::vector<GlyphID>, std::vector<Point>> SimpleTextShaper::Shape(
const std::string& text, const tgfx::Font& font) {
const char* textStart = text.data();
const char* textStop = textStart + text.size();
std::vector<GlyphID> glyphs = {};
std::vector<Point> positions = {};
auto emptyGlyphID = font.getGlyphID(" ");
auto emptyAdvance = font.getAdvance(emptyGlyphID);
float xOffset = 0;
while (textStart < textStop) {
auto unichar = UTF::NextUTF8(&textStart, textStop);
auto glyphID = font.getGlyphID(unichar);
if (glyphID > 0) {
glyphs.push_back(glyphID);
positions.push_back(Point::Make(xOffset, 0.0f));
auto advance = font.getAdvance(glyphID);
xOffset += advance;
} else {
xOffset += emptyAdvance;
}
}
return {glyphs, positions};
}
} // namespace tgfx
29 changes: 29 additions & 0 deletions src/utils/SimpleTextShaper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tencent is pleased to support the open source community by making tgfx available.
//
// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// unless required by applicable law or agreed to in writing, software distributed under the
// license is distributed on an "as is" basis, without warranties or conditions of any kind,
// either express or implied. see the license for the specific language governing permissions
// and limitations under the license.
//
/////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#include "tgfx/core/Font.h"

namespace tgfx {
class SimpleTextShaper {
public:
static std::pair<std::vector<GlyphID>, std::vector<Point>> Shape(const std::string& text,
const Font& font);
};
} // namespace tgfx
25 changes: 25 additions & 0 deletions src/utils/UTF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,29 @@ int32_t UTF::NextUTF8(const char** ptr, const char* end) {
*ptr = (char*)p + 1;
return c;
}

std::string UTF::ToUTF8(int32_t unichar) {
if ((uint32_t)unichar > 0x10FFFF) {
return "";
}
if (unichar <= 127) {
return {(char)unichar};
}
char tmp[4];
char* p = tmp;
size_t count = 1;
while (unichar > 0x7F >> count) {
*p++ = (char)(0x80 | (unichar & 0x3F));
unichar >>= 6;
count += 1;
}
char utf8[4];
p = tmp;
char* to = utf8 + count;
while (p < tmp + count - 1) {
*--to = *p++;
}
*--to = (char)(~(0xFF >> count) | unichar);
return {utf8, count};
}
} // namespace tgfx
6 changes: 2 additions & 4 deletions src/vectors/coregraphics/CGTypeface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,9 @@ static size_t ToUTF16(int32_t uni, uint16_t utf16[2]) {
return 1 + extra;
}

GlyphID CGTypeface::getGlyphID(const std::string& name) const {
const char* start = &(name[0]);
auto uni = UTF::NextUTF8(&start, start + name.size());
GlyphID CGTypeface::getGlyphID(Unichar unichar) const {
UniChar utf16[2] = {0, 0};
auto srcCount = ToUTF16(uni, utf16);
auto srcCount = ToUTF16(unichar, utf16);
GlyphID macGlyphs[2] = {0, 0};
CTFontGetGlyphsForCharacters(ctFont, utf16, macGlyphs, static_cast<CFIndex>(srcCount));
return macGlyphs[0];
Expand Down
2 changes: 1 addition & 1 deletion src/vectors/coregraphics/CGTypeface.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class CGTypeface : public Typeface {

bool hasColor() const override;

GlyphID getGlyphID(const std::string& name) const override;
GlyphID getGlyphID(Unichar unichar) const override;

std::shared_ptr<Data> getBytes() const override;

Expand Down
13 changes: 2 additions & 11 deletions src/vectors/freetype/FTTypeface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class EmptyTypeface : public Typeface {
return false;
}

GlyphID getGlyphID(const std::string&) const override {
GlyphID getGlyphID(Unichar) const override {
return 0;
}

Expand Down Expand Up @@ -161,16 +161,7 @@ bool FTTypeface::hasColor() const {
return FT_HAS_COLOR(_face->face);
}

GlyphID FTTypeface::getGlyphID(const std::string& name) const {
if (name.empty()) {
return 0;
}
auto count = UTF::CountUTF8(name.c_str(), name.size());
if (count > 1 || count <= 0) {
return 0;
}
const char* start = name.data();
auto unichar = UTF::NextUTF8(&start, start + name.size());
GlyphID FTTypeface::getGlyphID(Unichar unichar) const {
return FT_Get_Char_Index(_face->face, static_cast<FT_ULong>(unichar));
}

Expand Down
2 changes: 1 addition & 1 deletion src/vectors/freetype/FTTypeface.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class FTTypeface : public Typeface {

bool hasColor() const override;

GlyphID getGlyphID(const std::string& name) const override;
GlyphID getGlyphID(Unichar unichar) const override;

std::shared_ptr<Data> getBytes() const override;

Expand Down
Loading

0 comments on commit 8f4ef0f

Please sign in to comment.