blob: c6f638f3e53c6204f9311e6c16173a37ccf6518b [file] [log] [blame] [edit]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/vr/elements/text.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "cc/paint/skia_paint_canvas.h"
#include "chrome/browser/vr/elements/ui_texture.h"
#include "third_party/icu/source/common/unicode/uscript.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/render_text.h"
#include "ui/gfx/shadow_value.h"
#include "ui/gfx/text_elider.h"
namespace vr {
namespace {
constexpr int kTextPixelPerDmm = 1100;
constexpr char kDefaultFontFamily[] = "sans-serif";
int DmmToPixel(float dmm) {
return static_cast<int>(dmm * kTextPixelPerDmm);
}
float PixelToDmm(int pixel) {
return static_cast<float>(pixel) / kTextPixelPerDmm;
}
void UpdateRenderText(gfx::RenderText* render_text,
const std::u16string& text,
const gfx::FontList& font_list,
SkColor color) {
// Disable the cursor to avoid reserving width for a trailing caret.
render_text->SetCursorEnabled(false);
// Subpixel rendering is counterproductive when drawing VR textures.
render_text->set_subpixel_rendering_suppressed(true);
render_text->SetText(text);
render_text->SetFontList(font_list);
render_text->SetColor(color);
render_text->set_shadows({});
render_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
const int font_style = font_list.GetFontStyle();
render_text->SetStyle(gfx::TEXT_STYLE_ITALIC,
(font_style & gfx::Font::ITALIC) != 0);
render_text->SetStyle(gfx::TEXT_STYLE_UNDERLINE,
(font_style & gfx::Font::UNDERLINE) != 0);
render_text->SetWeight(font_list.GetFontWeight());
}
} // namespace
class TextTexture : public UiTexture {
public:
explicit TextTexture(Text* element) : element_(element) {}
TextTexture(const TextTexture&) = delete;
TextTexture& operator=(const TextTexture&) = delete;
~TextTexture() override = default;
void SetFontHeightInDmm(float font_height_dmms) {
SetAndDirty(&font_height_dmms_, font_height_dmms);
}
void SetText(const std::u16string& text) { SetAndDirty(&text_, text); }
void SetColor(SkColor color) { SetAndDirty(&color_, color); }
void SetTextWidth(float width) { SetAndDirty(&text_width_, width); }
// This method does all text preparation for the element other than drawing to
// the texture. This allows for deeper unit testing of the Text element
// without having to mock canvases and simulate frame rendering. The state of
// the texture is modified here.
gfx::Size LayOutText();
const std::vector<std::unique_ptr<gfx::RenderText>>& lines() const {
return lines_;
}
private:
void Draw(SkCanvas* sk_canvas, const gfx::Size& texture_size) override;
void PrepareDrawText(const std::u16string& text,
const gfx::FontList& font_list,
gfx::Rect* bounds,
SkColor color);
gfx::SizeF size_;
std::u16string text_;
float font_height_dmms_ = 0;
float text_width_ = 0;
SkColor color_ = SK_ColorBLACK;
std::vector<std::unique_ptr<gfx::RenderText>> lines_;
raw_ptr<Text> element_ = nullptr;
};
Text::Text(float font_height_dmms)
: TexturedElement(), texture_(std::make_unique<TextTexture>(this)) {
texture_->SetFontHeightInDmm(font_height_dmms);
}
Text::~Text() = default;
void Text::SetText(const std::u16string& text) {
texture_->SetText(text);
}
void Text::SetFieldWidth(float width) {
field_width_ = width;
texture_->SetTextWidth(width);
}
void Text::SetColor(SkColor color) {
texture_->SetColor(color);
}
const std::vector<std::unique_ptr<gfx::RenderText>>& Text::LinesForTest() {
return texture_->lines();
}
UiTexture* Text::GetTexture() const {
return texture_.get();
}
bool Text::TextureDependsOnMeasurement() const {
return true;
}
gfx::Size Text::MeasureTextureSize() {
text_texture_size_ = texture_->LayOutText();
// Adjust the actual size of the element to match the texture.
TexturedElement::SetSize(field_width_,
PixelToDmm(text_texture_size_.height()));
return text_texture_size_;
}
gfx::Size TextTexture::LayOutText() {
int pixel_font_height = DmmToPixel(font_height_dmms_);
gfx::Rect text_bounds;
DCHECK(text_width_ > 0.f) << element_->DebugName();
text_bounds.set_width(DmmToPixel(text_width_));
gfx::FontList fonts =
gfx::FontList(gfx::Font(kDefaultFontFamily, pixel_font_height));
PrepareDrawText(text_, fonts, &text_bounds, color_);
set_measured();
return text_bounds.size();
}
void TextTexture::Draw(SkCanvas* sk_canvas, const gfx::Size& texture_size) {
cc::SkiaPaintCanvas paint_canvas(sk_canvas);
gfx::Canvas gfx_canvas(&paint_canvas, 1.0f);
gfx::Canvas* canvas = &gfx_canvas;
for (auto& render_text : lines_)
render_text->Draw(canvas);
}
void TextTexture::PrepareDrawText(const std::u16string& text,
const gfx::FontList& font_list,
gfx::Rect* bounds,
SkColor color) {
DCHECK(bounds);
lines_.clear();
gfx::Rect rect(*bounds);
std::vector<std::u16string> strings;
gfx::ElideRectangleText(text, font_list, bounds->width(),
bounds->height() ? bounds->height() : INT_MAX,
gfx::WRAP_LONG_WORDS, &strings);
int height = 0;
int line_height = 0;
for (size_t i = 0; i < strings.size(); i++) {
std::unique_ptr<gfx::RenderText> render_text =
gfx::RenderText::CreateRenderText();
UpdateRenderText(render_text.get(), strings[i], font_list, color);
if (i == 0) {
// Measure line and center text vertically.
line_height = render_text->GetStringSize().height();
rect.set_height(line_height);
if (bounds->height()) {
const int text_height = strings.size() * line_height;
rect += gfx::Vector2d(0, (bounds->height() - text_height) / 2);
}
}
render_text->SetDisplayRect(rect);
height += line_height;
rect += gfx::Vector2d(0, line_height);
lines_.push_back(std::move(render_text));
}
// Set calculated height.
if (bounds->height() == 0)
bounds->set_height(height);
}
} // namespace vr