Revert "[COLRv1] Support retrieving ClipBox."

This reverts commit 4f12b4a599.

Reason for revert: MSAN issue

Original change's description:
> [COLRv1] Support retrieving ClipBox.
>
> After the discussion in [1] which was filed also in response to feedback
> from Ben, the COLRv1 spec moved to not using a bounding box derived from
> the `glyf` glyph for a give glyph id, but instead either use a ClipBox
> found for a particular glyph id range from a ClipList array in the
> COLRv1 table. If such a ClipBox is not found, perform a traversal of the
> COLRv1 graph to compute the union of rectangles to compute a bounding
> box.
>
> [1] https://github.com/googlefonts/colr-gradients-spec/issues/251
>
> Includes FreeType roll:
> 47b1a541cb..2c853b38a7
>
> Fixed: skia:12297
> Cq-Include-Trybots: luci.skia.skia.primary:Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-All-Android_NativeFonts
> Change-Id: Iac3677cb1207b953876b106a1e0d754760612955
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/435116
> Reviewed-by: Ben Wagner <bungeman@google.com>
> Commit-Queue: Dominik Röttsches <drott@google.com>

TBR=bungeman@google.com,drott@chromium.org,drott@google.com,behdad.esfahbod@gmail.com,skcq-be@skia-corp.google.com.iam.gserviceaccount.com

Change-Id: Id041a270bd2e4d4edcaa114c524981bc2c9e5320
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Cq-Include-Trybots: luci.skia.skia.primary:Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-All-Android_NativeFonts
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/437738
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2021-08-09 17:57:27 +00:00 committed by SkCQ
parent 3176e868c4
commit df1ba0395f
6 changed files with 60 additions and 324 deletions

2
DEPS
View File

@ -23,7 +23,7 @@ deps = {
"third_party/externals/dng_sdk" : "https://android.googlesource.com/platform/external/dng_sdk.git@c8d0c9b1d16bfda56f15165d39e0ffa360a11123",
"third_party/externals/egl-registry" : "https://skia.googlesource.com/external/github.com/KhronosGroup/EGL-Registry@a0bca08de07c7d7651047bedc0b653cfaaa4f2ae",
"third_party/externals/expat" : "https://chromium.googlesource.com/external/github.com/libexpat/libexpat.git@a28238bdeebc087071777001245df1876a11f5ee",
"third_party/externals/freetype" : "https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@2c853b38a717c615d3113a64033fc896e5888fa8",
"third_party/externals/freetype" : "https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@47b1a541cb1943d85da3976b93f9a5ed490288e2",
"third_party/externals/harfbuzz" : "https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@368e9578873798e2d17ed78a0474dec7d4e9d6c0",
"third_party/externals/icu" : "https://chromium.googlesource.com/chromium/deps/icu.git@a0718d4f121727e30b8d52c7a189ebf5ab52421f",
"third_party/externals/imgui" : "https://skia.googlesource.com/external/github.com/ocornut/imgui.git@9418dcb69355558f70de260483424412c5ca2fce",

View File

@ -35,8 +35,7 @@ public:
kColorFontsRepoExtendMode,
kColorFontsRepoRotate,
kColorFontsRepoSkew,
kColorFontsRepoTransform,
kColorFontsRepoClipBox
kColorFontsRepoTransform
};
ColrV1GM(ColrV1TestType testType, SkScalar skewX, SkScalar rotateDeg)
@ -59,8 +58,6 @@ protected:
return SkString("skew");
case kColorFontsRepoTransform:
return SkString("transform");
case kColorFontsRepoClipBox:
return SkString("clipbox");
}
SkASSERT(false); /* not reached */
return SkString();
@ -103,9 +100,6 @@ protected:
case kColorFontsRepoTransform:
fEmojiFont.fGlyphs = {31, 32, 33, 34};
break;
case kColorFontsRepoClipBox:
fEmojiFont.fGlyphs = {35, 36, 37, 38, 39};
break;
}
}
@ -172,7 +166,5 @@ DEF_GM(return new ColrV1GM(ColrV1GM::kColorFontsRepoExtendMode, 0.f, 0.f);)
DEF_GM(return new ColrV1GM(ColrV1GM::kColorFontsRepoRotate, 0.f, 0.f);)
DEF_GM(return new ColrV1GM(ColrV1GM::kColorFontsRepoSkew, 0.f, 0.f);)
DEF_GM(return new ColrV1GM(ColrV1GM::kColorFontsRepoTransform, 0.f, 0.f);)
DEF_GM(return new ColrV1GM(ColrV1GM::kColorFontsRepoClipBox, 0.f, 0.f);)
DEF_GM(return new ColrV1GM(ColrV1GM::kColorFontsRepoClipBox, -0.5f, 20.f);)
} // namespace skiagm

