Skip to content

Commit

Permalink
Add Paragraph class from prototype
Browse files Browse the repository at this point in the history
Change-Id: Id38e4261c4d6f8fa99f405d9b21bdd9e259a9384
  • Loading branch information
abarth committed May 11, 2017
1 parent 3ff068f commit 4185e93
Show file tree
Hide file tree
Showing 10 changed files with 382 additions and 2 deletions.
2 changes: 2 additions & 0 deletions include/minikin/LineBreaker.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
#include "unicode/locid.h"
#include <cmath>
#include <vector>
#include "minikin/FontCollection.h"
#include "minikin/Hyphenator.h"
#include "minikin/MinikinFont.h"
#include "minikin/WordBreaker.h"

namespace minikin {
Expand Down
4 changes: 4 additions & 0 deletions src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ static_library("src") {
"font_skia.h",
"font_style.h",
"font_weight.h",
"paint_record.cc",
"paint_record.h",
"paragraph.cc",
"paragraph.h",
"paragraph_builder.cc",
"paragraph_builder.h",
"paragraph_style.h",
Expand Down
2 changes: 1 addition & 1 deletion src/font_skia.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ hb_face_t* FontSkia::CreateHarfBuzzFace() const {
return hb_face_create_for_tables(GetTable, typeface_.get(), 0);
}

const sk_sp<SkTypeface>& FontSkia::GetSkTypeface() {
const sk_sp<SkTypeface>& FontSkia::GetSkTypeface() const {
return typeface_;
}

Expand Down
2 changes: 1 addition & 1 deletion src/font_skia.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class FontSkia : public minikin::MinikinFont {

const std::vector<minikin::FontVariation>& GetAxes() const override;

const sk_sp<SkTypeface>& GetSkTypeface();
const sk_sp<SkTypeface>& GetSkTypeface() const;

private:
sk_sp<SkTypeface> typeface_;
Expand Down
41 changes: 41 additions & 0 deletions src/paint_record.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2017 Google Inc.
*
* 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.
*/

#include "lib/txt/src/paint_record.h"

namespace txt {

PaintRecord::PaintRecord() = default;

PaintRecord::~PaintRecord() = default;

PaintRecord::PaintRecord(SkColor color, SkPoint offset, sk_sp<SkTextBlob> text)
: color_(color), offset_(offset), text_(std::move(text)) {}

PaintRecord::PaintRecord(PaintRecord&& other) {
color_ = other.color_;
offset_ = other.offset_;
text_ = std::move(other.text_);
}

PaintRecord& PaintRecord::operator=(PaintRecord&& other) {
color_ = other.color_;
offset_ = other.offset_;
text_ = std::move(other.text_);
return *this;
}

} // namespace txt
53 changes: 53 additions & 0 deletions src/paint_record.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2017 Google Inc.
*
* 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.
*/

#ifndef LIB_TXT_SRC_PAINT_RECORD_H_
#define LIB_TXT_SRC_PAINT_RECORD_H_

#include "lib/ftl/macros.h"
#include "third_party/skia/include/core/SkTextBlob.h"

namespace txt {

class PaintRecord {
public:
PaintRecord();

~PaintRecord();

PaintRecord(SkColor color, SkPoint offset, sk_sp<SkTextBlob> text);

PaintRecord(const PaintRecord& other) = delete;

PaintRecord(PaintRecord&& other);

PaintRecord& operator=(PaintRecord&& other);

SkColor color() const { return color_; }

SkPoint offset() const { return offset_; }

SkTextBlob* text() const { return text_.get(); }

private:
SkColor color_;
SkPoint offset_;
sk_sp<SkTextBlob> text_;
};

} // namespace txt

#endif // LIB_TXT_SRC_PAINT_RECORD_H_
210 changes: 210 additions & 0 deletions src/paragraph.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
* Copyright 2017 Google Inc.
*
* 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.
*/

#include "lib/txt/src/paragraph.h"

#include <algorithm>
#include <limits>
#include <utility>
#include <vector>

#include <minikin/Layout.h>
#include <minikin/LineBreaker.h>

#include "lib/ftl/logging.h"

#include "lib/txt/src/font_provider.h"
#include "lib/txt/src/font_skia.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/core/SkTypeface.h"

namespace txt {
namespace {

const sk_sp<SkTypeface>& GetTypefaceForGlyph(const minikin::Layout& layout,
size_t index) {
const FontSkia* font = static_cast<const FontSkia*>(layout.getFont(index));
return font->GetSkTypeface();
}

size_t GetBlobLength(const minikin::Layout& layout, size_t blob_start) {
const size_t glyph_count = layout.nGlyphs();
const sk_sp<SkTypeface>& typeface = GetTypefaceForGlyph(layout, blob_start);
for (size_t blob_end = blob_start + 1; blob_end < glyph_count; ++blob_end) {
if (GetTypefaceForGlyph(layout, blob_end).get() != typeface.get())
return blob_end - blob_start;
}
return glyph_count - blob_start;
}

int GetWeight(const TextStyle& style) {
switch (style.font_weight) {
case FontWeight::w100:
return 1;
case FontWeight::w200:
return 2;
case FontWeight::w300:
return 3;
case FontWeight::w400:
return 4;
case FontWeight::w500:
return 5;
case FontWeight::w600:
return 6;
case FontWeight::w700:
return 7;
case FontWeight::w800:
return 8;
case FontWeight::w900:
return 9;
}
}

bool GetItalic(const TextStyle& style) {
switch (style.font_style) {
case FontStyle::normal:
return false;
case FontStyle::italic:
return true;
}
}

void GetFontAndMinikinPaint(const TextStyle& style,
minikin::FontStyle* font,
minikin::MinikinPaint* paint) {
*font = minikin::FontStyle(GetWeight(style), GetItalic(style));
paint->size = style.font_size;
paint->letterSpacing = style.letter_spacing;
// TODO(abarth): font_family, word_spacing.
}

void GetPaint(const TextStyle& style, SkPaint* paint) {
paint->setTextSize(style.font_size);
}

} // namespace

Paragraph::Paragraph() = default;

Paragraph::~Paragraph() = default;

void Paragraph::SetText(std::vector<uint16_t> text, StyledRuns runs) {
text_ = std::move(text);
runs_ = std::move(runs);

breaker_.setLocale(icu::Locale(), nullptr);
breaker_.resize(text_.size());
memcpy(breaker_.buffer(), text_.data(), text_.size() * sizeof(text_[0]));
breaker_.setText();
}

void Paragraph::AddRunsToLineBreaker() {
auto collection = FontProvider::GetDefault().GetFontCollectionForFamily("");
minikin::FontStyle font;
minikin::MinikinPaint paint;
for (size_t i = 0; i < runs_.size(); ++i) {
auto run = runs_.GetRun(i);
GetFontAndMinikinPaint(run.style, &font, &paint);
breaker_.addStyleRun(&paint, collection, font, run.start, run.end, false);
}
}

void Paragraph::Layout(double width) {
breaker_.setLineWidths(0.0f, 0, width);
AddRunsToLineBreaker();
size_t breaks_count = breaker_.computeBreaks();
const int* breaks = breaker_.getBreaks();

SkPaint paint;
paint.setAntiAlias(true);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);

minikin::FontStyle font;
minikin::MinikinPaint minikin_paint;

SkTextBlobBuilder builder;
auto collection = FontProvider::GetDefault().GetFontCollectionForFamily("");
minikin::Layout layout;
SkScalar x = 0.0f;
SkScalar y = 0.0f;
size_t break_index = 0;
for (size_t run_index = 0; run_index < runs_.size(); ++run_index) {
auto run = runs_.GetRun(run_index);
GetFontAndMinikinPaint(run.style, &font, &minikin_paint);
GetPaint(run.style, &paint);

size_t layout_start = run.start;
while (layout_start < run.end) {
const size_t next_break = (break_index > breaks_count - 1)
? std::numeric_limits<size_t>::max()
: breaks[break_index];
const size_t layout_end = std::min(run.end, next_break);

int bidiFlags = 0;
layout.doLayout(text_.data(), layout_start, layout_end - layout_start,
text_.size(), bidiFlags, font, minikin_paint, collection);

const size_t glyph_count = layout.nGlyphs();
size_t blob_start = 0;
while (blob_start < glyph_count) {
const size_t blob_length = GetBlobLength(layout, blob_start);
// TODO(abarth): Precompute when we can use allocRunPosH.
paint.setTypeface(GetTypefaceForGlyph(layout, blob_start));

auto buffer = builder.allocRunPos(paint, blob_length);

for (size_t blob_index = 0; blob_index < blob_length; ++blob_index) {
const size_t glyph_index = blob_start + blob_index;
buffer.glyphs[blob_index] = layout.getGlyphId(glyph_index);
const size_t pos_index = 2 * blob_index;
buffer.pos[pos_index] = layout.getX(glyph_index);
buffer.pos[pos_index + 1] = layout.getY(glyph_index);
}
blob_start += blob_length;
}

// TODO(abarth): We could keep the same SkTextBlobBuilder as long as the
// color stayed the same.
records_.push_back(
PaintRecord(run.style.color, SkPoint::Make(x, y), builder.make()));

if (layout_end == next_break) {
x = 0.0f;
// TODO(abarth): Use the line height, which is something like the max
// font_size for runs in this line times the paragraph's line height.
y += run.style.font_size;
break_index += 1;
} else {
x += layout.getAdvance();
}

layout_start = layout_end;
}
}
}

void Paragraph::Paint(SkCanvas* canvas, double x, double y) {
SkPaint paint;
for (const auto& record : records_) {
paint.setColor(record.color());
const SkPoint& offset = record.offset();
canvas->drawTextBlob(record.text(), x + offset.x(), y + offset.y(), paint);
}
}

} // namespace txt
Loading

0 comments on commit 4185e93

Please sign in to comment.