diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc new file mode 100644 index 00000000000..25c450376ca --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc @@ -0,0 +1,356 @@ +// Copyright 2017 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 "third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h" + +#include <memory> +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/fonts/character_range.h" +#include "third_party/blink/renderer/platform/fonts/font.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h" +#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h" +#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h" +#include "third_party/blink/renderer/platform/fonts/simple_font_data.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_typeface.h" +#include "third_party/blink/renderer/platform/wtf/optional.h" + +namespace blink { + +namespace { + +// Creating minimal test SimpleFontData objects, +// the font won't have any glyphs, but that's okay. +static scoped_refptr<SimpleFontData> CreateTestSimpleFontData( + bool force_rotation = false) { + FontPlatformData platform_data( + PaintTypeface::FromSkTypeface(SkTypeface::MakeDefault()), CString(), 10, + false, false, + force_rotation ? FontOrientation::kVerticalUpright + : FontOrientation::kHorizontal); + return SimpleFontData::Create(platform_data, nullptr); +} + +class ShapeResultBloberizerTest : public testing::Test { + protected: + void SetUp() override { + font_description.SetComputedSize(12.0); + font_description.SetLocale(LayoutLocale::Get("en")); + ASSERT_EQ(USCRIPT_LATIN, font_description.GetScript()); + font_description.SetGenericFamily(FontDescription::kStandardFamily); + + font = Font(font_description); + font.Update(nullptr); + ASSERT_TRUE(font.CanShapeWordByWord()); + fallback_fonts = nullptr; + cache = std::make_unique<ShapeCache>(); + } + + FontCachePurgePreventer font_cache_purge_preventer; + FontDescription font_description; + Font font; + std::unique_ptr<ShapeCache> cache; + HashSet<const SimpleFontData*>* fallback_fonts; + unsigned start_index = 0; + unsigned num_glyphs = 0; + hb_script_t script = HB_SCRIPT_INVALID; +}; + +} // anonymous namespace + +TEST_F(ShapeResultBloberizerTest, StartsEmpty) { + Font font; + ShapeResultBloberizer bloberizer(font, 1); + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer), + nullptr); + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer).size(), + 0ul); + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer).size(), + 0ul); + EXPECT_FALSE( + ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer)); + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer), + 0ul); + EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul); + + EXPECT_TRUE(bloberizer.Blobs().IsEmpty()); +} + +TEST_F(ShapeResultBloberizerTest, StoresGlyphsOffsets) { + Font font; + ShapeResultBloberizer bloberizer(font, 1); + + scoped_refptr<SimpleFontData> font1 = CreateTestSimpleFontData(); + scoped_refptr<SimpleFontData> font2 = CreateTestSimpleFontData(); + + // 2 pending glyphs + bloberizer.Add(42, font1.get(), CanvasRotationInVertical::kRegular, 10); + bloberizer.Add(43, font1.get(), CanvasRotationInVertical::kRegular, 15); + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer), + font1.get()); + EXPECT_FALSE( + ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer)); + { + const auto& glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer); + EXPECT_EQ(glyphs.size(), 2ul); + EXPECT_EQ(42, glyphs[0]); + EXPECT_EQ(43, glyphs[1]); + + const auto& offsets = + ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer); + EXPECT_EQ(offsets.size(), 2ul); + EXPECT_EQ(10, offsets[0]); + EXPECT_EQ(15, offsets[1]); + } + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer), + 0ul); + EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul); + + // one more glyph, different font => pending run flush + bloberizer.Add(44, font2.get(), CanvasRotationInVertical::kRegular, 12); + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer), + font2.get()); + EXPECT_FALSE( + ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer)); + { + const auto& glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer); + EXPECT_EQ(glyphs.size(), 1ul); + EXPECT_EQ(44, glyphs[0]); + + const auto& offsets = + ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer); + EXPECT_EQ(offsets.size(), 1ul); + EXPECT_EQ(12, offsets[0]); + } + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer), + 1ul); + EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul); + + // flush everything (1 blob w/ 2 runs) + EXPECT_EQ(bloberizer.Blobs().size(), 1ul); +} + +TEST_F(ShapeResultBloberizerTest, StoresGlyphsVerticalOffsets) { + Font font; + ShapeResultBloberizer bloberizer(font, 1); + + scoped_refptr<SimpleFontData> font1 = CreateTestSimpleFontData(); + scoped_refptr<SimpleFontData> font2 = CreateTestSimpleFontData(); + + // 2 pending glyphs + bloberizer.Add(42, font1.get(), CanvasRotationInVertical::kRegular, + FloatPoint(10, 0)); + bloberizer.Add(43, font1.get(), CanvasRotationInVertical::kRegular, + FloatPoint(15, 0)); + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer), + font1.get()); + EXPECT_TRUE( + ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer)); + { + const auto& glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer); + EXPECT_EQ(glyphs.size(), 2ul); + EXPECT_EQ(42, glyphs[0]); + EXPECT_EQ(43, glyphs[1]); + + const auto& offsets = + ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer); + EXPECT_EQ(offsets.size(), 4ul); + EXPECT_EQ(10, offsets[0]); + EXPECT_EQ(0, offsets[1]); + EXPECT_EQ(15, offsets[2]); + EXPECT_EQ(0, offsets[3]); + } + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer), + 0ul); + EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul); + + // one more glyph, different font => pending run flush + bloberizer.Add(44, font2.get(), CanvasRotationInVertical::kRegular, + FloatPoint(12, 2)); + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer), + font2.get()); + EXPECT_TRUE( + ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer)); + { + const auto& glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer); + EXPECT_EQ(glyphs.size(), 1ul); + EXPECT_EQ(44, glyphs[0]); + + const auto& offsets = + ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer); + EXPECT_EQ(offsets.size(), 2ul); + EXPECT_EQ(12, offsets[0]); + EXPECT_EQ(2, offsets[1]); + } + + EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer), + 1ul); + EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul); + + // flush everything (1 blob w/ 2 runs) + EXPECT_EQ(bloberizer.Blobs().size(), 1ul); +} + +TEST_F(ShapeResultBloberizerTest, MixedBlobRotation) { + Font font; + ShapeResultBloberizer bloberizer(font, 1); + + scoped_refptr<SimpleFontData> test_font = CreateTestSimpleFontData(); + + struct { + CanvasRotationInVertical canvas_rotation; + size_t expected_pending_glyphs; + size_t expected_pending_runs; + size_t expected_committed_blobs; + } append_ops[] = { + // append 2 horizontal glyphs -> these go into the pending glyph buffer + {CanvasRotationInVertical::kRegular, 1u, 0u, 0u}, + {CanvasRotationInVertical::kRegular, 2u, 0u, 0u}, + + // append 3 vertical rotated glyphs -> push the prev pending (horizontal) + // glyphs into a new run in the current (horizontal) blob + {CanvasRotationInVertical::kRotateCanvasUpright, 1u, 1u, 0u}, + {CanvasRotationInVertical::kRotateCanvasUpright, 2u, 1u, 0u}, + {CanvasRotationInVertical::kRotateCanvasUpright, 3u, 1u, 0u}, + + // append 2 more horizontal glyphs -> flush the current (horizontal) blob, + // push prev (vertical) pending glyphs into new vertical blob run + {CanvasRotationInVertical::kRegular, 1u, 1u, 1u}, + {CanvasRotationInVertical::kRegular, 2u, 1u, 1u}, + + // append 1 more vertical glyph -> flush current (vertical) blob, push + // prev (horizontal) pending glyphs into a new horizontal blob run + {CanvasRotationInVertical::kRotateCanvasUpright, 1u, 1u, 2u}, + }; + + for (const auto& op : append_ops) { + bloberizer.Add(42, test_font.get(), op.canvas_rotation, FloatPoint()); + EXPECT_EQ( + op.expected_pending_glyphs, + ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer).size()); + EXPECT_EQ(op.canvas_rotation, + ShapeResultBloberizerTestInfo::PendingBlobRotation(bloberizer)); + EXPECT_EQ(op.expected_pending_runs, + ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer)); + EXPECT_EQ(op.expected_committed_blobs, + ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer)); + } + + // flush everything -> 4 blobs total + EXPECT_EQ(4u, bloberizer.Blobs().size()); +} + +// Tests that filling a glyph buffer for a specific range returns the same +// results when shaping word by word as when shaping the full run in one go. +TEST_F(ShapeResultBloberizerTest, CommonAccentLeftToRightFillGlyphBuffer) { + // "/. ." with an accent mark over the first dot. + const UChar kStr[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E, 0x0}; + TextRun text_run(kStr, 5); + TextRunPaintInfo run_info(text_run); + run_info.to = 3; + + ShapeResultBloberizer bloberizer(font, 1); + CachingWordShaper word_shaper(font); + ShapeResultBuffer buffer; + word_shaper.FillResultBuffer(run_info, &buffer); + bloberizer.FillGlyphs(run_info, buffer); + + Font reference_font(font_description); + reference_font.Update(nullptr); + reference_font.SetCanShapeWordByWordForTesting(false); + + ShapeResultBloberizer reference_bloberizer(reference_font, 1); + CachingWordShaper reference_word_shaper(font); + ShapeResultBuffer reference_buffer; + reference_word_shaper.FillResultBuffer(run_info, &reference_buffer); + reference_bloberizer.FillGlyphs(run_info, reference_buffer); + + const auto& glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer); + ASSERT_EQ(glyphs.size(), 3ul); + const auto reference_glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(reference_bloberizer); + ASSERT_EQ(reference_glyphs.size(), 3ul); + + EXPECT_EQ(reference_glyphs[0], glyphs[0]); + EXPECT_EQ(reference_glyphs[1], glyphs[1]); + EXPECT_EQ(reference_glyphs[2], glyphs[2]); +} + +// Tests that filling a glyph buffer for a specific range returns the same +// results when shaping word by word as when shaping the full run in one go. +TEST_F(ShapeResultBloberizerTest, CommonAccentRightToLeftFillGlyphBuffer) { + // "[] []" with an accent mark over the last square bracket. + const UChar kStr[] = {0x5B, 0x5D, 0x20, 0x5B, 0x301, 0x5D, 0x0}; + TextRun text_run(kStr, 6); + text_run.SetDirection(TextDirection::kRtl); + TextRunPaintInfo run_info(text_run); + run_info.from = 1; + + ShapeResultBloberizer bloberizer(font, 1); + CachingWordShaper word_shaper(font); + ShapeResultBuffer buffer; + word_shaper.FillResultBuffer(run_info, &buffer); + bloberizer.FillGlyphs(run_info, buffer); + + Font reference_font(font_description); + reference_font.Update(nullptr); + reference_font.SetCanShapeWordByWordForTesting(false); + + ShapeResultBloberizer reference_bloberizer(reference_font, 1); + CachingWordShaper reference_word_shaper(font); + ShapeResultBuffer reference_buffer; + reference_word_shaper.FillResultBuffer(run_info, &reference_buffer); + reference_bloberizer.FillGlyphs(run_info, reference_buffer); + + const auto& glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer); + ASSERT_EQ(5u, glyphs.size()); + const auto reference_glyphs = + ShapeResultBloberizerTestInfo::PendingRunGlyphs(reference_bloberizer); + ASSERT_EQ(5u, reference_glyphs.size()); + + EXPECT_EQ(reference_glyphs[0], glyphs[0]); + EXPECT_EQ(reference_glyphs[1], glyphs[1]); + EXPECT_EQ(reference_glyphs[2], glyphs[2]); + EXPECT_EQ(reference_glyphs[3], glyphs[3]); + EXPECT_EQ(reference_glyphs[4], glyphs[4]); +} + +// Tests that runs with zero glyphs (the ZWJ non-printable character in this +// case) are handled correctly. This test passes if it does not cause a crash. +TEST_F(ShapeResultBloberizerTest, SubRunWithZeroGlyphs) { + // "Foo ‌ bar" + const UChar kStr[] = {0x46, 0x6F, 0x6F, 0x20, 0x200C, + 0x20, 0x62, 0x61, 0x71, 0x0}; + TextRun text_run(kStr, 9); + + CachingWordShaper shaper(font); + FloatRect glyph_bounds; + ASSERT_GT(shaper.Width(text_run, nullptr, &glyph_bounds), 0); + + ShapeResultBloberizer bloberizer(font, 1); + TextRunPaintInfo run_info(text_run); + run_info.to = 8; + + CachingWordShaper word_shaper(font); + ShapeResultBuffer buffer; + word_shaper.FillResultBuffer(run_info, &buffer); + bloberizer.FillGlyphs(run_info, buffer); + + shaper.GetCharacterRange(text_run, 0, 8); +} + +} // namespace blink |