View File

@ -37,7 +37,6 @@
#include <ft2build.h>
#include <freetype/ftadvanc.h>
#include <freetype/ftimage.h>
#include <freetype/ftbitmap.h>
#ifdef FT_COLOR_H // 2.10.0
# include <freetype/ftcolor.h>
@ -1105,62 +1104,60 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
opaqueLayerPaint.p = nullptr;
if (FT_Get_Color_Glyph_Paint(fFace, glyph->getGlyphID(),
FT_COLOR_INCLUDE_ROOT_TRANSFORM, &opaqueLayerPaint)) {
haveLayers = true;
haveLayers = true;
FT_ClipBox colrGlyphBbox;
// COLRv1 glyphs define a placeholder glyph in the glyf table that
// defines the extrema of the glyph. If this placeholder glyph
// contains only two points and FreeType applies the transform first -
// as configured by setupSize() - then we get a transformed bounding
// box that is too small as it only needs to fit the two points. To
// fix that, create a temporary contour consisting of all four corners
// of an imaginary rectangle enclosing those two points, then
// transform that and perform the bounding box computations on this
// one.
// COLRv1 optionally provides a ClipBox that we can use for allocation.
if (FT_Get_Color_Glyph_ClipBox(fFace, glyph->getGlyphID(), &colrGlyphBbox)) {
// Find enclosing bounding box of clip box corner points, needed
// when clipbox is transformed.
bounds.xMin = colrGlyphBbox.bottom_left.x;
bounds.xMax = colrGlyphBbox.bottom_left.x;
bounds.yMin = colrGlyphBbox.bottom_left.y;
bounds.yMax = colrGlyphBbox.bottom_left.y;
FT_Set_Transform(fFace, nullptr, nullptr);
for (auto& corner : {colrGlyphBbox.top_left,
colrGlyphBbox.top_right,
colrGlyphBbox.bottom_right}) {
if (corner.x < bounds.xMin) {
bounds.xMin = corner.x;
}
if (corner.y < bounds.yMin) {
bounds.yMin = corner.y;
}
if (corner.x > bounds.xMax) {
bounds.xMax = corner.x;
}
if (corner.y > bounds.yMax) {
bounds.yMax = corner.y;
}
}
} else {
// Otherwise we need to traverse the glyph graph with a focus on measuring the
// required bounding box.
FT_BBox computed_bounds;
if (!computeColrV1GlyphBoundingBox(fFace, glyph->getGlyphID(), &computed_bounds)) {
glyph->zeroMetrics();
return;
}
// Reset face so the main glyph slot contains information about the
// base glyph again, for usage for computing and copying horizontal
// metrics from FreeType to Skia below.
if (this->setupSize()) {
glyph->zeroMetrics();
return;
}
FT_Error err;
err = FT_Load_Glyph(
fFace, glyph->getGlyphID(), fLoadGlyphFlags | FT_LOAD_BITMAP_METRICS_ONLY);
if (err != 0) {
glyph->zeroMetrics();
return;
}
bounds = computed_bounds;
err = FT_Load_Glyph(fFace, glyph->getGlyphID(),
fLoadGlyphFlags | FT_LOAD_BITMAP_METRICS_ONLY);
if (err != 0 || fFace->glyph->outline.n_contours == 0) {
glyph->zeroMetrics();
return;
}
emboldenIfNeeded(fFace, fFace->glyph, glyph->getGlyphID());
FT_BBox bbox_untransformed;
FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox_untransformed);
FT_Outline bboxOutline;
err = FT_Outline_New(gFTLibrary->library(), 4, 0, &bboxOutline);
if (err != 0) {
glyph->zeroMetrics();
return;
}
// Compose a rectangle contour by setting the four corners.
bboxOutline.points[0].x = bbox_untransformed.xMin;
bboxOutline.points[0].y = bbox_untransformed.yMin;
bboxOutline.points[1].x = bbox_untransformed.xMin;
bboxOutline.points[1].y = bbox_untransformed.yMax;
bboxOutline.points[2].x = bbox_untransformed.xMax;
bboxOutline.points[2].y = bbox_untransformed.yMax;
bboxOutline.points[3].x = bbox_untransformed.xMax;
bboxOutline.points[3].y = bbox_untransformed.yMin;
FT_Outline_Transform(&bboxOutline, &fMatrix22);
FT_Outline faceOutline = fFace->glyph->outline;
fFace->glyph->outline = bboxOutline;
// Retrieve from temporarily replaced transformed rectangle outline.
getBBoxForCurrentGlyph(glyph, &bounds, true);
fFace->glyph->outline = faceOutline;
FT_Outline_Done(gFTLibrary->library(), &bboxOutline);
}
#endif // #TT_SUPPORT_COLRV1

