浏览代码

paragraphBuilder && paragraph

/siyaoH-1.17-PlatformMessage
siyao 4 年前
当前提交
4c404d1e
共有 16 个文件被更改,包括 2192 次插入13 次删除
  1. 4
      com.unity.uiwidgets/Runtime/ui2/native_bindings.cs
  2. 134
      com.unity.uiwidgets/Runtime/ui2/painting.cs
  3. 10
      engine/Build.bee.cs
  4. 28
      engine/README.md
  5. 8
      engine/src/engine.cc
  6. 5
      engine/src/render_api.h
  7. 15
      engine/src/render_api_d3d11.cc
  8. 5
      engine/src/runtime/mono_api.cc
  9. 54
      Samples/UIWidgetsSamples_2019_4/Assets/testPlugin.cs
  10. 1001
      com.unity.uiwidgets/Runtime/ui2/text.cs
  11. 241
      engine/src/lib/ui/txt/paragraph.cc
  12. 53
      engine/src/lib/ui/txt/paragraph.h
  13. 563
      engine/src/lib/ui/txt/paragraph_builder.cc
  14. 64
      engine/src/lib/ui/txt/paragraph_builder.h
  15. 20
      engine/src/lib/ui/txt/utils.h

4
com.unity.uiwidgets/Runtime/ui2/native_bindings.cs


public abstract class NativeWrapper {
protected internal IntPtr _ptr { get; protected set; }
public IntPtr ptr {
get { return _ptr; }
}
protected NativeWrapper() {
}

134
com.unity.uiwidgets/Runtime/ui2/painting.cs


public static readonly Color black = new Color(0xFF000000);
public static readonly Color white = new Color(0xFFFFFFFF);
public static Color fromARGB(int a, int r, int g, int b) {
return new Color(
(uint) (((a & 0xff) << 24) |

// If you add more fields, remember to update _kDataByteCount.
const int _kDataByteCount = 56;
object[] _objects;
public object[] _objects;
internal IntPtr[] _objectPtrs;
const int _kShaderIndex = 0;
const int _kColorFilterIndex = 1;

static extern IntPtr PictureRecorder_endRecording(IntPtr ptr);
}
// TODO: for text.
public Shadow(
Color color,
Offset offset,
float blurRadius = 0) {
D.assert(color != null);
D.assert(offset != null);
D.assert(blurRadius >= 0.0);
this.color = color ?? new Color(_kColorDefault);
this.offset = offset ?? Offset.zero;
this.blurRadius = blurRadius;
}
public static readonly uint _kColorDefault = 0xFF000000;
// Constants for shadow encoding.
static readonly int _kBytesPerShadow = 16;
static readonly int _kColorOffset = 0 << 2;
static readonly int _kXOffset = 1 << 2;
static readonly int _kYOffset = 2 << 2;
static readonly int _kBlurOffset = 3 << 2;
readonly Color color;
readonly Offset offset;
readonly float blurRadius;
static float convertRadiusToSigma(float radius) {
return radius * 0.57735f + 0.5f;
}
public float blurSigma {
get { return convertRadiusToSigma(blurRadius); }
}
public Paint toPaint() {
return new Paint() {
color = color,
maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma)
};
}
public Shadow scale(float factor) {
return new Shadow(
color: color,
offset: offset * factor,
blurRadius: blurRadius * factor
);
}
public static Shadow lerp(Shadow a, Shadow b, float t) {
D.assert(t != null);
if (a == null && b == null)
return null;
if (a == null)
return b.scale(t);
if (b == null)
return a.scale(1.0f - t);
return new Shadow(
color: Color.lerp(a.color, b.color, t),
offset: Offset.lerp(a.offset, b.offset, t),
blurRadius: Mathf.Lerp(a.blurRadius, b.blurRadius, t)
);
}
public static List<Shadow> lerpList(List<Shadow> a, List<Shadow> b, float t) {
D.assert(t != null);
if (a == null && b == null)
return null;
a ??= new List<Shadow>();
b ??= new List<Shadow>();
List<Shadow> result = new List<Shadow>();
int commonLength = Math.Min(a.Count, b.Count);
for (int i = 0; i < commonLength; i += 1)
result.Add(Shadow.lerp(a[i], b[i], t));
for (int i = commonLength; i < a.Count; i += 1)
result.Add(a[i].scale(1.0f - t));
for (int i = commonLength; i < b.Count; i += 1)
result.Add(b[i].scale(t));
return result;
}
public override bool Equals(object obj) {
if (ReferenceEquals(this, obj))
return true;
return obj is Shadow other
&& other.color == color
&& other.offset == offset
&& other.blurRadius == blurRadius;
}
public override int GetHashCode() {
int hashcode = color.GetHashCode();
hashcode = (hashcode ^ 397) ^ offset.GetHashCode();
hashcode = (hashcode ^ 397) ^ blurRadius.GetHashCode();
return hashcode;
}
public static byte[] _encodeShadows(List<Shadow> shadows) {
if (shadows == null)
return new byte[0];
int byteCount = shadows.Count * _kBytesPerShadow;
byte[] shadowsData = new byte[byteCount];
int shadowOffset = 0;
for (int shadowIndex = 0; shadowIndex < shadows.Count; ++shadowIndex) {
Shadow shadow = shadows[shadowIndex];
if (shadow == null)
continue;
shadowOffset = shadowIndex * _kBytesPerShadow;
shadowsData.setInt32(_kColorOffset + shadowOffset,
(int) (shadow.color.value ^ Shadow._kColorDefault));
shadowsData.setFloat32(_kXOffset + shadowOffset,
shadow.offset.dx);
shadowsData.setFloat32(_kYOffset + shadowOffset,
shadow.offset.dy);
shadowsData.setFloat32(_kBlurOffset + shadowOffset,
shadow.blurRadius);
}
return shadowsData;
}
public override string ToString() => $"TextShadow({color}, {offset}, {blurRadius})";
}
}

