1ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien/*
2ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * Copyright (C) 2013 The Android Open Source Project
3ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien *
4ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * Licensed under the Apache License, Version 2.0 (the "License");
5ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * you may not use this file except in compliance with the License.
6ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * You may obtain a copy of the License at
7ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien *
8ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien *      http://www.apache.org/licenses/LICENSE-2.0
9ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien *
10ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * Unless required by applicable law or agreed to in writing, software
11ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * distributed under the License is distributed on an "AS IS" BASIS,
12ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * See the License for the specific language governing permissions and
14ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien * limitations under the License.
15ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien */
16ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
17ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien// This is a test program that uses Minikin to layout and draw some text.
18ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien// At the moment, it just draws a string into /data/local/tmp/foo.pgm.
19ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
20ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <stdio.h>
21ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <vector>
22ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <fstream>
23ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
24ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <unicode/unistr.h>
25ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <unicode/utf16.h>
26ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
27ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <minikin/MinikinFontFreeType.h>
28ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <minikin/Layout.h>
29ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
30ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <SkCanvas.h>
31ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <SkGraphics.h>
32ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <SkImageEncoder.h>
33ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <SkTypeface.h>
34ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <SkPaint.h>
35ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
36ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include "MinikinSkia.h"
37ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
38ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienusing std::vector;
39ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
40ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Leviennamespace android {
41ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
42ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph LevienFT_Library library;  // TODO: this should not be a global
43ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
44ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph LevienFontCollection *makeFontCollection() {
45ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    vector<FontFamily *>typefaces;
46ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const char *fns[] = {
47ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-Regular.ttf",
48ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-Italic.ttf",
49ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-BoldItalic.ttf",
50ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-Light.ttf",
51ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-Thin.ttf",
52ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-Bold.ttf",
53ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-ThinItalic.ttf",
54ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        "/system/fonts/Roboto-LightItalic.ttf"
55ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    };
56ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
57ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    FontFamily *family = new FontFamily();
58ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) {
59ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        const char *fn = fns[i];
60ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
61ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        MinikinFont *font = new MinikinFontSkia(skFace);
62ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        family->addFont(font);
63ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
64ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    typefaces.push_back(family);
65ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
66ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#if 1
67ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    family = new FontFamily();
68ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const char *fn = "/system/fonts/DroidSansDevanagari-Regular.ttf";
69ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
70ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    MinikinFont *font = new MinikinFontSkia(skFace);
71ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    family->addFont(font);
72ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    typefaces.push_back(family);
73ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#endif
74ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
75ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return new FontCollection(typefaces);
76ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
77ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
78ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien// Maybe move to MinikinSkia (esp. instead of opening GetSkTypeface publicly)?
79ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
80ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienvoid drawToSkia(SkCanvas *canvas, SkPaint *paint, Layout *layout, float x, float y) {
81ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    size_t nGlyphs = layout->nGlyphs();
82ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    uint16_t *glyphs = new uint16_t[nGlyphs];
83ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkPoint *pos = new SkPoint[nGlyphs];
84ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkTypeface *lastFace = NULL;
85ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkTypeface *skFace = NULL;
86ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    size_t start = 0;
87ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
88ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
89ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    for (size_t i = 0; i < nGlyphs; i++) {
90ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        MinikinFontSkia *mfs = static_cast<MinikinFontSkia *>(layout->getFont(i));
91ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        skFace = mfs->GetSkTypeface();
92ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        glyphs[i] = layout->getGlyphId(i);
93a3998d4f51e862f00b3dc6b8b99cfbeea2622b2dLeon Scroggins III        pos[i].fX = x + layout->getX(i);
94a3998d4f51e862f00b3dc6b8b99cfbeea2622b2dLeon Scroggins III        pos[i].fY = y + layout->getY(i);
95ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (i > 0 && skFace != lastFace) {
96ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            paint->setTypeface(lastFace);
97ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            canvas->drawPosText(glyphs + start, (i - start) << 1, pos + start, *paint);
98ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            start = i;
99ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        }
100ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        lastFace = skFace;
101ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
102ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint->setTypeface(skFace);
103ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    canvas->drawPosText(glyphs + start, (nGlyphs - start) << 1, pos + start, *paint);
104ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    delete[] glyphs;
105ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    delete[] pos;
106ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
107ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
108ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienint runMinikinTest() {
109ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    FT_Error error = FT_Init_FreeType(&library);
110ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    if (error) {
111ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        return -1;
112ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
113ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    Layout::init();
114ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
115ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    FontCollection *collection = makeFontCollection();
116ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    Layout layout;
117ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    layout.setFontCollection(collection);
118ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const char *text = "fine world \xe0\xa4\xa8\xe0\xa4\xae\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\xa4\xe0\xa5\x87";
1199802b0dc8e648117ab917e716e81e8a0da909cb3Behdad Esfahbod    int bidiFlags = 0;
1209802b0dc8e648117ab917e716e81e8a0da909cb3Behdad Esfahbod    FontStyle fontStyle(7);
1219802b0dc8e648117ab917e716e81e8a0da909cb3Behdad Esfahbod    MinikinPaint minikinPaint;
1229802b0dc8e648117ab917e716e81e8a0da909cb3Behdad Esfahbod    minikinPaint.size = 32;
123ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    icu::UnicodeString icuText = icu::UnicodeString::fromUTF8(text);
1249802b0dc8e648117ab917e716e81e8a0da909cb3Behdad Esfahbod    layout.doLayout(icuText.getBuffer(), 0, icuText.length(), icuText.length(), bidiFlags, fontStyle, minikinPaint);
125ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    layout.dump();
126ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
127ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkAutoGraphics ag;
128ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
129f8134eff0007533f213925d3e8e37d213b3afdeeMike Reed    int width = 800;
130f8134eff0007533f213925d3e8e37d213b3afdeeMike Reed    int height = 600;
131ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkBitmap bitmap;
132f8134eff0007533f213925d3e8e37d213b3afdeeMike Reed    bitmap.allocN32Pixels(width, height);
133ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkCanvas canvas(bitmap);
134ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkPaint paint;
135ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint.setARGB(255, 0, 0, 128);
136ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint.setStyle(SkPaint::kStroke_Style);
137ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint.setStrokeWidth(2);
138ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint.setTextSize(100);
139ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint.setAntiAlias(true);
140ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    canvas.drawLine(10, 300, 10 + layout.getAdvance(), 300, paint);
141ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    paint.setStyle(SkPaint::kFill_Style);
142ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    drawToSkia(&canvas, &paint, &layout, 10, 300);
143ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
144ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    SkImageEncoder::EncodeFile("/data/local/tmp/foo.png", bitmap, SkImageEncoder::kPNG_Type, 100);
145ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return 0;
146ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
147ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
148ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
149ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
150ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienint main(int argc, const char** argv) {
151ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return android::runMinikinTest();
152ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
153