View File

@ -756,18 +756,9 @@ void colrv1_draw_glyph_with_path(SkCanvas* canvas, const FT_Color* palette, FT_F
}
}
/* In drawing mode, concatenates the transforms directly on SkCanvas. In
* bounding box calculation mode, no SkCanvas is specified, but we only want to
* retrieve the transform from the FreeType paint object. */
void colrv1_transform(FT_Face face,
FT_COLR_Paint colrv1_paint,
SkCanvas* canvas,
SkMatrix* out_transform = 0) {
void colrv1_transform(SkCanvas* canvas, FT_Face face, FT_COLR_Paint colrv1_paint) {
SkMatrix transform;
SkASSERT(canvas || out_transform);
switch (colrv1_paint.format) {
case FT_COLR_PAINTFORMAT_TRANSFORM: {
transform = ToSkMatrix(colrv1_paint.u.transform.affine);
@ -818,14 +809,10 @@ void colrv1_transform(FT_Face face,
SkASSERT(false);
}
}
if (canvas) {
canvas->concat(transform);
}
if (out_transform) {
*out_transform = transform;
}
canvas->concat(transform);
}
bool colrv1_start_glyph(SkCanvas* canvas,
const FT_Color* palette,
FT_Face ft_face,
@ -890,28 +877,28 @@ bool colrv1_traverse_paint(SkCanvas* canvas,
FT_COLOR_NO_ROOT_TRANSFORM);
break;
case FT_COLR_PAINTFORMAT_TRANSFORM:
colrv1_transform(face, paint, canvas);
colrv1_transform(canvas, face, paint);
traverse_result = colrv1_traverse_paint(canvas, palette, face,
paint.u.transform.paint, visited_set);
break;
case FT_COLR_PAINTFORMAT_TRANSLATE:
colrv1_transform(face, paint, canvas);
colrv1_transform(canvas, face, paint);
traverse_result = colrv1_traverse_paint(canvas, palette, face,
paint.u.translate.paint, visited_set);
break;
case FT_COLR_PAINTFORMAT_SCALE:
colrv1_transform(face, paint, canvas);
colrv1_transform(canvas, face, paint);
traverse_result = colrv1_traverse_paint(canvas, palette, face,
paint.u.scale.paint, visited_set);
break;
case FT_COLR_PAINTFORMAT_ROTATE:
colrv1_transform(face, paint, canvas);
colrv1_transform(canvas, face, paint);
traverse_result =
colrv1_traverse_paint(canvas, palette, face,
paint.u.rotate.paint, visited_set);
break;
case FT_COLR_PAINTFORMAT_SKEW:
colrv1_transform(face, paint, canvas);
colrv1_transform(canvas, face, paint);
traverse_result =
colrv1_traverse_paint(canvas, palette, face,
paint.u.skew.paint, visited_set);
@ -943,72 +930,6 @@ bool colrv1_traverse_paint(SkCanvas* canvas,
return traverse_result;
}
SkPath GetClipBoxPath(FT_Face ft_face, uint16_t glyph_id, bool untransformed) {
SkPath resultPath;
using DoneFTSize = SkFunctionWrapper<decltype(FT_Done_Size), FT_Done_Size>;
std::unique_ptr<std::remove_pointer_t<FT_Size>, DoneFTSize> unscaledFtSize = nullptr;
FT_Size oldSize = ft_face->size;
FT_Matrix oldTransform;
FT_Vector oldDelta;
FT_Error err = 0;
if (untransformed) {
unscaledFtSize.reset(
[ft_face]() -> FT_Size {
FT_Size size;
FT_Error err = FT_New_Size(ft_face, &size);
if (err != 0) {
SK_TRACEFTR(err,
"FT_New_Size(%s) failed in generateFacePathStaticCOLRv1.",
ft_face->family_name);
return nullptr;
}
return size;
}());
if (!unscaledFtSize) {
return resultPath;
}
err = FT_Activate_Size(unscaledFtSize.get());
if (err != 0) {
return resultPath;
}
err = FT_Set_Char_Size(ft_face, SkIntToFDot6(ft_face->units_per_EM), 0, 0, 0);
if (err != 0) {
return resultPath;
}
FT_Get_Transform(ft_face, &oldTransform, &oldDelta);
FT_Set_Transform(ft_face, nullptr, nullptr);
}
FT_ClipBox colrGlyphClipBox;
if (FT_Get_Color_Glyph_ClipBox(ft_face, glyph_id, &colrGlyphClipBox)) {
resultPath = SkPath::Polygon({{SkFDot6ToScalar(colrGlyphClipBox.bottom_left.x),
-SkFDot6ToScalar(colrGlyphClipBox.bottom_left.y)},
{SkFDot6ToScalar(colrGlyphClipBox.top_left.x),
-SkFDot6ToScalar(colrGlyphClipBox.top_left.y)},
{SkFDot6ToScalar(colrGlyphClipBox.top_right.x),
-SkFDot6ToScalar(colrGlyphClipBox.top_right.y)},
{SkFDot6ToScalar(colrGlyphClipBox.bottom_right.x),
-SkFDot6ToScalar(colrGlyphClipBox.bottom_right.y)}},
true);
}
if (untransformed) {
err = FT_Activate_Size(oldSize);
if (err != 0) {
return resultPath;
}
FT_Set_Transform(ft_face, &oldTransform, &oldDelta);
}
return resultPath;
}
bool colrv1_start_glyph(SkCanvas* canvas,
const FT_Color* palette,
FT_Face ft_face,
@ -1019,149 +940,11 @@ bool colrv1_start_glyph(SkCanvas* canvas,
bool has_colrv1_layers = false;
if (FT_Get_Color_Glyph_Paint(ft_face, glyph_id, root_transform, &opaque_paint)) {
has_colrv1_layers = true;
SkPath clipBoxPath =
GetClipBoxPath(ft_face, glyph_id, root_transform == FT_COLOR_NO_ROOT_TRANSFORM);
if (!clipBoxPath.isEmpty()) {
canvas->clipPath(clipBoxPath, true);
}
VisitedSet visited_set;
colrv1_traverse_paint(canvas, palette, ft_face, opaque_paint, &visited_set);
}
return has_colrv1_layers;
}
bool colrv1_start_glyph_bounds(SkMatrix *ctm,
SkRect* bounds,
FT_Face ft_face,
uint16_t glyph_id,
FT_Color_Root_Transform root_transform);
bool colrv1_traverse_paint_bounds(SkMatrix* ctm,
SkRect* bounds,
FT_Face face,
FT_OpaquePaint opaque_paint,
VisitedSet* visited_set) {
// Cycle detection, see section "5.7.11.1.9 Color glyphs as a directed acyclic graph".
if (visited_set->contains(opaque_paint)) {
return false;
}
visited_set->add(opaque_paint);
SK_AT_SCOPE_EXIT(visited_set->remove(opaque_paint));
FT_COLR_Paint paint;
if (!FT_Get_Paint(face, opaque_paint, &paint)) {
return false;
}
// Keep track of failures to retrieve the FT_COLR_Paint from FreeType in the
// recursion, cancel recursion when a paint retrieval fails.
bool traverse_result = true;
SkMatrix restore_matrix = *ctm;
SK_AT_SCOPE_EXIT(*ctm = restore_matrix);
switch (paint.format) {
case FT_COLR_PAINTFORMAT_COLR_LAYERS: {
FT_LayerIterator& layer_iterator = paint.u.colr_layers.layer_iterator;
FT_OpaquePaint opaque_paint_fetch;
opaque_paint_fetch.p = nullptr;
while (FT_Get_Paint_Layers(face, &layer_iterator, &opaque_paint_fetch)) {
colrv1_traverse_paint_bounds(ctm, bounds, face, opaque_paint_fetch, visited_set);
}
break;
}
case FT_COLR_PAINTFORMAT_GLYPH: {
FT_UInt glyphID = paint.u.glyph.glyphID;
SkPath path;
if ((traverse_result = generateFacePathCOLRv1(face, glyphID, &path))) {
path.transform(*ctm);
bounds->join(path.getBounds());
}
break;
}
case FT_COLR_PAINTFORMAT_COLR_GLYPH:
traverse_result = colrv1_start_glyph_bounds(
ctm, bounds, face, paint.u.colr_glyph.glyphID, FT_COLOR_NO_ROOT_TRANSFORM);
break;
case FT_COLR_PAINTFORMAT_TRANSFORM: {
SkMatrix transform_matrix;
colrv1_transform(face, paint, nullptr, &transform_matrix);
ctm->preConcat(transform_matrix);
traverse_result = colrv1_traverse_paint_bounds(
ctm, bounds, face, paint.u.transform.paint, visited_set);
break;
}
case FT_COLR_PAINTFORMAT_TRANSLATE: {
SkMatrix transform_matrix;
colrv1_transform(face, paint, nullptr, &transform_matrix);
ctm->preConcat(transform_matrix);
traverse_result = colrv1_traverse_paint_bounds(
ctm, bounds, face, paint.u.translate.paint, visited_set);
break;
}
case FT_COLR_PAINTFORMAT_SCALE: {
SkMatrix transform_matrix;
colrv1_transform(face, paint, nullptr, &transform_matrix);
ctm->preConcat(transform_matrix);
traverse_result = colrv1_traverse_paint_bounds(
ctm, bounds, face, paint.u.scale.paint, visited_set);
break;
}
case FT_COLR_PAINTFORMAT_ROTATE: {
SkMatrix transform_matrix;
colrv1_transform(face, paint, nullptr, &transform_matrix);
ctm->preConcat(transform_matrix);
traverse_result = colrv1_traverse_paint_bounds(
ctm, bounds, face, paint.u.rotate.paint, visited_set);
break;
}
case FT_COLR_PAINTFORMAT_SKEW: {
SkMatrix transform_matrix;
colrv1_transform(face, paint, nullptr, &transform_matrix);
ctm->preConcat(transform_matrix);
traverse_result = colrv1_traverse_paint_bounds(
ctm, bounds, face, paint.u.skew.paint, visited_set);
break;
}
case FT_COLR_PAINTFORMAT_COMPOSITE: {
traverse_result = colrv1_traverse_paint_bounds(
ctm, bounds, face, paint.u.composite.backdrop_paint, visited_set);
traverse_result = colrv1_traverse_paint_bounds(
ctm, bounds, face, paint.u.composite.source_paint, visited_set);
break;
}
case FT_COLR_PAINTFORMAT_SOLID:
case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
break;
}
default:
SkASSERT(false);
break;
}
return traverse_result;
}
bool colrv1_start_glyph_bounds(SkMatrix *ctm,
SkRect* bounds,
FT_Face ft_face,
uint16_t glyph_id,
FT_Color_Root_Transform root_transform) {
FT_OpaquePaint opaque_paint;
opaque_paint.p = nullptr;
bool has_colrv1_layers = false;
if (FT_Get_Color_Glyph_Paint(ft_face, glyph_id, root_transform, &opaque_paint)) {
has_colrv1_layers = true;
VisitedSet visited_set;
colrv1_traverse_paint_bounds(ctm, bounds, ft_face, opaque_paint, &visited_set);
}
return has_colrv1_layers;
}
#endif // TT_SUPPORT_COLRV1
} // namespace
@ -1667,27 +1450,3 @@ bool SkScalerContext_FreeType_Base::generateFacePath(FT_Face face,
SkPath* path) {
return generateFacePathStatic(face, glyphID, path);
}
bool SkScalerContext_FreeType_Base::computeColrV1GlyphBoundingBox(FT_Face face,
SkGlyphID glyphID,
FT_BBox* boundingBox) {
#ifdef TT_SUPPORT_COLRV1
SkMatrix ctm;
SkRect bounds;
if (!colrv1_start_glyph_bounds(&ctm, &bounds, face, glyphID, FT_COLOR_INCLUDE_ROOT_TRANSFORM)) {
return false;
}
/* Convert back to FT_BBox as caller needs it in this format. */
bounds.sort();
boundingBox->xMin = SkScalarToFDot6(bounds.left());
boundingBox->xMax = SkScalarToFDot6(bounds.right());
boundingBox->yMin = SkScalarToFDot6(-bounds.bottom());
boundingBox->yMax = SkScalarToFDot6(-bounds.top());
return true;
#else
SkASSERT(false);
return false;
#endif
}

View File

@ -23,8 +23,6 @@ typedef struct FT_LibraryRec_* FT_Library;
typedef struct FT_FaceRec_* FT_Face;
typedef struct FT_StreamRec_* FT_Stream;
typedef signed long FT_Pos;
typedef struct FT_BBox_ FT_BBox;
#ifdef SK_DEBUG
const char* SkTraceFtrGetError(int);
@ -50,16 +48,6 @@ protected:
void generateGlyphImage(FT_Face face, const SkGlyph& glyph, const SkMatrix& bitmapTransform);
bool generateGlyphPath(FT_Face face, SkPath* path);
bool generateFacePath(FT_Face face, SkGlyphID glyphID, SkPath* path);
// Computes a bounding box for a COLRv1 glyph id in FT_BBox 26.6 format and FreeType's y-up
// coordinate space.
// Needed to call into COLRv1 from generateMetrics().
//
// Note : This method may change the configured size and transforms on FT_Face. Make sure to
// configure size, matrix and load glyphs as needed after using this function to restore the
// state of FT_Face.
bool computeColrV1GlyphBoundingBox(FT_Face face, SkGlyphID glyphID, FT_BBox* boundingBox);
private:
using INHERITED = SkScalerContext;
};