10
engine/Build.bee.cs


"src/lib/ui/compositing/scene.h",
"src/lib/ui/compositing/scene_builder.cc",
"src/lib/ui/compositing/scene_builder.h",
"src/lib/ui/txt/utils.h",
"src/lib/ui/txt/paragraph_builder.cc",
"src/lib/ui/txt/paragraph_builder.h",
"src/lib/ui/txt/paragraph.cc",
"src/lib/ui/txt/paragraph.h",
"src/lib/ui/painting/canvas.cc",
"src/lib/ui/painting/canvas.h",

OutputName = {c => $"libUIWidgets{(c.CodeGen == CodeGen.Debug ? "_d" : "")}"},
};
np.CompilerSettings().Add(c => c.WithCppLanguageVersion(CppLanguageVersion.Cpp17));
np.IncludeDirectories.Add("src");
np.IncludeDirectories.Add("src");
np.Defines.Add("UIWIDGETS_ENGINE_VERSION=\\\"0.0\\\"", "SKIA_VERSION=\\\"0.0\\\"");

28
engine/README.md


+ ]
+}
```
cmd
```
set GYP_MSVS_OVERRIDE_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community
cd engine/src

powershell 
```
$env:GYP_MSVS_OVERRIDE_PATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community"
$env:FLUTTER_ROOT="E:\c\src" # target to flutter
$env:SKIA_ROOT="C:\Users\siyao\skia_repo\skia\" # target to skia
```
## Create symbolic
### Create symbolic
cmd
cd <uiwidigets_dir>\engine
cd third_party \\ create the directory if not exists
mklink /D skia <SKIA_ROOT>
cd <uiwidigets_dir>\engine
cd third_party   \\ create the directory if not exists
mklink /D skia <SKIA_ROOT>
```
powershell (run as administrator)
```
cd <uiwidigets_dir>\engine
cd third_party   # create the directory if not exists
New-Item -Path skia -ItemType SymbolicLink -Value C:\Users\siyao\skia_repo\skia\ 
```
Flutter engine txt include skia header in this pattern 'third_party/skia/*', so without symbolic, the txt lib will include skia
header file in flutter engine, instead of headers in skia repo.

```
## Set ICU Data Enviroment Varaible
cmd
```
powershell
```
$env:UIWIDGETS_ICUDATA="$env:SKIA_ROOT/out/Debug/icudtl.dat"
```
Unity Editor need to run with those environment variables set.

8
engine/src/engine.cc


s_CurrentAPI->Draw();
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API DrawParagraph(uiwidgets::Paragraph* paragraph) {
if (s_CurrentAPI == NULL) {
return;
}
s_CurrentAPI->Draw2(paragraph);
}
static void UNITY_INTERFACE_API OnRenderEvent(int eventID) {
// Unknown / unsupported graphics device type? Do nothing
if (s_CurrentAPI == NULL) {

5
engine/src/render_api.h


#pragma once
#include "Unity/IUnityGraphics.h"
#include "src/lib/ui/txt/paragraph.h"
class RenderAPI {
public:
virtual ~RenderAPI() {}

virtual void SetImageTexture(void* ptr) = 0;
virtual void Draw() = 0;
virtual void Draw() = 0;
virtual void Draw2(uiwidgets::Paragraph* paragraph) = 0;
virtual void PreDraw() = 0;

15
engine/src/render_api_d3d11.cc


void SetImageTexture(void* ptr) override;
void Draw() override;
void Draw2(uiwidgets::Paragraph* paragraph) override;
void PreDraw() override;
void PostDraw() override;

void draw(SkCanvas* canvas);
void draw2(SkCanvas* canvas, uiwidgets::Paragraph* paragraph);
private:
ID3D11Device* m_Device;

testParagraph->Paint(canvas, 10, 200);
}
void RenderAPI_D3D11::draw2(SkCanvas* canvas, uiwidgets::Paragraph* paragraph) {
paragraph->m_paragraph->Paint(canvas, 10, 150);
}
void draw1(SkCanvas* canvas) {
canvas->clear(SK_ColorWHITE);
SkPaint paint;

canvas->getGrContext()->submit(true);
// if (rdoc_api) rdoc_api->EndFrameCapture(m_DeviceSkia, NULL);
}
void RenderAPI_D3D11::Draw2(uiwidgets::Paragraph* paragraph) {
SkCanvas* canvas = m_SkSurface->getCanvas();
draw(canvas);
draw2(canvas, paragraph);
canvas->flush();
canvas->getGrContext()->submit(true);
}
void RenderAPI_D3D11::PreDraw() {

5
engine/src/runtime/mono_api.cc


} // namespace uiwidgets
extern "C" int64_t Dart_TimelineGetMicros() { return uiwidgets::Mono_TimelineGetMicros(); }
// TODO: This is temp solution
// extern "C" int64_t Dart_TimelineGetMicros() { return uiwidgets::Mono_TimelineGetMicros(); }
extern "C" int64_t Dart_TimelineGetMicros() { return 0; }

54
Samples/UIWidgetsSamples_2019_4/Assets/testPlugin.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using Unity.UIWidgets.ui2;
using Color = Unity.UIWidgets.ui2.Color;
public class testPlugin : MonoBehaviour
{
[DllImport("libUIWidgets_d")]
static extern IntPtr CreateTexture(int w, int h);
[DllImport("libUIWidgets_d")]
static extern void Draw123();
[DllImport("libUIWidgets_d")]
static extern void DrawParagraph(IntPtr paragraph);
void testParagraph()
{
var renderer = GetComponent<Renderer>();
var c = CreateTexture(1024, 1024);
renderer.material.mainTexture =
Texture2D.CreateExternalTexture(1024, 1024, TextureFormat.RGBA32, true, true, c);
Draw123();
var style = new ParagraphStyle(fontFamily: "Arial", height: 4);
var pb = new ParagraphBuilder(style);
var ts = new TextStyle(
color: new Color(0xFFFF00F0),
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.doubleLine,
fontFamily: "Arial",
fontSize:30,
height:1.5
);
pb.pushStyle(ts);
pb.addText("just for test\n 中文测试 分段测试长长长长长长长长长长长长长长长长长长长长长长长长66666666");
var p = pb.build();
p.layout(new ParagraphConstraints(500));
DrawParagraph(p.ptr);
}
// Start is called before the first frame update
void Start()
{
testParagraph();
}
// Update is called once per frame
void Update()
{
}
}

1001
com.unity.uiwidgets/Runtime/ui2/text.cs
文件差异内容过多而无法显示
查看文件

241
engine/src/lib/ui/txt/paragraph.cc


#include "lib/ui/ui_mono_state.h"
#include "paragraph.h"
#include "utils.h"
#include <flutter\third_party\txt\src\txt\paragraph.h>
namespace uiwidgets {
Paragraph::Paragraph(std::unique_ptr<txt::Paragraph> paragraph)
: m_paragraph(std::move(paragraph)) {}
Paragraph::~Paragraph() = default;
size_t Paragraph::GetAllocationSize() {
// We don't have an accurate accounting of the paragraph's memory consumption,
// so return a fixed size to indicate that its impact is more than the size
// of the Paragraph class.
return 2000;
}
double Paragraph::width() {
return m_paragraph->GetMaxWidth();
}
double Paragraph::height() {
return m_paragraph->GetHeight();
}
double Paragraph::longestLine() {
return m_paragraph->GetLongestLine();
}
double Paragraph::minIntrinsicWidth() {
return m_paragraph->GetMinIntrinsicWidth();
}
double Paragraph::maxIntrinsicWidth() {
return m_paragraph->GetMaxIntrinsicWidth();
}
double Paragraph::alphabeticBaseline() {
return m_paragraph->GetAlphabeticBaseline();
}
double Paragraph::ideographicBaseline() {
return m_paragraph->GetIdeographicBaseline();
}
bool Paragraph::didExceedMaxLines() {
return m_paragraph->DidExceedMaxLines();
}
void Paragraph::layout(double width) {
m_paragraph->Layout(width);
}
void Paragraph::paint(Canvas * canvas, double x, double y) {
SkCanvas* sk_canvas = canvas->canvas();
if (!sk_canvas)
return;
m_paragraph->Paint(sk_canvas, x, y);
}
static Float32List EncodeTextBoxes(
const std::vector<txt::Paragraph::TextBox> & boxes) {
// Layout:
// First value is the number of values.
// Then there are boxes.size() groups of 5 which are LTRBD, where D is the
// text direction index.
Float32List result = {
new float[boxes.size() * 5],
boxes.size() * 5
};
unsigned long position = 0;
for (unsigned long i = 0; i < boxes.size(); i++) {
const txt::Paragraph::TextBox& box = boxes[i];
result.data[position++] = box.rect.fLeft;
result.data[position++] = box.rect.fTop;
result.data[position++] = box.rect.fRight;
result.data[position++] = box.rect.fBottom;
result.data[position++] = static_cast<float>(box.direction);
}
return result;
}
Float32List Paragraph::getRectsForRange(unsigned start,
unsigned end,
unsigned boxHeightStyle,
unsigned boxWidthStyle) {
std::vector<txt::Paragraph::TextBox> boxes = m_paragraph->GetRectsForRange(
start, end, static_cast<txt::Paragraph::RectHeightStyle>(boxHeightStyle),
static_cast<txt::Paragraph::RectWidthStyle>(boxWidthStyle));
return EncodeTextBoxes(boxes);
}
Float32List Paragraph::getRectsForPlaceholders() {
std::vector<txt::Paragraph::TextBox> boxes =
m_paragraph->GetRectsForPlaceholders();
return EncodeTextBoxes(boxes);
}
SizeTList Paragraph::getPositionForOffset(double dx, double dy) {
SizeTList result = {
new size_t[2],
2
};
txt::Paragraph::PositionWithAffinity pos =
m_paragraph->GetGlyphPositionAtCoordinate(dx, dy);
result.data[0] = pos.position;
result.data[1] = static_cast<int>(pos.affinity);
return result;
}
SizeTList Paragraph::getWordBoundary(unsigned offset) {
txt::Paragraph::Range<size_t> point = m_paragraph->GetWordBoundary(offset);
SizeTList result = {
new size_t[2],
2
};
result.data[0] = point.start;
result.data[1] = point.end;
return result;
}
SizeTList Paragraph::getLineBoundary(unsigned offset) {
std::vector<txt::LineMetrics> metrics = m_paragraph->GetLineMetrics();
int line_start = -1;
int line_end = -1;
for (txt::LineMetrics& line : metrics) {
if (offset >= line.start_index && offset <= line.end_index) {
line_start = line.start_index;
line_end = line.end_index;
break;
}
}
SizeTList result = {
new size_t[2],
2
};
result.data[0] = line_start;
result.data[1] = line_end;
return result;
}
Float32List Paragraph::computeLineMetrics() {
std::vector<txt::LineMetrics> metrics = m_paragraph->GetLineMetrics();
// Layout:
// boxes.size() groups of 9 which are the line metrics
// properties
Float32List result = {
new float[metrics.size() * 9],
2
};
unsigned long position = 0;
for (unsigned long i = 0; i < metrics.size(); i++) {
const txt::LineMetrics& line = metrics[i];
result.data[position++] = static_cast<double>(line.hard_break);
result.data[position++] = line.ascent;
result.data[position++] = line.descent;
result.data[position++] = line.unscaled_ascent;
// We add then round to get the height. The
// definition of height here is different
// than the one in LibTxt.
result.data[position++] = round(line.ascent + line.descent);
result.data[position++] = line.width;
result.data[position++] = line.left;
result.data[position++] = line.baseline;
result.data[position++] = static_cast<double>(line.line_number);
}
return result;
}
UIWIDGETS_API(double) Paragraph_width(Paragraph* ptr) {
return ptr->width();
}
UIWIDGETS_API(double) Paragraph_height(Paragraph* ptr) {
return ptr->height();
}
UIWIDGETS_API(double) Paragraph_longestLine(Paragraph* ptr) {
return ptr->longestLine();
}
UIWIDGETS_API(double) Paragraph_minIntrinsicWidth(Paragraph* ptr) {
return ptr->minIntrinsicWidth();
}
UIWIDGETS_API(double) Paragraph_maxIntrinsicWidth(Paragraph* ptr) {
return ptr->maxIntrinsicWidth();
}
UIWIDGETS_API(double) Paragraph_alphabeticBaseline(Paragraph* ptr) {
return ptr->alphabeticBaseline();
}
UIWIDGETS_API(double) Paragraph_ideographicBaseline(Paragraph* ptr) {
return ptr->ideographicBaseline();
}
UIWIDGETS_API(bool) Paragraph_didExceedMaxLines(Paragraph* ptr) {
return ptr->didExceedMaxLines();
}
UIWIDGETS_API(void) Paragraph_layout(Paragraph* ptr, double width) {
ptr->layout(width);
}
UIWIDGETS_API(Float32List) Paragraph_getRectsForRange(Paragraph* ptr, int start, int end, int boxHeightStyle, int boxWidthStyle) {
return ptr->getRectsForRange(start, end, boxHeightStyle, boxWidthStyle);
}
UIWIDGETS_API(Float32List) Paragraph_getRectsForPlaceholders(Paragraph* ptr) {
return ptr->getRectsForPlaceholders();
}
UIWIDGETS_API(SizeTList) Paragraph_getPositionForOffset(Paragraph* ptr, double dx, double dy) {
return ptr->getPositionForOffset(dx, dy);
}
UIWIDGETS_API(SizeTList) Paragraph_getWordBoundary(Paragraph* ptr, int offset) {
return ptr->getWordBoundary(offset);
}
UIWIDGETS_API(SizeTList) Paragraph_getLineBoundary(Paragraph* ptr, int offset) {
return ptr->getLineBoundary(offset);
}
UIWIDGETS_API(void) Paragraph_paint(Paragraph* ptr, Canvas* canvas, double x, double y) {
ptr->paint(canvas, x, y);
}
UIWIDGETS_API(Float32List) Paragraph_computeLineMetrics(Paragraph* ptr) {
return ptr->computeLineMetrics();
}
UIWIDGETS_API(void) Paragraph_dispose(Paragraph* ptr) {
ptr->Release();
}
}

53
engine/src/lib/ui/txt/paragraph.h


#pragma once
#include <flutter/fml/memory/ref_counted.h>
#include <src\lib\ui\painting\canvas.h>
#include <flutter\third_party\txt\src\txt\paragraph.h>
#include "utils.h"
namespace uiwidgets {
class Paragraph : public fml::RefCountedThreadSafe<Paragraph> {
FML_FRIEND_MAKE_REF_COUNTED(Paragraph);
public:
static fml::RefPtr<Paragraph> Create();
static fml::RefPtr<Paragraph> Create(std::unique_ptr<txt::Paragraph> txt_paragraph) {
return fml::MakeRefCounted<Paragraph>(std::move(txt_paragraph));
}
~Paragraph();
double width();
double height();
double longestLine();
double minIntrinsicWidth();
double maxIntrinsicWidth();
double alphabeticBaseline();
double ideographicBaseline();
bool didExceedMaxLines();
void layout(double width);
void paint(Canvas* canvas, double x, double y);
Float32List getRectsForRange(unsigned start,
unsigned end,
unsigned boxHeightStyle,
unsigned boxWidthStyle);
Float32List getRectsForPlaceholders();
SizeTList getPositionForOffset(double dx, double dy);
SizeTList getWordBoundary(unsigned offset);
SizeTList getLineBoundary(unsigned offset);
Float32List computeLineMetrics();
size_t GetAllocationSize();
std::unique_ptr<txt::Paragraph> m_paragraph;
private:
explicit Paragraph(std::unique_ptr<txt::Paragraph> paragraph);
};
} // namespace uiwidgets

563
engine/src/lib/ui/txt/paragraph_builder.cc


#include "lib/ui/ui_mono_state.h"
#include "paragraph_builder.h"
#include "third_party/icu/source/common/unicode/ustring.h"
namespace uiwidgets {
namespace {
// TextStyle
const int tsColorIndex = 1;
const int tsTextDecorationIndex = 2;
const int tsTextDecorationColorIndex = 3;
const int tsTextDecorationStyleIndex = 4;
const int tsFontWeightIndex = 5;
const int tsFontStyleIndex = 6;
const int tsTextBaselineIndex = 7;
const int tsTextDecorationThicknessIndex = 8;
const int tsFontFamilyIndex = 9;
const int tsFontSizeIndex = 10;
const int tsLetterSpacingIndex = 11;
const int tsWordSpacingIndex = 12;
const int tsHeightIndex = 13;
const int tsLocaleIndex = 14;
const int tsBackgroundIndex = 15;
const int tsForegroundIndex = 16;
const int tsTextShadowsIndex = 17;
const int tsFontFeaturesIndex = 18;
const int tsColorMask = 1 << tsColorIndex;
const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex;
const int tsTextDecorationStyleMask = 1 << tsTextDecorationStyleIndex;
const int tsTextDecorationThicknessMask = 1 << tsTextDecorationThicknessIndex;
const int tsFontWeightMask = 1 << tsFontWeightIndex;
const int tsFontStyleMask = 1 << tsFontStyleIndex;
const int tsTextBaselineMask = 1 << tsTextBaselineIndex;
const int tsFontFamilyMask = 1 << tsFontFamilyIndex;
const int tsFontSizeMask = 1 << tsFontSizeIndex;
const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
const int tsWordSpacingMask = 1 << tsWordSpacingIndex;
const int tsHeightMask = 1 << tsHeightIndex;
const int tsLocaleMask = 1 << tsLocaleIndex;
const int tsBackgroundMask = 1 << tsBackgroundIndex;
const int tsForegroundMask = 1 << tsForegroundIndex;
const int tsTextShadowsMask = 1 << tsTextShadowsIndex;
const int tsFontFeaturesMask = 1 << tsFontFeaturesIndex;
// ParagraphStyle
const int psTextAlignIndex = 1;
const int psTextDirectionIndex = 2;
const int psFontWeightIndex = 3;
const int psFontStyleIndex = 4;
const int psMaxLinesIndex = 5;
const int psTextHeightBehaviorIndex = 6;
const int psFontFamilyIndex = 7;
const int psFontSizeIndex = 8;
const int psHeightIndex = 9;
const int psStrutStyleIndex = 10;
const int psEllipsisIndex = 11;
const int psLocaleIndex = 12;
const int psTextAlignMask = 1 << psTextAlignIndex;
const int psTextDirectionMask = 1 << psTextDirectionIndex;
const int psFontWeightMask = 1 << psFontWeightIndex;
const int psFontStyleMask = 1 << psFontStyleIndex;
const int psMaxLinesMask = 1 << psMaxLinesIndex;
const int psFontFamilyMask = 1 << psFontFamilyIndex;
const int psFontSizeMask = 1 << psFontSizeIndex;
const int psHeightMask = 1 << psHeightIndex;
const int psTextHeightBehaviorMask = 1 << psTextHeightBehaviorIndex;
const int psStrutStyleMask = 1 << psStrutStyleIndex;
const int psEllipsisMask = 1 << psEllipsisIndex;
const int psLocaleMask = 1 << psLocaleIndex;
// TextShadows decoding
constexpr uint32_t kColorDefault = 0xFF000000;
constexpr uint32_t kBytesPerShadow = 16;
constexpr uint32_t kShadowPropertiesCount = 4;
constexpr uint32_t kColorOffset = 0;
constexpr uint32_t kXOffset = 1;
constexpr uint32_t kYOffset = 2;
constexpr uint32_t kBlurOffset = 3;
// FontFeature decoding
constexpr uint32_t kBytesPerFontFeature = 8;
constexpr uint32_t kFontFeatureTagLength = 4;
// Strut decoding
const int sFontWeightIndex = 0;
const int sFontStyleIndex = 1;
const int sFontFamilyIndex = 2;
const int sFontSizeIndex = 3;
const int sHeightIndex = 4;
const int sLeadingIndex = 5;
const int sForceStrutHeightIndex = 6;
const int sFontWeightMask = 1 << sFontWeightIndex;
const int sFontStyleMask = 1 << sFontStyleIndex;
const int sFontFamilyMask = 1 << sFontFamilyIndex;
const int sFontSizeMask = 1 << sFontSizeIndex;
const int sHeightMask = 1 << sHeightIndex;
const int sLeadingMask = 1 << sLeadingIndex;
const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex;
} // namespace
fml::RefPtr<ParagraphBuilder> ParagraphBuilder::create(
int* encoded,
uint8_t* strutData,
int strutDataSize,
const std::string& fontFamily,
const std::vector<std::string>& strutFontFamilies,
double fontSize,
double height,
const std::u16string& ellipsis,
const std::string& locale) {
return fml::MakeRefCounted<ParagraphBuilder>(encoded, strutData, strutDataSize, fontFamily,
strutFontFamilies, fontSize,
height, ellipsis, locale);
}
void decodeTextShadows(uint8_t* shadows_data, int shadow_data_size,
std::vector<txt::TextShadow>& decoded_shadows) {
decoded_shadows.clear();
FML_CHECK(shadow_data_size % kBytesPerShadow == 0);
const uint32_t* uint_data = reinterpret_cast<const uint32_t*>(shadows_data);
const float* float_data = reinterpret_cast<const float*>(shadows_data);
size_t shadow_count = shadow_data_size / kBytesPerShadow;
size_t shadow_count_offset = 0;
for (size_t shadow_index = 0; shadow_index < shadow_count; ++shadow_index) {
shadow_count_offset = shadow_index * kShadowPropertiesCount;
SkColor color =
uint_data[shadow_count_offset + kColorOffset] ^ kColorDefault;
decoded_shadows.emplace_back(
color,
SkPoint::Make(float_data[shadow_count_offset + kXOffset],
float_data[shadow_count_offset + kYOffset]),
float_data[shadow_count_offset + kBlurOffset]);
}
}
void decodeStrut(uint8_t* strut_data, int struct_data_size,
const std::vector<std::string>& strut_font_families,
txt::ParagraphStyle& paragraph_style) {
if (strut_data == nullptr) {
return;
}
if (struct_data_size == 0) {
return;
}
paragraph_style.strut_enabled = true;
const uint8_t* uint8_data = static_cast<const uint8_t*>(strut_data);
uint8_t mask = uint8_data[0];
// Data is stored in order of increasing size, eg, 8 bit ints will be before
// any 32 bit ints. In addition, the order of decoding is the same order
// as it is encoded, and the order is used to maintain consistency.
size_t byte_count = 1;
if (mask & sFontWeightMask) {
paragraph_style.strut_font_weight =
static_cast<txt::FontWeight>(uint8_data[byte_count++]);
}
if (mask & sFontStyleMask) {
paragraph_style.strut_font_style =
static_cast<txt::FontStyle>(uint8_data[byte_count++]);
}
std::vector<float> float_data;
float_data.resize((struct_data_size - byte_count) / 4);
memcpy(float_data.data(),
reinterpret_cast<const char*>(strut_data) + byte_count,
struct_data_size - byte_count);
size_t float_count = 0;
if (mask & sFontSizeMask) {
paragraph_style.strut_font_size = float_data[float_count++];
}
if (mask & sHeightMask) {
paragraph_style.strut_height = float_data[float_count++];
paragraph_style.strut_has_height_override = true;
}
if (mask & sLeadingMask) {
paragraph_style.strut_leading = float_data[float_count++];
}
if (mask & sForceStrutHeightMask) {
// The boolean is stored as the last bit in the bitmask.
paragraph_style.force_strut_height = (mask & 1 << 7) != 0;
}
if (mask & sFontFamilyMask) {
paragraph_style.strut_font_families = strut_font_families;
}
else {
// Provide an empty font name so that the platform default font will be
// used.
paragraph_style.strut_font_families.push_back("");
}
}
ParagraphBuilder::ParagraphBuilder(
int* encoded,
uint8_t* strutData,
int strutData_size,
const std::string& fontFamily,
const std::vector<std::string>& strutFontFamilies,
double fontSize,
double height,
const std::u16string& ellipsis,
const std::string& locale) {
int32_t mask = encoded[0];
txt::ParagraphStyle style;
if (mask & psTextAlignMask) {
style.text_align = txt::TextAlign(encoded[psTextAlignIndex]);
}
if (mask & psTextDirectionMask) {
style.text_direction = txt::TextDirection(encoded[psTextDirectionIndex]);
}
if (mask & psFontWeightMask) {
style.font_weight =
static_cast<txt::FontWeight>(encoded[psFontWeightIndex]);
}
if (mask & psFontStyleMask) {
style.font_style = static_cast<txt::FontStyle>(encoded[psFontStyleIndex]);
}
if (mask & psFontFamilyMask) {
style.font_family = fontFamily;
}
if (mask & psFontSizeMask) {
style.font_size = fontSize;
}
if (mask & psHeightMask) {
style.height = height;
style.has_height_override = true;
}
if (mask & psTextHeightBehaviorMask) {
style.text_height_behavior = encoded[psTextHeightBehaviorIndex];
}
if (mask & psStrutStyleMask) {
decodeStrut(strutData, strutData_size, strutFontFamilies, style);
}
if (mask & psMaxLinesMask) {
style.max_lines = encoded[psMaxLinesIndex];
}
if (mask & psEllipsisMask) {
style.ellipsis = ellipsis;
}
if (mask & psLocaleMask) {
style.locale = locale;
}
// TODO: change to window
//FontCollection& font_collection =
// UIDartState::Current()->window()->client()->GetFontCollection();
std::shared_ptr<txt::FontCollection> collection =
std::make_shared<txt::FontCollection>();
collection->SetupDefaultFontManager();
#if FLUTTER_ENABLE_SKSHAPER
#define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateSkiaBuilder
#else
#define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateTxtBuilder
#endif
//m_paragraphBuilder =
// FLUTTER_PARAGRAPH_BUILDER(style, font_collection.GetFontCollection());
m_paragraphBuilder =
FLUTTER_PARAGRAPH_BUILDER(style, collection);
}
ParagraphBuilder::~ParagraphBuilder() = default;
void decodeFontFeatures(uint8_t* font_features_data, int font_features_data_size,
txt::FontFeatures& font_features) {
uint8_t* byte_data(font_features_data);
FML_CHECK(font_features_data_size % kBytesPerFontFeature == 0);
size_t feature_count = font_features_data_size / kBytesPerFontFeature;
const char* char_data = reinterpret_cast<const char*>(font_features_data);
for (size_t feature_index = 0; feature_index < feature_count;
++feature_index) {
size_t feature_offset = feature_index * kBytesPerFontFeature;
const char* feature_bytes =
char_data + feature_offset;
std::string tag(feature_bytes, kFontFeatureTagLength);
int32_t value = *(reinterpret_cast<const int32_t*>(feature_bytes +
kFontFeatureTagLength));
font_features.SetFeature(tag, value);
}
}
void ParagraphBuilder::pushStyle(
int* encoded,
int encodedSize,
char** fontFamilies,
int fontFamiliesSize,
double fontSize,
double letterSpacing,
double wordSpacing,
double height,
double decorationThickness,
const std::string& locale,
void** background_objects,
uint8_t* background_data,
void** foreground_objects,
uint8_t* foreground_data,
uint8_t* shadows_data,
int shadow_data_size,
uint8_t* font_features_data,
int font_feature_data_size) {
FML_DCHECK(encodedSize == 8);
int32_t mask = encoded[0];
// Set to use the properties of the previous style if the property is not
// explicitly given.
txt::TextStyle style = m_paragraphBuilder->PeekStyle();
// Only change the style property from the previous value if a new explicitly
// set value is available
if (mask & tsColorMask) {
style.color = encoded[tsColorIndex];
}
if (mask & tsTextDecorationMask) {
style.decoration =
static_cast<txt::TextDecoration>(encoded[tsTextDecorationIndex]);
}
if (mask & tsTextDecorationColorMask) {
style.decoration_color = encoded[tsTextDecorationColorIndex];
}
if (mask & tsTextDecorationStyleMask) {
style.decoration_style = static_cast<txt::TextDecorationStyle>(
encoded[tsTextDecorationStyleIndex]);
}
if (mask & tsTextDecorationThicknessMask) {
style.decoration_thickness_multiplier = decorationThickness;
}
if (mask & tsTextBaselineMask) {
// TODO(abarth): Implement TextBaseline. The CSS version of this
// property wasn't wired up either.
}
if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontSizeMask |
tsLetterSpacingMask | tsWordSpacingMask)) {
if (mask & tsFontWeightMask)
style.font_weight =
static_cast<txt::FontWeight>(encoded[tsFontWeightIndex]);
if (mask & tsFontStyleMask)
style.font_style = static_cast<txt::FontStyle>(encoded[tsFontStyleIndex]);
if (mask & tsFontSizeMask)
style.font_size = fontSize;
if (mask & tsLetterSpacingMask)
style.letter_spacing = letterSpacing;
if (mask & tsWordSpacingMask)
style.word_spacing = wordSpacing;
}
if (mask & tsHeightMask) {
style.height = height;
style.has_height_override = true;
}
if (mask & tsLocaleMask) {
style.locale = locale;
}
if (mask & tsBackgroundMask) {
Paint background(background_objects, background_data);
if (background.paint()) {
style.has_background = true;
style.background = *background.paint();
}
}
if (mask & tsForegroundMask) {
Paint foreground(foreground_objects, foreground_data);
if (foreground.paint()) {
style.has_foreground = true;
style.foreground = *foreground.paint();
}
}
if (mask & tsTextShadowsMask) {
decodeTextShadows(shadows_data, shadow_data_size, style.text_shadows);
}
if (mask & tsFontFamilyMask) {
// The child style's font families override the parent's font families.
// If the child's fonts are not available, then the font collection will
// use the system fallback fonts (not the parent's fonts).
style.font_families = std::vector<std::string>(fontFamilies, fontFamilies + fontFamiliesSize);
}
if (mask & tsFontFeaturesMask) {
decodeFontFeatures(font_features_data, font_feature_data_size, style.font_features);
}
m_paragraphBuilder->PushStyle(style);
}
void ParagraphBuilder::pop() {
m_paragraphBuilder->Pop();
}
const char* ParagraphBuilder::addText(const std::u16string& text) {
if (text.empty())
return nullptr;
// Use ICU to validate the UTF-16 input. Calling u_strToUTF8 with a null
// output buffer will return U_BUFFER_OVERFLOW_ERROR if the input is well
// formed.
const UChar* text_ptr = reinterpret_cast<const UChar*>(text.data());
UErrorCode error_code = U_ZERO_ERROR;
u_strToUTF8(nullptr, 0, nullptr, text_ptr, text.size(), &error_code);
if (error_code != U_BUFFER_OVERFLOW_ERROR)
return "string is not well-formed UTF-16";
m_paragraphBuilder->AddText(text);
return nullptr;
}
fml::RefPtr<Paragraph> ParagraphBuilder::build(/*Dart_Handle paragraph_handle*/) {
return Paragraph::Create(/*paragraph_handle,*/ m_paragraphBuilder->Build());
}
const char* ParagraphBuilder::addPlaceholder(
double width,
double height,
unsigned alignment,
double baseline_offset,
unsigned baseline) {
txt::PlaceholderRun placeholder_run(
width, height, static_cast<txt::PlaceholderAlignment>(alignment),
static_cast<txt::TextBaseline>(baseline), baseline_offset);
m_paragraphBuilder->AddPlaceholder(placeholder_run);
return nullptr;
}
UIWIDGETS_API(ParagraphBuilder*) ParagraphBuilder_constructor(
int* encoded,
int encodedSize,
uint8_t* structData,
int structDataSize,
char* fontFamily,
char** structFontFamily,
int structFontFamilySize,
double fontSize,
double height,
char16_t* ellipsis,
char* locale) {
std::string fontFamily_s= fontFamily ? std::string(fontFamily) : "";
auto structFamily_v = structFontFamily ? std::vector<std::string>(structFontFamily, structFontFamily + structFontFamilySize) : std::vector<std::string>();
auto ellipsis_s = ellipsis ? std::u16string(ellipsis) : u"";
auto local_s = locale ? std::string(locale) : "";
fml::RefPtr<ParagraphBuilder> paragraphBuilder = ParagraphBuilder::create(
encoded,
structData,
structDataSize,
fontFamily_s,
structFamily_v,
fontSize,
height,
ellipsis_s,
local_s
);
paragraphBuilder->AddRef();
return paragraphBuilder.get();
}
UIWIDGETS_API(void) ParagraphBuilder_dispose(Canvas* ptr) { ptr->Release(); }
UIWIDGETS_API(void) ParagraphBuilder_pushStyle(
ParagraphBuilder* ptr,
int* encoded,
int encodedSize,
char** fontFamilies,
int fontFamiliesSize,
double* fontSize,
double* letterSpacing,
double* wordSpacing,
double* height,
double* decorationThickness,
char* locale,
void** background_objects,
uint8_t* background_data,
void** foreground_objects,
uint8_t* foreground_data,
uint8_t* shadows_data,
int shadow_data_size,
uint8_t* font_features_data,
int font_feature_data_size
) {
ptr->pushStyle(encoded,
encodedSize,
fontFamilies,
fontFamiliesSize,
*fontSize,
*letterSpacing,
*wordSpacing,
*height,
*decorationThickness,
locale,
background_objects,
background_data,
foreground_objects,
foreground_data,
shadows_data,
shadow_data_size,
font_features_data,
font_feature_data_size
);
}
UIWIDGETS_API(void) ParagraphBuilder_pop(ParagraphBuilder* ptr) {
ptr->pop();
}
UIWIDGETS_API(const char*) ParagraphBuilder_addText(ParagraphBuilder* ptr, char16_t* text) {
return ptr->addText(std::u16string(text));
}
UIWIDGETS_API(const char*) ParagraphBuilder_addPlaceholder(ParagraphBuilder* ptr, float width, float height, int alignment, float baselineOffset, unsigned baseline) {
return ptr->addPlaceholder(width, height, alignment, baselineOffset, baseline);
}
UIWIDGETS_API(Paragraph*) ParagraphBuilder_build(ParagraphBuilder* ptr/*, IntPtr outParagraph*/) {
auto paragraph = ptr->build();
paragraph->AddRef();
return paragraph.get();
}
}

64
engine/src/lib/ui/txt/paragraph_builder.h


#pragma once
#include <flutter/fml/memory/ref_counted.h>
#include <src\lib\ui\painting\canvas.h>
#include <flutter\third_party\txt\src\txt\paragraph.h>
#include "flutter/third_party/txt/src/txt/paragraph_builder.h"
#include "utils.h"
#include "paragraph.h"
namespace uiwidgets {
class ParagraphBuilder : public fml::RefCountedThreadSafe<ParagraphBuilder> {
FML_FRIEND_MAKE_REF_COUNTED(ParagraphBuilder);
public:
static fml::RefPtr<ParagraphBuilder> create(int* encoded, uint8_t* structData, int structDataSize, const std::string& fontFamily, const std::vector<std::string>& strutFontFamilies, double fontSize, double height, const std::u16string& ellipsis, const std::string& locale);
void pushStyle(
int* encoded,
int encodedSize,
char** fontFamilies,
int fontFamiliesSize,
double fontSize,
double letterSpacing,
double wordSpacing,
double height,
double decorationThickness,
const std::string& locale,
void** background_objects,
uint8_t* background_data,
void** foreground_objects,
uint8_t* foreground_data,
uint8_t* shadows_data,
int shadow_data_size,
uint8_t* font_features_data,
int font_feature_data_size);
const char* addText(const std::u16string& text);
const char* addPlaceholder(
double width,
double height,
unsigned alignment,
double baseline_offset,
unsigned baseline);
fml::RefPtr<Paragraph> build(/*Dart_Handle paragraph_handle*/);
~ParagraphBuilder();
void pop();
private:
explicit ParagraphBuilder(
int* encoded,
uint8_t* strutData,
int strutData_size,
const std::string& fontFamily,
const std::vector<std::string>& strutFontFamilies,
double fontSize,
double height,
const std::u16string& ellipsis,
const std::string& locale);
std::unique_ptr<txt::ParagraphBuilder> m_paragraphBuilder;
};
}

20
engine/src/lib/ui/txt/utils.h


#pragma once
namespace uiwidgets {
#define DATALIST(T,N) \
extern "C" struct N{\
T* data;\
int length;\
};
DATALIST(float, Float32List)
DATALIST(int, Int32List)
DATALIST(size_t, SizeTList)
DATALIST(char, CharList)
DATALIST(CharList, StringList)
extern "C" struct StringList2 {
char** data;
int length;
};
}
正在加载...
取消
保存