summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaph Levien <raph@google.com>2013-07-15 14:19:59 -0700
committerRaph Levien <raph@google.com>2014-05-12 09:08:15 -0700
commitecc2d34ac23a497988f21e5f415b53c007b9d8c5 (patch)
tree876b3c943b7841c9600db636738cd0e59b5f2a7c
parent5adafc0d84d238948b5d257ec5311030ca04271c (diff)
downloadandroid_frameworks_minikin-ecc2d34ac23a497988f21e5f415b53c007b9d8c5.tar.gz
android_frameworks_minikin-ecc2d34ac23a497988f21e5f415b53c007b9d8c5.tar.bz2
android_frameworks_minikin-ecc2d34ac23a497988f21e5f415b53c007b9d8c5.zip
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
-rw-r--r--include/minikin/Layout.h26
-rw-r--r--include/minikin/MinikinFont.h28
-rw-r--r--libs/minikin/Android.mk4
-rw-r--r--libs/minikin/FontCollection.cpp19
-rw-r--r--libs/minikin/FontFamily.cpp1
-rw-r--r--libs/minikin/Layout.cpp180
-rw-r--r--sample/Android.mk36
-rw-r--r--sample/MinikinSkia.cpp64
-rw-r--r--sample/MinikinSkia.h26
-rw-r--r--sample/example_skia.cpp152
10 files changed, 499 insertions, 37 deletions
diff --git a/include/minikin/Layout.h b/include/minikin/Layout.h
index fd0ed75..896478b 100644
--- a/include/minikin/Layout.h
+++ b/include/minikin/Layout.h
@@ -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
diff --git a/include/minikin/MinikinFont.h b/include/minikin/MinikinFont.h
index c08e4fe..e84f5df 100644
--- a/include/minikin/MinikinFont.h
+++ b/include/minikin/MinikinFont.h
@@ -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;
diff --git a/libs/minikin/Android.mk b/libs/minikin/Android.mk
index baac98c..5e13495 100644
--- a/libs/minikin/Android.mk
+++ b/libs/minikin/Android.mk
@@ -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)
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 702bd20..aa37825 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -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];
diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp
index 558fd77..16031ce 100644
--- a/libs/minikin/FontFamily.cpp
+++ b/libs/minikin/FontFamily.cpp
@@ -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
diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp
index d4e09c5..ffaa451 100644
--- a/libs/minikin/Layout.cpp
+++ b/libs/minikin/Layout.cpp
@@ -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
diff --git a/sample/Android.mk b/sample/Android.mk
index 939aca4..a19019a 100644
--- a/sample/Android.mk
+++ b/sample/Android.mk
@@ -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)
diff --git a/sample/MinikinSkia.cpp b/sample/MinikinSkia.cpp
new file mode 100644
index 0000000..d67e59f
--- /dev/null
+++ b/sample/MinikinSkia.cpp
@@ -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();
+}
+
+}
diff --git a/sample/MinikinSkia.h b/sample/MinikinSkia.h
new file mode 100644
index 0000000..8286a4c
--- /dev/null
+++ b/sample/MinikinSkia.h
@@ -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 \ No newline at end of file
diff --git a/sample/example_skia.cpp b/sample/example_skia.cpp
new file mode 100644
index 0000000..ff13b5c
--- /dev/null
+++ b/sample/example_skia.cpp
@@ -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();
+}