forked from firka/flutter
A basket of features: itemization, bounds, refcount
This patch improves script run itemization and also exposes metrics and bounds for layouts. In addition, there is a fair amount of internal cleanup, including ref counting, and making the MinikinFont abstraction strong enough to support both FreeType and Skia implementations. There is also a sample implementation using Skia, in the sample directory. As part of its functionality, his patch measures the bounds of the layout and gives access through Layout::GetBounds(). The corresponding method is not implemented in the FreeType-only implementation of MinikinFont, so that will probably have to be fixed. Change-Id: Ib1a3fe9d7c90519ac651fb4aa957848e4bb758ec
This commit is contained in:
@@ -57,32 +57,50 @@ struct LayoutGlyph {
|
||||
|
||||
class Layout {
|
||||
public:
|
||||
|
||||
void dump() const;
|
||||
void setFontCollection(const FontCollection *collection);
|
||||
void setFontCollection(const FontCollection* collection);
|
||||
void doLayout(const uint16_t* buf, size_t nchars);
|
||||
void draw(Bitmap*, int x0, int y0) const;
|
||||
void setProperties(const std::string css);
|
||||
|
||||
float getAdvance() const;
|
||||
|
||||
// This must be called before any invocations.
|
||||
// TODO: probably have a factory instead
|
||||
static void init();
|
||||
|
||||
// public accessors
|
||||
size_t nGlyphs() const;
|
||||
// Does not bump reference; ownership is still layout
|
||||
MinikinFont *getFont(int i) const;
|
||||
unsigned int getGlyphId(int i) const;
|
||||
float getX(int i) const;
|
||||
float getY(int i) const;
|
||||
|
||||
float getAdvance() const;
|
||||
|
||||
// Get advances, copying into caller-provided buffer. The size of this
|
||||
// buffer must match the length of the string (nchars arg to doLayout).
|
||||
void getAdvances(float* advances);
|
||||
|
||||
void getBounds(MinikinRect* rect);
|
||||
|
||||
private:
|
||||
// Find a face in the mFaces vector, or create a new entry
|
||||
int findFace(MinikinFont* face, MinikinPaint* paint);
|
||||
|
||||
CssProperties mProps; // TODO: want spans
|
||||
std::vector<LayoutGlyph> mGlyphs;
|
||||
std::vector<float> mAdvances;
|
||||
|
||||
// In future, this will be some kind of mapping from the
|
||||
// identifier used to represent font-family to a font collection.
|
||||
// But for the time being, it should be ok to have just one
|
||||
// per layout.
|
||||
const FontCollection *mCollection;
|
||||
const FontCollection* mCollection;
|
||||
std::vector<MinikinFont *> mFaces;
|
||||
std::vector<hb_font_t *> mHbFonts;
|
||||
float mAdvance;
|
||||
MinikinRect mBounds;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -31,6 +31,29 @@ struct MinikinPaint {
|
||||
// todo: skew, stretch, hinting
|
||||
};
|
||||
|
||||
struct MinikinRect {
|
||||
float mLeft, mTop, mRight, mBottom;
|
||||
bool isEmpty() const {
|
||||
return mLeft == mRight || mTop == mBottom;
|
||||
}
|
||||
void set(const MinikinRect& r) {
|
||||
mLeft = r.mLeft;
|
||||
mTop = r.mTop;
|
||||
mRight = r.mRight;
|
||||
mBottom = r.mBottom;
|
||||
}
|
||||
void offset(float dx, float dy) {
|
||||
mLeft += dx;
|
||||
mTop += dy;
|
||||
mRight += dx;
|
||||
mBottom += dy;
|
||||
}
|
||||
void setEmpty() {
|
||||
mLeft = mTop = mRight = mBottom = 0;
|
||||
}
|
||||
void join(const MinikinRect& r);
|
||||
};
|
||||
|
||||
class MinikinFontFreeType;
|
||||
|
||||
class MinikinFont {
|
||||
@@ -38,6 +61,8 @@ public:
|
||||
void Ref() { mRefcount_++; }
|
||||
void Unref() { if (--mRefcount_ == 0) { delete this; } }
|
||||
|
||||
MinikinFont() : mRefcount_(1) { }
|
||||
|
||||
virtual ~MinikinFont() { };
|
||||
|
||||
virtual bool GetGlyph(uint32_t codepoint, uint32_t *glyph) const = 0;
|
||||
@@ -45,6 +70,9 @@ public:
|
||||
virtual float GetHorizontalAdvance(uint32_t glyph_id,
|
||||
const MinikinPaint &paint) const = 0;
|
||||
|
||||
virtual void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
|
||||
const MinikinPaint &paint) const = 0;
|
||||
|
||||
// If buf is NULL, just update size
|
||||
virtual bool GetTable(uint32_t tag, uint8_t *buf, size_t *size) = 0;
|
||||
|
||||
|
||||
@@ -32,13 +32,15 @@ LOCAL_MODULE := libminikin
|
||||
LOCAL_C_INCLUDES += \
|
||||
external/harfbuzz_ng/src \
|
||||
external/freetype/include \
|
||||
external/icu4c/common \
|
||||
frameworks/minikin/include
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libharfbuzz_ng \
|
||||
libft2 \
|
||||
liblog \
|
||||
libpng \
|
||||
libz \
|
||||
libstlport
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#include <stdio.h> // for debugging - remove
|
||||
#endif
|
||||
// #define VERBOSE_DEBUG
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include <minikin/CmapCoverage.h>
|
||||
#include <minikin/FontCollection.h>
|
||||
@@ -35,7 +36,7 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
|
||||
vector<uint32_t> lastChar;
|
||||
size_t nTypefaces = typefaces.size();
|
||||
#ifdef VERBOSE_DEBUG
|
||||
printf("nTypefaces = %d\n", nTypefaces);
|
||||
ALOGD("nTypefaces = %d\n", nTypefaces);
|
||||
#endif
|
||||
const FontStyle defaultStyle;
|
||||
for (size_t i = 0; i < nTypefaces; i++) {
|
||||
@@ -47,7 +48,7 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
|
||||
instance->mCoverage = new SparseBitSet;
|
||||
MinikinFont* typeface = family->getClosestMatch(defaultStyle);
|
||||
#ifdef VERBOSE_DEBUG
|
||||
printf("closest match = %x, family size = %d\n", typeface, family->getNumFonts());
|
||||
ALOGD("closest match = %p, family size = %d\n", typeface, family->getNumFonts());
|
||||
#endif
|
||||
const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
|
||||
size_t cmapSize = 0;
|
||||
@@ -56,7 +57,7 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
|
||||
ok = typeface->GetTable(cmapTag, cmapData.get(), &cmapSize);
|
||||
CmapCoverage::getCoverage(*instance->mCoverage, cmapData.get(), cmapSize);
|
||||
#ifdef VERBOSE_DEBUG
|
||||
printf("font coverage length=%d, first ch=%x\n", instance->mCoverage->length(),
|
||||
ALOGD("font coverage length=%d, first ch=%x\n", instance->mCoverage->length(),
|
||||
instance->mCoverage->nextSetBit(0));
|
||||
#endif
|
||||
mMaxChar = max(mMaxChar, instance->mCoverage->length());
|
||||
@@ -70,7 +71,7 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
|
||||
mRanges.push_back(dummy);
|
||||
Range* range = &mRanges.back();
|
||||
#ifdef VERBOSE_DEBUG
|
||||
printf("i=%d: range start = %d\n", i, offset);
|
||||
ALOGD("i=%d: range start = %d\n", i, offset);
|
||||
#endif
|
||||
range->start = offset;
|
||||
for (size_t j = 0; j < nTypefaces; j++) {
|
||||
@@ -80,7 +81,7 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
|
||||
offset++;
|
||||
uint32_t nextChar = instance->mCoverage->nextSetBit((i + 1) << kLogCharsPerPage);
|
||||
#ifdef VERBOSE_DEBUG
|
||||
printf("nextChar = %d (j = %d)\n", nextChar, j);
|
||||
ALOGD("nextChar = %d (j = %d)\n", nextChar, j);
|
||||
#endif
|
||||
lastChar[j] = nextChar;
|
||||
}
|
||||
@@ -102,7 +103,7 @@ const FontFamily* FontCollection::getFamilyForChar(uint32_t ch) const {
|
||||
}
|
||||
const Range& range = mRanges[ch >> kLogCharsPerPage];
|
||||
#ifdef VERBOSE_DEBUG
|
||||
printf("querying range %d:%d\n", range.start, range.end);
|
||||
ALOGD("querying range %d:%d\n", range.start, range.end);
|
||||
#endif
|
||||
for (size_t i = range.start; i < range.end; i++) {
|
||||
const FontInstance* instance = mInstanceVec[i];
|
||||
|
||||
@@ -51,7 +51,6 @@ bool FontFamily::addFont(MinikinFont* typeface) {
|
||||
|
||||
void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
|
||||
mFonts.push_back(Font(typeface, style));
|
||||
ALOGD("added font, mFonts.size() = %d", mFonts.size());
|
||||
}
|
||||
|
||||
// Compute a matching metric between two styles - 0 is an exact match
|
||||
|
||||
@@ -14,12 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <iostream> // for debugging
|
||||
#include <stdio.h> // ditto
|
||||
|
||||
#include <hb-icu.h>
|
||||
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <minikin/MinikinFontFreeType.h>
|
||||
#include <minikin/Layout.h>
|
||||
|
||||
@@ -31,6 +38,8 @@ namespace android {
|
||||
// TODO: globals are not cool, move to a factory-ish object
|
||||
hb_buffer_t* buffer = 0;
|
||||
|
||||
Mutex gLock;
|
||||
|
||||
Bitmap::Bitmap(int width, int height) : width(width), height(height) {
|
||||
buf = new uint8_t[width * height]();
|
||||
}
|
||||
@@ -69,11 +78,23 @@ void Bitmap::drawGlyph(const GlyphBitmap& bitmap, int x, int y) {
|
||||
}
|
||||
}
|
||||
|
||||
void MinikinRect::join(const MinikinRect& r) {
|
||||
if (isEmpty()) {
|
||||
set(r);
|
||||
} else if (!r.isEmpty()) {
|
||||
mLeft = std::min(mLeft, r.mLeft);
|
||||
mTop = std::min(mTop, r.mTop);
|
||||
mRight = std::max(mRight, r.mRight);
|
||||
mBottom = std::max(mBottom, r.mBottom);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: the actual initialization is deferred, maybe make this explicit
|
||||
void Layout::init() {
|
||||
buffer = hb_buffer_create();
|
||||
}
|
||||
|
||||
void Layout::setFontCollection(const FontCollection *collection) {
|
||||
ALOGD("setFontCollection(%p)", collection);
|
||||
mCollection = collection;
|
||||
}
|
||||
|
||||
@@ -193,8 +214,76 @@ static FontStyle styleFromCss(const CssProperties &props) {
|
||||
return FontStyle(weight, italic);
|
||||
}
|
||||
|
||||
static hb_script_t codePointToScript(hb_codepoint_t codepoint) {
|
||||
static hb_unicode_funcs_t *u = 0;
|
||||
if (!u) {
|
||||
u = hb_icu_get_unicode_funcs();
|
||||
}
|
||||
return hb_unicode_script(u, codepoint);
|
||||
}
|
||||
|
||||
static hb_codepoint_t decodeUtf16(const uint16_t *chars, size_t len, ssize_t *iter) {
|
||||
const uint16_t v = chars[(*iter)++];
|
||||
// test whether v in (0xd800..0xdfff), lead or trail surrogate
|
||||
if ((v & 0xf800) == 0xd800) {
|
||||
// test whether v in (0xd800..0xdbff), lead surrogate
|
||||
if (size_t(*iter) < len && (v & 0xfc00) == 0xd800) {
|
||||
const uint16_t v2 = chars[(*iter)++];
|
||||
// test whether v2 in (0xdc00..0xdfff), trail surrogate
|
||||
if ((v2 & 0xfc00) == 0xdc00) {
|
||||
// (0xd800 0xdc00) in utf-16 maps to 0x10000 in ucs-32
|
||||
const hb_codepoint_t delta = (0xd800 << 10) + 0xdc00 - 0x10000;
|
||||
return (((hb_codepoint_t)v) << 10) + v2 - delta;
|
||||
}
|
||||
(*iter) -= 2;
|
||||
return ~0u;
|
||||
} else {
|
||||
(*iter)--;
|
||||
return ~0u;
|
||||
}
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
static hb_script_t getScriptRun(const uint16_t *chars, size_t len, ssize_t *iter) {
|
||||
if (size_t(*iter) == len) {
|
||||
return HB_SCRIPT_UNKNOWN;
|
||||
}
|
||||
uint32_t cp = decodeUtf16(chars, len, iter);
|
||||
hb_script_t current_script = codePointToScript(cp);
|
||||
for (;;) {
|
||||
if (size_t(*iter) == len)
|
||||
break;
|
||||
const ssize_t prev_iter = *iter;
|
||||
cp = decodeUtf16(chars, len, iter);
|
||||
const hb_script_t script = codePointToScript(cp);
|
||||
if (script != current_script) {
|
||||
if (current_script == HB_SCRIPT_INHERITED ||
|
||||
current_script == HB_SCRIPT_COMMON) {
|
||||
current_script = script;
|
||||
} else if (script == HB_SCRIPT_INHERITED ||
|
||||
script == HB_SCRIPT_COMMON) {
|
||||
continue;
|
||||
} else {
|
||||
*iter = prev_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current_script == HB_SCRIPT_INHERITED) {
|
||||
current_script = HB_SCRIPT_COMMON;
|
||||
}
|
||||
|
||||
return current_script;
|
||||
}
|
||||
|
||||
// TODO: API should probably take context
|
||||
void Layout::doLayout(const uint16_t* buf, size_t nchars) {
|
||||
AutoMutex _l(gLock);
|
||||
if (buffer == 0) {
|
||||
buffer = hb_buffer_create();
|
||||
}
|
||||
FT_Error error;
|
||||
|
||||
vector<FontCollection::Run> items;
|
||||
@@ -208,6 +297,9 @@ void Layout::doLayout(const uint16_t* buf, size_t nchars) {
|
||||
mGlyphs.clear();
|
||||
mFaces.clear();
|
||||
mHbFonts.clear();
|
||||
mBounds.setEmpty();
|
||||
mAdvances.clear();
|
||||
mAdvances.resize(nchars, 0);
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
for (size_t run_ix = 0; run_ix < items.size(); run_ix++) {
|
||||
@@ -215,6 +307,10 @@ void Layout::doLayout(const uint16_t* buf, size_t nchars) {
|
||||
int font_ix = findFace(run.font, &paint);
|
||||
paint.font = mFaces[font_ix];
|
||||
hb_font_t *hbFont = mHbFonts[font_ix];
|
||||
if (paint.font == NULL) {
|
||||
// TODO: should log what went wrong
|
||||
continue;
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
std::cout << "Run " << run_ix << ", font " << font_ix <<
|
||||
" [" << run.start << ":" << run.end << "]" << std::endl;
|
||||
@@ -222,24 +318,38 @@ void Layout::doLayout(const uint16_t* buf, size_t nchars) {
|
||||
hb_font_set_ppem(hbFont, size, size);
|
||||
hb_font_set_scale(hbFont, HBFloatToFixed(size), HBFloatToFixed(size));
|
||||
|
||||
hb_buffer_reset(buffer);
|
||||
hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
|
||||
hb_buffer_add_utf16(buffer, buf, nchars, run.start, run.end - run.start);
|
||||
hb_shape(hbFont, buffer, NULL, 0);
|
||||
unsigned int numGlyphs;
|
||||
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
|
||||
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, NULL);
|
||||
for (unsigned int i = 0; i < numGlyphs; i++) {
|
||||
#ifdef VERBOSE
|
||||
std::cout << positions[i].x_advance << " " << positions[i].y_advance << " " << positions[i].x_offset << " " << positions[i].y_offset << std::endl; std::cout << "DoLayout " << info[i].codepoint <<
|
||||
": " << HBFixedToFloat(positions[i].x_advance) << "; " << positions[i].x_offset << ", " << positions[i].y_offset << std::endl;
|
||||
#endif
|
||||
hb_codepoint_t glyph_ix = info[i].codepoint;
|
||||
float xoff = HBFixedToFloat(positions[i].x_offset);
|
||||
float yoff = HBFixedToFloat(positions[i].y_offset);
|
||||
LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff};
|
||||
mGlyphs.push_back(glyph);
|
||||
x += HBFixedToFloat(positions[i].x_advance);
|
||||
int srunend;
|
||||
for (int srunstart = run.start; srunstart < run.end; srunstart = srunend) {
|
||||
srunend = srunstart;
|
||||
hb_script_t script = getScriptRun(buf, run.end, &srunend);
|
||||
|
||||
hb_buffer_reset(buffer);
|
||||
hb_buffer_set_script(buffer, script);
|
||||
hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
|
||||
hb_buffer_add_utf16(buffer, buf, nchars, srunstart, srunend - srunstart);
|
||||
hb_shape(hbFont, buffer, NULL, 0);
|
||||
unsigned int numGlyphs;
|
||||
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
|
||||
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, NULL);
|
||||
for (unsigned int i = 0; i < numGlyphs; i++) {
|
||||
#ifdef VERBOSE
|
||||
std::cout << positions[i].x_advance << " " << positions[i].y_advance << " " << positions[i].x_offset << " " << positions[i].y_offset << std::endl; std::cout << "DoLayout " << info[i].codepoint <<
|
||||
": " << HBFixedToFloat(positions[i].x_advance) << "; " << positions[i].x_offset << ", " << positions[i].y_offset << std::endl;
|
||||
#endif
|
||||
hb_codepoint_t glyph_ix = info[i].codepoint;
|
||||
float xoff = HBFixedToFloat(positions[i].x_offset);
|
||||
float yoff = HBFixedToFloat(positions[i].y_offset);
|
||||
LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff};
|
||||
mGlyphs.push_back(glyph);
|
||||
float xAdvance = HBFixedToFloat(positions[i].x_advance);
|
||||
MinikinRect glyphBounds;
|
||||
paint.font->GetBounds(&glyphBounds, glyph_ix, paint);
|
||||
glyphBounds.offset(x + xoff, y + yoff);
|
||||
mBounds.join(glyphBounds);
|
||||
size_t cluster = info[i].cluster;
|
||||
mAdvances[cluster] += xAdvance;
|
||||
x += xAdvance;
|
||||
}
|
||||
}
|
||||
}
|
||||
mAdvance = x;
|
||||
@@ -275,8 +385,40 @@ void Layout::setProperties(string css) {
|
||||
mProps.parse(css);
|
||||
}
|
||||
|
||||
size_t Layout::nGlyphs() const {
|
||||
return mGlyphs.size();
|
||||
}
|
||||
|
||||
MinikinFont *Layout::getFont(int i) const {
|
||||
const LayoutGlyph& glyph = mGlyphs[i];
|
||||
return mFaces[glyph.font_ix];
|
||||
}
|
||||
|
||||
unsigned int Layout::getGlyphId(int i) const {
|
||||
const LayoutGlyph& glyph = mGlyphs[i];
|
||||
return glyph.glyph_id;
|
||||
}
|
||||
|
||||
float Layout::getX(int i) const {
|
||||
const LayoutGlyph& glyph = mGlyphs[i];
|
||||
return glyph.x;
|
||||
}
|
||||
|
||||
float Layout::getY(int i) const {
|
||||
const LayoutGlyph& glyph = mGlyphs[i];
|
||||
return glyph.y;
|
||||
}
|
||||
|
||||
float Layout::getAdvance() const {
|
||||
return mAdvance;
|
||||
}
|
||||
|
||||
void Layout::getAdvances(float* advances) {
|
||||
memcpy(advances, &mAdvances[0], mAdvances.size() * sizeof(float));
|
||||
}
|
||||
|
||||
void Layout::getBounds(MinikinRect* bounds) {
|
||||
bounds->set(mBounds);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -36,10 +36,40 @@ LOCAL_SHARED_LIBRARIES += \
|
||||
libicuuc \
|
||||
libft2 \
|
||||
libpng \
|
||||
libz
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libminikin
|
||||
libz \
|
||||
libminikin
|
||||
|
||||
LOCAL_MODULE:= minikin_example
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
include external/stlport/libstlport.mk
|
||||
|
||||
LOCAL_MODULE_TAG := tests
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
external/harfbuzz_ng/src \
|
||||
external/freetype/include \
|
||||
external/icu4c/common \
|
||||
frameworks/minikin/include \
|
||||
external/skia/src/core
|
||||
|
||||
LOCAL_SRC_FILES:= example_skia.cpp \
|
||||
MinikinSkia.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += \
|
||||
libutils \
|
||||
liblog \
|
||||
libcutils \
|
||||
libstlport \
|
||||
libharfbuzz_ng \
|
||||
libicuuc \
|
||||
libskia \
|
||||
libminikin \
|
||||
libft2
|
||||
|
||||
LOCAL_MODULE:= minikin_skia_example
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
64
engine/src/flutter/sample/MinikinSkia.cpp
Normal file
64
engine/src/flutter/sample/MinikinSkia.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <SkTypeface.h>
|
||||
#include <SkPaint.h>
|
||||
|
||||
#include <minikin/MinikinFont.h>
|
||||
#include "MinikinSkia.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) :
|
||||
mTypeface(typeface) {
|
||||
}
|
||||
|
||||
MinikinFontSkia::~MinikinFontSkia() {
|
||||
SkSafeUnref(mTypeface);
|
||||
}
|
||||
|
||||
bool MinikinFontSkia::GetGlyph(uint32_t codepoint, uint32_t *glyph) const {
|
||||
SkPaint paint;
|
||||
paint.setTypeface(mTypeface);
|
||||
paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
|
||||
uint16_t glyph16;
|
||||
paint.textToGlyphs(&codepoint, sizeof(codepoint), &glyph16);
|
||||
*glyph = glyph16;
|
||||
//printf("glyph for U+%04x = %d\n", codepoint, glyph16);
|
||||
return !!glyph;
|
||||
}
|
||||
|
||||
float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id,
|
||||
const MinikinPaint &paint) const {
|
||||
SkPaint skpaint;
|
||||
skpaint.setTypeface(mTypeface);
|
||||
skpaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
// TODO: set paint from Minikin
|
||||
skpaint.setTextSize(100);
|
||||
uint16_t glyph16 = glyph_id;
|
||||
SkScalar skWidth;
|
||||
SkRect skBounds;
|
||||
skpaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, &skBounds);
|
||||
// bounds?
|
||||
//printf("advance for glyph %d = %f\n", glyph_id, SkScalarToFP(skWidth));
|
||||
return skWidth;
|
||||
}
|
||||
|
||||
bool MinikinFontSkia::GetTable(uint32_t tag, uint8_t *buf, size_t *size) {
|
||||
if (buf == NULL) {
|
||||
const size_t tableSize = mTypeface->getTableSize(tag);
|
||||
*size = tableSize;
|
||||
return tableSize != 0;
|
||||
} else {
|
||||
const size_t actualSize = mTypeface->getTableData(tag, 0, *size, buf);
|
||||
*size = actualSize;
|
||||
return actualSize != 0;
|
||||
}
|
||||
}
|
||||
|
||||
SkTypeface *MinikinFontSkia::GetSkTypeface() {
|
||||
return mTypeface;
|
||||
}
|
||||
|
||||
int32_t MinikinFontSkia::GetUniqueId() const {
|
||||
return mTypeface->uniqueID();
|
||||
}
|
||||
|
||||
}
|
||||
26
engine/src/flutter/sample/MinikinSkia.h
Normal file
26
engine/src/flutter/sample/MinikinSkia.h
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace android {
|
||||
|
||||
class MinikinFontSkia : public MinikinFont {
|
||||
public:
|
||||
explicit MinikinFontSkia(SkTypeface *typeface);
|
||||
|
||||
~MinikinFontSkia();
|
||||
|
||||
bool GetGlyph(uint32_t codepoint, uint32_t *glyph) const;
|
||||
|
||||
float GetHorizontalAdvance(uint32_t glyph_id,
|
||||
const MinikinPaint &paint) const;
|
||||
|
||||
// If buf is NULL, just update size
|
||||
bool GetTable(uint32_t tag, uint8_t *buf, size_t *size);
|
||||
|
||||
int32_t GetUniqueId() const;
|
||||
|
||||
SkTypeface *GetSkTypeface();
|
||||
|
||||
private:
|
||||
SkTypeface *mTypeface;
|
||||
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
152
engine/src/flutter/sample/example_skia.cpp
Normal file
152
engine/src/flutter/sample/example_skia.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This is a test program that uses Minikin to layout and draw some text.
|
||||
// At the moment, it just draws a string into /data/local/tmp/foo.pgm.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#include <unicode/unistr.h>
|
||||
#include <unicode/utf16.h>
|
||||
|
||||
#include <minikin/MinikinFontFreeType.h>
|
||||
#include <minikin/Layout.h>
|
||||
|
||||
#include <SkCanvas.h>
|
||||
#include <SkGraphics.h>
|
||||
#include <SkImageEncoder.h>
|
||||
#include <SkTypeface.h>
|
||||
#include <SkPaint.h>
|
||||
|
||||
#include "MinikinSkia.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace android {
|
||||
|
||||
FT_Library library; // TODO: this should not be a global
|
||||
|
||||
FontCollection *makeFontCollection() {
|
||||
vector<FontFamily *>typefaces;
|
||||
const char *fns[] = {
|
||||
"/system/fonts/Roboto-Regular.ttf",
|
||||
"/system/fonts/Roboto-Italic.ttf",
|
||||
"/system/fonts/Roboto-BoldItalic.ttf",
|
||||
"/system/fonts/Roboto-Light.ttf",
|
||||
"/system/fonts/Roboto-Thin.ttf",
|
||||
"/system/fonts/Roboto-Bold.ttf",
|
||||
"/system/fonts/Roboto-ThinItalic.ttf",
|
||||
"/system/fonts/Roboto-LightItalic.ttf"
|
||||
};
|
||||
|
||||
FontFamily *family = new FontFamily();
|
||||
FT_Face face;
|
||||
FT_Error error;
|
||||
for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) {
|
||||
const char *fn = fns[i];
|
||||
SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
|
||||
MinikinFont *font = new MinikinFontSkia(skFace);
|
||||
family->addFont(font);
|
||||
}
|
||||
typefaces.push_back(family);
|
||||
|
||||
#if 1
|
||||
family = new FontFamily();
|
||||
const char *fn = "/system/fonts/DroidSansDevanagari-Regular.ttf";
|
||||
SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
|
||||
MinikinFont *font = new MinikinFontSkia(skFace);
|
||||
family->addFont(font);
|
||||
typefaces.push_back(family);
|
||||
#endif
|
||||
|
||||
return new FontCollection(typefaces);
|
||||
}
|
||||
|
||||
// Maybe move to MinikinSkia (esp. instead of opening GetSkTypeface publicly)?
|
||||
|
||||
void drawToSkia(SkCanvas *canvas, SkPaint *paint, Layout *layout, float x, float y) {
|
||||
size_t nGlyphs = layout->nGlyphs();
|
||||
uint16_t *glyphs = new uint16_t[nGlyphs];
|
||||
SkPoint *pos = new SkPoint[nGlyphs];
|
||||
SkTypeface *lastFace = NULL;
|
||||
SkTypeface *skFace = NULL;
|
||||
size_t start = 0;
|
||||
|
||||
paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
for (size_t i = 0; i < nGlyphs; i++) {
|
||||
MinikinFontSkia *mfs = static_cast<MinikinFontSkia *>(layout->getFont(i));
|
||||
skFace = mfs->GetSkTypeface();
|
||||
glyphs[i] = layout->getGlyphId(i);
|
||||
pos[i].fX = SkFloatToScalar(x + layout->getX(i));
|
||||
pos[i].fY = SkFloatToScalar(y + layout->getY(i));
|
||||
if (i > 0 && skFace != lastFace) {
|
||||
paint->setTypeface(lastFace);
|
||||
canvas->drawPosText(glyphs + start, (i - start) << 1, pos + start, *paint);
|
||||
start = i;
|
||||
}
|
||||
lastFace = skFace;
|
||||
}
|
||||
paint->setTypeface(skFace);
|
||||
canvas->drawPosText(glyphs + start, (nGlyphs - start) << 1, pos + start, *paint);
|
||||
delete[] glyphs;
|
||||
delete[] pos;
|
||||
}
|
||||
|
||||
int runMinikinTest() {
|
||||
FT_Error error = FT_Init_FreeType(&library);
|
||||
if (error) {
|
||||
return -1;
|
||||
}
|
||||
Layout::init();
|
||||
|
||||
FontCollection *collection = makeFontCollection();
|
||||
Layout layout;
|
||||
layout.setFontCollection(collection);
|
||||
layout.setProperties("font-size: 32; font-weight: 700;");
|
||||
const char *text = "fine world \xe0\xa4\xa8\xe0\xa4\xae\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\xa4\xe0\xa5\x87";
|
||||
icu::UnicodeString icuText = icu::UnicodeString::fromUTF8(text);
|
||||
layout.doLayout(icuText.getBuffer(), icuText.length());
|
||||
layout.dump();
|
||||
|
||||
SkAutoGraphics ag;
|
||||
|
||||
SkScalar width = 800;
|
||||
SkScalar height = 600;
|
||||
SkBitmap bitmap;
|
||||
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
|
||||
bitmap.allocPixels();
|
||||
SkCanvas canvas(bitmap);
|
||||
SkPaint paint;
|
||||
paint.setARGB(255, 0, 0, 128);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(2);
|
||||
paint.setTextSize(100);
|
||||
paint.setAntiAlias(true);
|
||||
canvas.drawLine(10, 300, 10 + layout.getAdvance(), 300, paint);
|
||||
paint.setStyle(SkPaint::kFill_Style);
|
||||
drawToSkia(&canvas, &paint, &layout, 10, 300);
|
||||
|
||||
SkImageEncoder::EncodeFile("/data/local/tmp/foo.png", bitmap, SkImageEncoder::kPNG_Type, 100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
return android::runMinikinTest();
|
||||
}
|
||||
Reference in New Issue
Block a user