Commit 5d3df507 authored by ckitagawa's avatar ckitagawa Committed by Commit Bot

[Paint Preview] Add Glyph Usage tracking

Add data structures for tracking glyph usage. There are two variants
sparse and dense and which one is used should depend on the font being
subset, this can be done heuristically before starting tracking to
minimize memory overhead.

Part of landing:
https://chromium-review.googlesource.com/c/chromium/src/+/1786583

Bug: 1006242
Change-Id: I251fae2270386ea0279268db4ffef8e98942e278
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1816440Reviewed-by: default avatarIan Vollick <vollick@chromium.org>
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#699716}
parent 0a74f421
......@@ -8,6 +8,8 @@ static_library("common") {
sources = [
"file_stream.cc",
"file_stream.h",
"glyph_usage.cc",
"glyph_usage.h",
]
deps = [
......@@ -21,6 +23,7 @@ source_set("unit_tests") {
sources = [
"file_stream_unittest.cc",
"glyph_usage_unittest.cc",
]
deps = [
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/paint_preview/common/glyph_usage.h"
#include "base/callback.h"
namespace paint_preview {
GlyphUsage::GlyphUsage() : first_(0), last_(0) {}
GlyphUsage::GlyphUsage(uint16_t first, uint16_t last)
: first_(first), last_(last) {
DCHECK_EQ(first, 1U);
DCHECK_LE(first, last);
}
GlyphUsage::~GlyphUsage() = default;
DenseGlyphUsage::DenseGlyphUsage() : GlyphUsage(), bitset_(0, 0) {}
DenseGlyphUsage::DenseGlyphUsage(uint16_t num_glyphs)
: GlyphUsage(1, num_glyphs), bitset_(num_glyphs + 1, false) {}
DenseGlyphUsage::~DenseGlyphUsage() = default;
void DenseGlyphUsage::Set(uint16_t glyph_id) {
if (!ShouldSubset() ||
(glyph_id != 0 && (glyph_id < First() || glyph_id > Last())))
return;
bitset_[glyph_id] = true;
}
bool DenseGlyphUsage::IsSet(uint16_t glyph_id) const {
if (!ShouldSubset() ||
(glyph_id != 0 && (glyph_id < First() || glyph_id > Last())))
return false;
return bitset_[glyph_id];
}
void DenseGlyphUsage::ForEach(
const base::RepeatingCallback<void(uint16_t)>& callback) const {
for (uint16_t i = 0; i < bitset_.size(); ++i) {
if (bitset_[i])
callback.Run(i);
}
}
SparseGlyphUsage::SparseGlyphUsage() : GlyphUsage() {}
SparseGlyphUsage::SparseGlyphUsage(uint16_t num_glyphs)
: GlyphUsage(1, num_glyphs) {}
SparseGlyphUsage::~SparseGlyphUsage() = default;
void SparseGlyphUsage::Set(uint16_t glyph_id) {
if (!ShouldSubset() ||
(glyph_id != 0 && (glyph_id < First() || glyph_id > Last())))
return;
glyph_ids_.insert(glyph_id);
}
bool SparseGlyphUsage::IsSet(uint16_t glyph_id) const {
return glyph_ids_.count(glyph_id);
}
void SparseGlyphUsage::ForEach(
const base::RepeatingCallback<void(uint16_t)>& callback) const {
for (const auto& key : glyph_ids_)
callback.Run(key);
}
} // namespace paint_preview
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PAINT_PREVIEW_COMMON_GLYPH_USAGE_H_
#define COMPONENTS_PAINT_PREVIEW_COMMON_GLYPH_USAGE_H_
#include <stdint.h>
#include <vector>
#include "base/callback_forward.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
namespace paint_preview {
// Base class for tracking glyph usage.
class GlyphUsage {
public:
GlyphUsage();
// Range of glyph ids.
// Note: at present |first| must be 1 as we don't support offset subset codes.
GlyphUsage(uint16_t first, uint16_t last);
virtual ~GlyphUsage();
virtual void Set(uint16_t glyph_id) = 0;
virtual bool IsSet(uint16_t glyph_id) const = 0;
// Executes |callback| with the glyph id of each glyph that is set.
virtual void ForEach(
const base::RepeatingCallback<void(uint16_t)>& callback) const = 0;
bool ShouldSubset() const { return first_ < last_; }
uint16_t First() const { return first_; }
uint16_t Last() const { return last_; }
GlyphUsage& operator=(GlyphUsage&& other) noexcept;
GlyphUsage(GlyphUsage&& other) noexcept;
private:
uint16_t first_;
uint16_t last_;
DISALLOW_COPY_AND_ASSIGN(GlyphUsage);
};
// An implementation of GlyphUsage that works well for densely set glyphs.
// Usecases:
// - Primary language
// - Pre-subsetted fonts
class DenseGlyphUsage : public GlyphUsage {
public:
DenseGlyphUsage();
DenseGlyphUsage(uint16_t num_glyphs);
~DenseGlyphUsage() override;
void Set(uint16_t glyph_id) override;
bool IsSet(uint16_t glyph_id) const override;
void ForEach(
const base::RepeatingCallback<void(uint16_t)>& callback) const override;
DenseGlyphUsage& operator=(DenseGlyphUsage&& other) noexcept;
DenseGlyphUsage(DenseGlyphUsage&& other) noexcept;
private:
std::vector<bool> bitset_;
DISALLOW_COPY_AND_ASSIGN(DenseGlyphUsage);
};
// An implementation of GlyphUsage that works well for sparsely set glyphs.
// Usecases:
// - Non-subsetted CJK fonts
// - Emoji
// - Large glyph counts fonts with low glyph usage
// - Non-primary language
class SparseGlyphUsage : public GlyphUsage {
public:
SparseGlyphUsage();
SparseGlyphUsage(uint16_t num_glyphs);
~SparseGlyphUsage() override;
void Set(uint16_t glyph_id) override;
bool IsSet(uint16_t glyph_id) const override;
void ForEach(
const base::RepeatingCallback<void(uint16_t)>& callback) const override;
SparseGlyphUsage& operator=(SparseGlyphUsage&& other) noexcept;
SparseGlyphUsage(SparseGlyphUsage&& other) noexcept;
private:
base::flat_set<uint16_t> glyph_ids_;
DISALLOW_COPY_AND_ASSIGN(SparseGlyphUsage);
};
} // namespace paint_preview
#endif // COMPONENTS_PAINT_PREVIEW_COMMON_GLYPH_USAGE_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/paint_preview/common/glyph_usage.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace paint_preview {
TEST(PaintPreviewGlyphUsageTest, TestEmpty) {
DenseGlyphUsage dense;
EXPECT_EQ(dense.First(), 0U);
EXPECT_EQ(dense.Last(), 0U);
EXPECT_FALSE(dense.ShouldSubset());
dense.Set(0);
EXPECT_FALSE(dense.IsSet(0));
dense.Set(5);
EXPECT_FALSE(dense.IsSet(5));
dense.Set(60);
EXPECT_FALSE(dense.IsSet(60));
size_t counter = 0;
auto cb = base::BindRepeating(
[](size_t* counter, uint16_t glyph_id) { ++(*counter); },
base::Unretained(&counter));
dense.ForEach(cb);
EXPECT_EQ(counter, 0U);
SparseGlyphUsage sparse;
EXPECT_EQ(sparse.First(), 0U);
EXPECT_EQ(sparse.Last(), 0U);
EXPECT_FALSE(sparse.ShouldSubset());
sparse.Set(0);
EXPECT_FALSE(sparse.IsSet(0));
sparse.Set(5);
EXPECT_FALSE(sparse.IsSet(5));
sparse.Set(60);
EXPECT_FALSE(sparse.IsSet(60));
counter = 0;
sparse.ForEach(cb);
EXPECT_EQ(counter, 0U);
}
TEST(PaintPreviewGlyphUsageTest, TestDense) {
const uint16_t kFirst = 1;
const uint16_t kNumGlyphs = 70;
DenseGlyphUsage dense(kNumGlyphs);
EXPECT_EQ(dense.First(), kFirst);
EXPECT_EQ(dense.Last(), kNumGlyphs);
EXPECT_TRUE(dense.ShouldSubset());
// 0 should be valid even though it is before "first".
std::vector<uint16_t> test_glyph_ids = {0, 1, 24, 70, 68, 1, 9, 8};
for (const auto& gid : test_glyph_ids) {
dense.Set(gid);
EXPECT_TRUE(dense.IsSet(gid));
}
dense.Set(kNumGlyphs + 1);
EXPECT_FALSE(dense.IsSet(kNumGlyphs + 1));
dense.Set(kNumGlyphs * 2);
EXPECT_FALSE(dense.IsSet(kNumGlyphs * 2));
std::vector<uint16_t> set_gids;
auto cb = base::BindRepeating(
[](std::vector<uint16_t>* set_gids, uint16_t glyph_id) {
set_gids->push_back(glyph_id);
},
base::Unretained(&set_gids));
dense.ForEach(cb);
EXPECT_THAT(set_gids,
testing::UnorderedElementsAre(0U, 1U, 24U, 70U, 68U, 9U, 8U));
}
TEST(PaintPreviewGlyphUsageTest, TestSparse) {
const uint16_t kFirst = 1;
const uint16_t kNumGlyphs = 70;
SparseGlyphUsage sparse(kNumGlyphs);
EXPECT_EQ(sparse.First(), kFirst);
EXPECT_EQ(sparse.Last(), kNumGlyphs);
EXPECT_TRUE(sparse.ShouldSubset());
// 0 should be valid even though it is before "first".
std::vector<uint16_t> test_glyph_ids = {0, 1, 24, 70, 68, 1, 9, 8};
for (const auto& gid : test_glyph_ids) {
sparse.Set(gid);
EXPECT_TRUE(sparse.IsSet(gid));
}
sparse.Set(kNumGlyphs + 1);
EXPECT_FALSE(sparse.IsSet(kNumGlyphs + 1));
sparse.Set(kNumGlyphs * 2);
EXPECT_FALSE(sparse.IsSet(kNumGlyphs * 2));
std::vector<uint16_t> set_gids;
auto cb = base::BindRepeating(
[](std::vector<uint16_t>* set_gids, uint16_t glyph_id) {
set_gids->push_back(glyph_id);
},
base::Unretained(&set_gids));
sparse.ForEach(cb);
EXPECT_THAT(set_gids,
testing::UnorderedElementsAre(0U, 1U, 24U, 70U, 68U, 9U, 8U));
}
} // namespace paint_preview
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment