[DisplayList] Migrate from SkRSXform to Impeller RSTransform (#161652)

Fixes SkRSXform task in https://github.com/flutter/flutter/issues/161456

Removes (nearly) all uses of Skia SkRSXform object from DisplayList and
replaces it with a new Impeller RSTransform geometry object.

There are remaining uses in:
- Skia adapter code which needs to convert them back to SkRSXform when
using the Skia backend
- dl_rendering_tests which is waiting for a major conversion effort
- ?Fuchsia? code has an SkCanvas spy adapter used in its embedder code
(not DisplayList related)
- web_ui/skwasm
This commit is contained in:
Jim Graham 2025-01-15 11:08:38 -08:00 committed by GitHub
parent 1580a3d3fb
commit a10e25e727
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 437 additions and 177 deletions

View File

@ -174,6 +174,7 @@
../../../flutter/impeller/geometry/path_unittests.cc
../../../flutter/impeller/geometry/rect_unittests.cc
../../../flutter/impeller/geometry/round_rect_unittests.cc
../../../flutter/impeller/geometry/rstransform_unittests.cc
../../../flutter/impeller/geometry/saturated_math_unittests.cc
../../../flutter/impeller/geometry/size_unittests.cc
../../../flutter/impeller/geometry/trig_unittests.cc

View File

@ -42513,6 +42513,8 @@ ORIGIN: ../../../flutter/impeller/geometry/round_rect.cc + ../../../flutter/LICE
ORIGIN: ../../../flutter/impeller/geometry/round_rect.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/rounding_radii.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/rounding_radii.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/rstransform.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/rstransform.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/saturated_math.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/scalar.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/geometry/separated_vector.cc + ../../../flutter/LICENSE
@ -45456,6 +45458,8 @@ FILE: ../../../flutter/impeller/geometry/round_rect.cc
FILE: ../../../flutter/impeller/geometry/round_rect.h
FILE: ../../../flutter/impeller/geometry/rounding_radii.cc
FILE: ../../../flutter/impeller/geometry/rounding_radii.h
FILE: ../../../flutter/impeller/geometry/rstransform.cc
FILE: ../../../flutter/impeller/geometry/rstransform.h
FILE: ../../../flutter/impeller/geometry/saturated_math.h
FILE: ../../../flutter/impeller/geometry/scalar.h
FILE: ../../../flutter/impeller/geometry/separated_vector.cc

View File

@ -158,7 +158,7 @@ class ComplexityCalculatorHelper
}
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -421,11 +421,11 @@ TEST(DisplayListComplexity, DrawAtlas) {
bitmap.allocPixels(info, 0);
auto image = SkImages::RasterFromBitmap(bitmap);
std::vector<SkRect> rects;
std::vector<SkRSXform> xforms;
std::vector<DlRect> rects;
std::vector<DlRSTransform> xforms;
for (int i = 0; i < 10; i++) {
rects.push_back(SkRect::MakeXYWH(0, 0, 10, 10));
xforms.push_back(SkRSXform::Make(1, 0, 0, 0));
rects.push_back(DlRect::MakeXYWH(0, 0, 10, 10));
xforms.push_back(DlRSTransform(1, 0, 0, 0));
}
DisplayListBuilder builder;

View File

@ -1201,7 +1201,10 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) {
kTestImage2, DlIRect::MakeLTRB(20, 20, 30, 30),
DlRect::MakeLTRB(0, 0, 20, 20), DlFilterMode::kLinear, nullptr);
, true);
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {
DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30),
@ -3502,7 +3505,10 @@ TEST_F(DisplayListTest, NopOperationsOmittedFromRecords) {
builder.DrawImageNine(kTestImage1, DlIRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(10, 10, 100, 100),
DlFilterMode::kLinear, &paint);
SkRSXform xforms[] = {{1, 0, 10, 10}, {0, 1, 10, 10}};
DlRSTransform xforms[] = {
DlRSTransform::Make({10.0f, 10.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({10.0f, 10.0f}, 1.0f, DlDegrees(90)),
};
DlRect rects[] = {
DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(10, 20, 30, 20),

View File

@ -1467,7 +1467,7 @@ void DisplayListBuilder::DrawImageNine(const sk_sp<DlImage>& image,
}
}
void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,
@ -1482,19 +1482,20 @@ void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
if (result == OpResult::kNoEffect) {
return;
}
SkPoint quad[4];
DlQuad quad;
AccumulationRect accumulator;
for (int i = 0; i < count; i++) {
const DlRect& src = tex[i];
xform[i].toQuad(src.GetWidth(), src.GetHeight(), quad);
xform[i].GetQuad(src.GetWidth(), src.GetHeight(), quad);
for (int j = 0; j < 4; j++) {
accumulator.accumulate(ToDlPoint(quad[j]));
accumulator.accumulate(quad[j]);
}
}
if (accumulator.is_empty() ||
!AccumulateOpBounds(accumulator.GetBounds(), flags)) {
return;
}
// Accumulating the bounds might not trip the overlap condition if the
// whole atlas operation is separated from other rendering calls, but
// since each atlas op is treated as an independent operation, we have
@ -1509,7 +1510,7 @@ void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
current_layer().layer_local_accumulator.record_overlapping_bounds();
}
int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
int bytes = count * (sizeof(DlRSTransform) + sizeof(DlRect));
void* data_ptr;
if (colors != nullptr) {
bytes += count * sizeof(DlColor);
@ -1541,7 +1542,7 @@ void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe();
}
void DisplayListBuilder::DrawAtlas(const sk_sp<DlImage>& atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -209,7 +209,7 @@ class DisplayListBuilder final : public virtual DlCanvas,
const DlPaint* paint = nullptr) override;
// |DlCanvas|
void DrawAtlas(const sk_sp<DlImage>& atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,
@ -471,7 +471,7 @@ class DisplayListBuilder final : public virtual DlCanvas,
bool render_with_attributes) override;
// |DlOpReceiver|
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -17,7 +17,6 @@
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRSXform.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkTextBlob.h"
@ -189,7 +188,7 @@ class DlCanvas {
DlFilterMode filter,
const DlPaint* paint = nullptr) = 0;
virtual void DrawAtlas(const sk_sp<DlImage>& atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,
@ -373,18 +372,6 @@ class DlCanvas {
const DlPaint* paint = nullptr) {
DrawImageNine(image, ToDlIRect(center), ToDlRect(dst), filter, paint);
}
void DrawAtlas(const sk_sp<DlImage>& atlas,
const SkRSXform xform[],
const SkRect tex[],
const DlColor colors[],
int count,
DlBlendMode mode,
DlImageSampling sampling,
const SkRect* cullRect,
const DlPaint* paint = nullptr) {
DrawAtlas(atlas, xform, ToDlRects(tex), colors, count, mode, sampling,
ToDlRect(cullRect), paint);
}
void DrawShadow(const SkPath& path,
const DlColor color,
const DlScalar elevation,

View File

@ -345,7 +345,7 @@ class DlOpReceiver {
DlFilterMode filter,
bool render_with_attributes) = 0;
virtual void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -15,7 +15,6 @@
#include "flutter/impeller/geometry/path.h"
#include "flutter/impeller/typographer/text_frame.h"
#include "third_party/skia/include/core/SkRSXform.h"
namespace flutter {
@ -841,7 +840,7 @@ DEFINE_DRAW_IMAGE_NINE_OP(DrawImageNineWithAttr, true)
// 4 byte header + 40 byte payload uses 44 bytes but is rounded up to 48 bytes
// (4 bytes unused)
// Each of these is then followed by a number of lists.
// SkRSXform list is a multiple of 16 bytes so it is always packed well
// DlRSTransform list is a multiple of 16 bytes so it is always packed well
// DlRect list is also a multiple of 16 bytes so it also packs well
// DlColor list only packs well if the count is even, otherwise there
// can be 4 unusued bytes at the end.
@ -876,7 +875,7 @@ struct DrawAtlasBaseOp : DrawOpBase {
render_with_attributes == other->render_with_attributes &&
sampling == other->sampling && atlas->Equals(other->atlas));
if (ret) {
size_t bytes = count * (sizeof(SkRSXform) + sizeof(DlRect));
size_t bytes = count * (sizeof(DlRSTransform) + sizeof(DlRect));
if (has_colors) {
bytes += count * sizeof(DlColor);
}
@ -906,7 +905,8 @@ struct DrawAtlasOp final : DrawAtlasBaseOp {
render_with_attributes) {}
void dispatch(DlOpReceiver& receiver) const {
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
const DlRSTransform* xform =
reinterpret_cast<const DlRSTransform*>(this + 1);
const DlRect* tex = reinterpret_cast<const DlRect*>(xform + count);
const DlColor* colors =
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
@ -950,7 +950,8 @@ struct DrawAtlasCulledOp final : DrawAtlasBaseOp {
const DlRect cull_rect;
void dispatch(DlOpReceiver& receiver) const {
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
const DlRSTransform* xform =
reinterpret_cast<const DlRSTransform*>(this + 1);
const DlRect* tex = reinterpret_cast<const DlRect*>(xform + count);
const DlColor* colors =
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;

View File

@ -9,6 +9,7 @@
#include "flutter/impeller/geometry/path.h"
#include "flutter/impeller/geometry/rect.h"
#include "flutter/impeller/geometry/round_rect.h"
#include "flutter/impeller/geometry/rstransform.h"
#include "flutter/impeller/geometry/scalar.h"
#include "flutter/third_party/skia/include/core/SkM44.h"
@ -34,6 +35,7 @@ using DlRoundRect = impeller::RoundRect;
using DlRoundingRadii = impeller::RoundingRadii;
using DlMatrix = impeller::Matrix;
using DlQuad = impeller::Quad;
using DlRSTransform = impeller::RSTransform;
static_assert(sizeof(SkPoint) == sizeof(DlPoint));
static_assert(sizeof(SkIPoint) == sizeof(DlIPoint));

View File

@ -296,7 +296,7 @@ void DlSkCanvasAdapter::DrawImageNine(const sk_sp<DlImage>& image,
}
void DlSkCanvasAdapter::DrawAtlas(const sk_sp<DlImage>& atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,
@ -311,9 +311,9 @@ void DlSkCanvasAdapter::DrawAtlas(const sk_sp<DlImage>& atlas,
for (int i = 0; i < count; ++i) {
sk_colors.push_back(colors[i].argb());
}
delegate_->drawAtlas(sk_image.get(), xform, ToSkRects(tex), sk_colors.data(),
count, ToSk(mode), ToSk(sampling), ToSkRect(cullRect),
sk_paint());
delegate_->drawAtlas(sk_image.get(), ToSk(xform), ToSkRects(tex),
sk_colors.data(), count, ToSk(mode), ToSk(sampling),
ToSkRect(cullRect), sk_paint());
}
void DlSkCanvasAdapter::DrawDisplayList(const sk_sp<DisplayList> display_list,

View File

@ -134,7 +134,7 @@ class DlSkCanvasAdapter final : public virtual DlCanvas {
DlFilterMode filter,
const DlPaint* paint = nullptr) override;
void DrawAtlas(const sk_sp<DlImage>& atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -120,6 +120,10 @@ inline SkMatrix* ToSk(const DlMatrix* matrix, SkMatrix& scratch) {
extern sk_sp<SkVertices> ToSk(const std::shared_ptr<DlVertices>& vertices);
inline const SkRSXform* ToSk(const DlRSTransform* transforms) {
return reinterpret_cast<const SkRSXform*>(transforms);
}
} // namespace flutter
#endif // FLUTTER_DISPLAY_LIST_SKIA_DL_SK_CONVERSIONS_H_

View File

@ -321,5 +321,56 @@ TEST(DisplayListSkConversions, ToSkDitheringEnabledForGradients) {
}
}
TEST(DisplayListSkConversions, ToSkRSTransform) {
constexpr size_t kTransformCount = 4;
DlRSTransform transforms[kTransformCount] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({12.25f, 14.75f}, 10.0f, DlDegrees(30)),
DlRSTransform::Make({-10.4f, 8.25f}, 11.0f, DlDegrees(400)),
DlRSTransform::Make({1.0f, 3.0f}, 0.5f, DlDegrees(45)),
};
SkRSXform expected_transforms[kTransformCount] = {
SkRSXform::MakeFromRadians(1.0f, SkDegreesToRadians(0), //
0.0f, 0.0f, 0.0f, 0.0f),
SkRSXform::MakeFromRadians(10.0f, SkDegreesToRadians(30), //
12.25f, 14.75f, 0.0f, 0.0f),
SkRSXform::MakeFromRadians(11.0f, SkDegreesToRadians(400), //
-10.4f, 8.25f, 0.0f, 0.0f),
SkRSXform::MakeFromRadians(0.5f, SkDegreesToRadians(45), //
1.0f, 3.0f, 0.0f, 0.0f),
};
auto sk_transforms = ToSk(transforms);
for (size_t i = 0; i < kTransformCount; i++) {
// Comparing dl values to transformed copy values
// should match exactly because arrays were simply aliased
EXPECT_EQ(sk_transforms[i].fSCos, transforms[i].scaled_cos) << i;
EXPECT_EQ(sk_transforms[i].fSSin, transforms[i].scaled_sin) << i;
EXPECT_EQ(sk_transforms[i].fTx, transforms[i].translate_x) << i;
EXPECT_EQ(sk_transforms[i].fTy, transforms[i].translate_y) << i;
// Comparing dl values to computed Skia values
// should match closely, but not exactly due to differences in trig
EXPECT_FLOAT_EQ(sk_transforms[i].fSCos, expected_transforms[i].fSCos) << i;
EXPECT_FLOAT_EQ(sk_transforms[i].fSSin, expected_transforms[i].fSSin) << i;
EXPECT_EQ(sk_transforms[i].fTx, expected_transforms[i].fTx) << i;
EXPECT_EQ(sk_transforms[i].fTy, expected_transforms[i].fTy) << i;
// Comparing the results of transforming a sprite with Skia vs Impeller
SkPoint sk_quad[4];
expected_transforms[i].toQuad(20, 30, sk_quad);
DlQuad dl_quad;
transforms[i].GetQuad(20, 30, dl_quad);
// Skia order is UL,UR,LR,LL, Impeller order is UL,UR,LL,LR
EXPECT_FLOAT_EQ(sk_quad[0].fX, dl_quad[0].x) << i;
EXPECT_FLOAT_EQ(sk_quad[0].fY, dl_quad[0].y) << i;
EXPECT_FLOAT_EQ(sk_quad[1].fX, dl_quad[1].x) << i;
EXPECT_FLOAT_EQ(sk_quad[1].fY, dl_quad[1].y) << i;
EXPECT_FLOAT_EQ(sk_quad[2].fX, dl_quad[3].x) << i;
EXPECT_FLOAT_EQ(sk_quad[2].fY, dl_quad[3].y) << i;
EXPECT_FLOAT_EQ(sk_quad[3].fX, dl_quad[2].x) << i;
EXPECT_FLOAT_EQ(sk_quad[3].fY, dl_quad[2].y) << i;
}
}
} // namespace testing
} // namespace flutter

View File

@ -245,7 +245,7 @@ void DlSkCanvasDispatcher::drawImageNine(const sk_sp<DlImage> image,
ToSk(filter), safe_paint(render_with_attributes));
}
void DlSkCanvasDispatcher::drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,
@ -267,7 +267,7 @@ void DlSkCanvasDispatcher::drawAtlas(const sk_sp<DlImage> atlas,
sk_colors.push_back(colors[i].argb());
}
}
canvas_->drawAtlas(skia_atlas.get(), xform, ToSkRects(tex),
canvas_->drawAtlas(skia_atlas.get(), ToSk(xform), ToSkRects(tex),
sk_colors.empty() ? nullptr : sk_colors.data(), count,
ToSk(mode), ToSk(sampling), ToSkRect(cullRect),
safe_paint(render_with_attributes));

View File

@ -95,7 +95,7 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver,
DlFilterMode filter,
bool render_with_attributes) override;
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -3584,7 +3584,15 @@ TEST_F(DisplayListRendering, DrawImageNineLinear) {
}
TEST_F(DisplayListRendering, DrawAtlasNearest) {
const SkRSXform xform[] = {
const SkRSXform sk_xform[] = {
// clang-format off
{ 1.2f, 0.0f, kRenderLeft, kRenderTop},
{ 0.0f, 1.2f, kRenderRight, kRenderTop},
{-1.2f, 0.0f, kRenderRight, kRenderBottom},
{ 0.0f, -1.2f, kRenderLeft, kRenderBottom},
// clang-format on
};
const DlRSTransform dl_xform[] = {
// clang-format off
{ 1.2f, 0.0f, kRenderLeft, kRenderTop},
{ 0.0f, 1.2f, kRenderRight, kRenderTop},
@ -3617,20 +3625,28 @@ TEST_F(DisplayListRendering, DrawAtlasNearest) {
CanvasCompareTester::RenderAll( //
TestParameters(
[=](const SkRenderContext& ctx) {
ctx.canvas->drawAtlas(ctx.image.get(), xform, tex, sk_colors, 4,
ctx.canvas->drawAtlas(ctx.image.get(), sk_xform, tex, sk_colors, 4,
SkBlendMode::kSrcOver, sk_sampling, nullptr,
&ctx.paint);
},
[=](const DlRenderContext& ctx) {
ctx.canvas->DrawAtlas(ctx.image, xform, tex, dl_colors, 4,
DlBlendMode::kSrcOver, dl_sampling, nullptr,
&ctx.paint);
ctx.canvas->DrawAtlas(ctx.image, dl_xform, ToDlRects(tex),
dl_colors, 4, DlBlendMode::kSrcOver,
dl_sampling, nullptr, &ctx.paint);
},
kDrawAtlasWithPaintFlags));
}
TEST_F(DisplayListRendering, DrawAtlasNearestNoPaint) {
const SkRSXform xform[] = {
const SkRSXform sk_xform[] = {
// clang-format off
{ 1.2f, 0.0f, kRenderLeft, kRenderTop},
{ 0.0f, 1.2f, kRenderRight, kRenderTop},
{-1.2f, 0.0f, kRenderRight, kRenderBottom},
{ 0.0f, -1.2f, kRenderLeft, kRenderBottom},
// clang-format on
};
const DlRSTransform dl_xform[] = {
// clang-format off
{ 1.2f, 0.0f, kRenderLeft, kRenderTop},
{ 0.0f, 1.2f, kRenderRight, kRenderTop},
@ -3663,20 +3679,28 @@ TEST_F(DisplayListRendering, DrawAtlasNearestNoPaint) {
CanvasCompareTester::RenderAll( //
TestParameters(
[=](const SkRenderContext& ctx) {
ctx.canvas->drawAtlas(ctx.image.get(), xform, tex, sk_colors, 4,
ctx.canvas->drawAtlas(ctx.image.get(), sk_xform, tex, sk_colors, 4,
SkBlendMode::kSrcOver, sk_sampling, //
nullptr, nullptr);
},
[=](const DlRenderContext& ctx) {
ctx.canvas->DrawAtlas(ctx.image, xform, tex, dl_colors, 4,
DlBlendMode::kSrcOver, dl_sampling, //
nullptr, nullptr);
ctx.canvas->DrawAtlas(ctx.image, dl_xform, ToDlRects(tex),
dl_colors, 4, DlBlendMode::kSrcOver,
dl_sampling, nullptr, nullptr);
},
kDrawAtlasFlags));
}
TEST_F(DisplayListRendering, DrawAtlasLinear) {
const SkRSXform xform[] = {
const SkRSXform sk_xform[] = {
// clang-format off
{ 1.2f, 0.0f, kRenderLeft, kRenderTop},
{ 0.0f, 1.2f, kRenderRight, kRenderTop},
{-1.2f, 0.0f, kRenderRight, kRenderBottom},
{ 0.0f, -1.2f, kRenderLeft, kRenderBottom},
// clang-format on
};
const DlRSTransform dl_xform[] = {
// clang-format off
{ 1.2f, 0.0f, kRenderLeft, kRenderTop},
{ 0.0f, 1.2f, kRenderRight, kRenderTop},
@ -3709,14 +3733,14 @@ TEST_F(DisplayListRendering, DrawAtlasLinear) {
CanvasCompareTester::RenderAll( //
TestParameters(
[=](const SkRenderContext& ctx) {
ctx.canvas->drawAtlas(ctx.image.get(), xform, tex, sk_colors, 2,
ctx.canvas->drawAtlas(ctx.image.get(), sk_xform, tex, sk_colors, 2,
SkBlendMode::kSrcOver, sk_sampling, //
nullptr, &ctx.paint);
},
[=](const DlRenderContext& ctx) {
ctx.canvas->DrawAtlas(ctx.image, xform, tex, dl_colors, 2,
DlBlendMode::kSrcOver, dl_sampling, //
nullptr, &ctx.paint);
ctx.canvas->DrawAtlas(ctx.image, dl_xform, ToDlRects(tex),
dl_colors, 2, DlBlendMode::kSrcOver,
dl_sampling, nullptr, &ctx.paint);
},
kDrawAtlasWithPaintFlags));
}

View File

@ -943,7 +943,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
{
{1, 48 + 32 + 8, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
r.drawAtlas(kTestImage1, xforms, texs, nullptr, 2,
@ -952,7 +955,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 48 + 32 + 8, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
r.drawAtlas(kTestImage1, xforms, texs, nullptr, 2,
@ -960,7 +966,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 48 + 32 + 8, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{0, 1, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(45)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
r.drawAtlas(kTestImage1, xforms, texs, nullptr, 2,
@ -969,7 +978,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 48 + 32 + 8, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 25, 30, 30)};
r.drawAtlas(kTestImage1, xforms, texs, nullptr, 2,
@ -978,7 +990,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 48 + 32 + 8, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
r.drawAtlas(kTestImage1, xforms, texs, nullptr, 2,
@ -986,7 +1001,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 48 + 32 + 8, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
r.drawAtlas(kTestImage1, xforms, texs, nullptr, 2,
@ -995,7 +1013,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 64 + 32 + 8, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
static DlRect cull_rect = DlRect::MakeLTRB(0, 0, 200, 200);
@ -1005,7 +1026,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 128, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
static DlColor colors[] = {DlColor::kBlue(), DlColor::kGreen()};
@ -1015,7 +1039,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 144, 1,
[](DlOpReceiver& r) {
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
static DlColor colors[] = {DlColor::kBlue(), DlColor::kGreen()};
@ -1027,7 +1054,10 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
{1, 48 + 32 + 8, 1,
[](DlOpReceiver& r) {
auto dl_image = DlImage::Make(kTestSkImage);
static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
static DlRSTransform xforms[] = {
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(0)),
DlRSTransform::Make({0.0f, 0.0f}, 1.0f, DlDegrees(90)),
};
static DlRect texs[] = {DlRect::MakeLTRB(10, 10, 20, 20),
DlRect::MakeLTRB(20, 20, 30, 30)};
r.drawAtlas(dl_image, xforms, texs, nullptr, 2,

View File

@ -124,7 +124,7 @@ class IgnoreDrawDispatchHelper : public virtual DlOpReceiver {
DlFilterMode filter,
bool render_with_attributes) override {}
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -16,7 +16,6 @@
#include "impeller/entity/contents/content_context.h"
#include "impeller/geometry/color.h"
#include "impeller/geometry/scalar.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkRefCnt.h"
namespace impeller {
@ -25,34 +24,14 @@ namespace testing {
using namespace flutter;
namespace {
SkRSXform MakeTranslation(Scalar tx, Scalar ty) {
return SkRSXform::Make(1, 0, tx, ty);
RSTransform MakeTranslation(Scalar tx, Scalar ty) {
return RSTransform::Make({tx, ty}, 1, DlDegrees(0));
}
std::tuple<std::vector<SkRect>, std::vector<SkRSXform>, sk_sp<DlImageImpeller>>
std::tuple<std::vector<DlRect>, //
std::vector<RSTransform>, //
sk_sp<DlImageImpeller>>
CreateTestData(const AiksTest* test) {
// Draws the image as four squares stiched together.
auto atlas =
DlImageImpeller::Make(test->CreateTextureForFixture("bay_bridge.jpg"));
auto size = atlas->impeller_texture()->GetSize();
// Divide image into four quadrants.
Scalar half_width = size.width / 2;
Scalar half_height = size.height / 2;
std::vector<SkRect> texture_coordinates = {
SkRect::MakeLTRB(0, 0, half_width, half_height),
SkRect::MakeLTRB(half_width, 0, size.width, half_height),
SkRect::MakeLTRB(0, half_height, half_width, size.height),
SkRect::MakeLTRB(half_width, half_height, size.width, size.height)};
// Position quadrants adjacent to eachother.
std::vector<SkRSXform> transforms = {
MakeTranslation(0, 0), MakeTranslation(half_width, 0),
MakeTranslation(0, half_height),
MakeTranslation(half_width, half_height)};
return std::make_tuple(texture_coordinates, transforms, atlas);
}
std::tuple<std::vector<DlRect>, std::vector<SkRSXform>, sk_sp<DlImageImpeller>>
CreateDlTestData(const AiksTest* test) {
// Draws the image as four squares stiched together.
auto atlas =
DlImageImpeller::Make(test->CreateTextureForFixture("bay_bridge.jpg"));
@ -66,7 +45,7 @@ CreateDlTestData(const AiksTest* test) {
DlRect::MakeLTRB(0, half_height, half_width, size.height),
DlRect::MakeLTRB(half_width, half_height, size.width, size.height)};
// Position quadrants adjacent to eachother.
std::vector<SkRSXform> transforms = {
std::vector<RSTransform> transforms = {
MakeTranslation(0, 0), MakeTranslation(half_width, 0),
MakeTranslation(0, half_height),
MakeTranslation(half_width, half_height)};
@ -138,9 +117,9 @@ TEST_P(AiksTest, DrawAtlasWithOpacity) {
TEST_P(AiksTest, DrawAtlasNoColorFullSize) {
auto atlas = DlImageImpeller::Make(CreateTextureForFixture("bay_bridge.jpg"));
auto size = atlas->impeller_texture()->GetSize();
std::vector<SkRect> texture_coordinates = {
SkRect::MakeLTRB(0, 0, size.width, size.height)};
std::vector<SkRSXform> transforms = {MakeTranslation(0, 0)};
std::vector<DlRect> texture_coordinates = {
DlRect::MakeLTRB(0, 0, size.width, size.height)};
std::vector<RSTransform> transforms = {MakeTranslation(0, 0)};
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
@ -199,7 +178,7 @@ TEST_P(AiksTest, DrawAtlasPlusWideGamut) {
}
TEST_P(AiksTest, DlAtlasGeometryNoBlend) {
auto [texture_coordinates, transforms, atlas] = CreateDlTestData(this);
auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
DlAtlasGeometry geom(atlas->impeller_texture(), transforms.data(),
texture_coordinates.data(), nullptr, transforms.size(),
@ -217,7 +196,7 @@ TEST_P(AiksTest, DlAtlasGeometryNoBlend) {
}
TEST_P(AiksTest, DlAtlasGeometryBlend) {
auto [texture_coordinates, transforms, atlas] = CreateDlTestData(this);
auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
std::vector<DlColor> colors;
colors.reserve(texture_coordinates.size());
@ -241,7 +220,7 @@ TEST_P(AiksTest, DlAtlasGeometryBlend) {
}
TEST_P(AiksTest, DlAtlasGeometryColorButNoBlend) {
auto [texture_coordinates, transforms, atlas] = CreateDlTestData(this);
auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
std::vector<DlColor> colors;
colors.reserve(texture_coordinates.size());
@ -258,7 +237,7 @@ TEST_P(AiksTest, DlAtlasGeometryColorButNoBlend) {
}
TEST_P(AiksTest, DlAtlasGeometrySkip) {
auto [texture_coordinates, transforms, atlas] = CreateDlTestData(this);
auto [texture_coordinates, transforms, atlas] = CreateTestData(this);
std::vector<DlColor> colors;
colors.reserve(texture_coordinates.size());

View File

@ -26,7 +26,6 @@
#include "include/core/SkCanvas.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPath.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkRefCnt.h"
namespace impeller {

View File

@ -16,7 +16,7 @@
namespace impeller {
DlAtlasGeometry::DlAtlasGeometry(const std::shared_ptr<Texture>& atlas,
const SkRSXform* xform,
const RSTransform* xform,
const flutter::DlRect* tex,
const flutter::DlColor* colors,
size_t count,
@ -46,12 +46,10 @@ Rect DlAtlasGeometry::ComputeBoundingBox() const {
if (cull_rect_.has_value()) {
return cull_rect_.value();
}
Rect bounding_box = {};
Rect bounding_box;
for (size_t i = 0; i < count_; i++) {
auto matrix = skia_conversions::ToRSXForm(xform_[i]);
auto sample_rect = tex_[i];
auto bounds = Rect::MakeSize(sample_rect.GetSize()).TransformBounds(matrix);
bounding_box = bounds.Union(bounding_box);
auto bounds = xform_[i].GetBounds(tex_[i].GetSize());
bounding_box = Rect::Union(bounding_box, bounds);
}
cull_rect_ = bounding_box;
return bounding_box;
@ -84,10 +82,8 @@ VertexBuffer DlAtlasGeometry::CreateSimpleVertexBuffer(
auto texture_size = atlas_->GetSize();
for (auto i = 0u; i < count_; i++) {
flutter::DlRect sample_rect = tex_[i];
Matrix matrix = skia_conversions::ToRSXForm(xform_[i]);
auto points = sample_rect.GetPoints();
auto transformed_points = Rect::MakeSize(sample_rect.GetSize())
.GetTransformedPoints(matrix);
auto transformed_points = xform_[i].GetQuad(sample_rect.GetSize());
for (size_t j = 0; j < 6; j++) {
data[offset].position = transformed_points[indices[j]];
data[offset].texture_coords = points[indices[j]] / texture_size;
@ -119,10 +115,8 @@ VertexBuffer DlAtlasGeometry::CreateBlendVertexBuffer(
auto texture_size = atlas_->GetSize();
for (auto i = 0u; i < count_; i++) {
flutter::DlRect sample_rect = tex_[i];
Matrix matrix = skia_conversions::ToRSXForm(xform_[i]);
auto points = sample_rect.GetPoints();
auto transformed_points = Rect::MakeSize(sample_rect.GetSize())
.GetTransformedPoints(matrix);
auto transformed_points = xform_[i].GetQuad(sample_rect.GetSize());
for (size_t j = 0; j < 6; j++) {
data[offset].vertices = transformed_points[indices[j]];
data[offset].texture_coords = points[indices[j]] / texture_size;

View File

@ -10,7 +10,6 @@
#include "impeller/core/sampler_descriptor.h"
#include "impeller/entity/contents/atlas_contents.h"
#include "impeller/geometry/color.h"
#include "include/core/SkRSXform.h"
namespace impeller {
@ -18,7 +17,7 @@ namespace impeller {
class DlAtlasGeometry : public AtlasGeometry {
public:
DlAtlasGeometry(const std::shared_ptr<Texture>& atlas,
const SkRSXform* xform,
const RSTransform* xform,
const flutter::DlRect* tex,
const flutter::DlColor* colors,
size_t count,
@ -47,7 +46,7 @@ class DlAtlasGeometry : public AtlasGeometry {
private:
const std::shared_ptr<Texture> atlas_;
const SkRSXform* xform_;
const RSTransform* xform_;
const flutter::DlRect* tex_;
const flutter::DlColor* colors_;
size_t count_;

View File

@ -774,7 +774,7 @@ void DlDispatcherBase::drawImageNine(const sk_sp<flutter::DlImage> image,
// |flutter::DlOpReceiver|
void DlDispatcherBase::drawAtlas(const sk_sp<flutter::DlImage> atlas,
const SkRSXform xform[],
const RSTransform xform[],
const DlRect tex[],
const flutter::DlColor colors[],
int count,

View File

@ -209,7 +209,7 @@ class DlDispatcherBase : public flutter::DlOpReceiver {
// |flutter::DlOpReceiver|
void drawAtlas(const sk_sp<flutter::DlImage> atlas,
const SkRSXform xform[],
const RSTransform xform[],
const DlRect tex[],
const flutter::DlColor colors[],
int count,

View File

@ -97,17 +97,6 @@ Color ToColor(const flutter::DlColor& color) {
};
}
Matrix ToRSXForm(const SkRSXform& form) {
// clang-format off
return Matrix{
form.fSCos, form.fSSin, 0, 0,
-form.fSSin, form.fSCos, 0, 0,
0, 0, 1, 0,
form.fTx, form.fTy, 0, 1
};
// clang-format on
}
std::optional<impeller::PixelFormat> ToPixelFormat(SkColorType type) {
switch (type) {
case kRGBA_8888_SkColorType:

View File

@ -19,7 +19,6 @@
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRSXform.h"
#include "third_party/skia/include/core/SkTextBlob.h"
namespace impeller {
@ -49,8 +48,6 @@ Size ToSize(const SkPoint& point);
Color ToColor(const flutter::DlColor& color);
Matrix ToRSXForm(const SkRSXform& form);
std::optional<impeller::PixelFormat> ToPixelFormat(SkColorType type);
impeller::SamplerDescriptor ToSamplerDescriptor(

View File

@ -33,6 +33,8 @@ impeller_component("geometry") {
"round_rect.h",
"rounding_radii.cc",
"rounding_radii.h",
"rstransform.cc",
"rstransform.h",
"saturated_math.h",
"scalar.h",
"separated_vector.cc",
@ -76,6 +78,7 @@ impeller_component("geometry_unittests") {
"path_unittests.cc",
"rect_unittests.cc",
"round_rect_unittests.cc",
"rstransform_unittests.cc",
"saturated_math_unittests.cc",
"size_unittests.cc",
"trig_unittests.cc",

View File

@ -602,7 +602,6 @@ struct Matrix {
// clang-format on
}
private:
static constexpr Vector2 CosSin(Radians radians) {
// The precision of a float around 1.0 is much lower than it is
// around 0.0, so we end up with cases on quadrant rotations where

View File

@ -703,15 +703,13 @@ struct TRect {
saturated::Cast<U, Type>(round(r.GetBottom())));
}
[[nodiscard]] constexpr static std::optional<TRect> Union(
const TRect& a,
const std::optional<TRect> b) {
[[nodiscard]] constexpr static TRect Union(const TRect& a,
const std::optional<TRect> b) {
return b.has_value() ? a.Union(b.value()) : a;
}
[[nodiscard]] constexpr static std::optional<TRect> Union(
const std::optional<TRect> a,
const TRect& b) {
[[nodiscard]] constexpr static TRect Union(const std::optional<TRect> a,
const TRect& b) {
return a.has_value() ? a->Union(b) : b;
}

View File

@ -1457,16 +1457,14 @@ TEST(RectTest, OptRectUnion) {
auto test1 = [](const Rect& r) {
// Rect, NullOpt
EXPECT_TRUE(Rect::Union(r, std::nullopt).has_value());
EXPECT_EQ(Rect::Union(r, std::nullopt).value(), r);
EXPECT_EQ(Rect::Union(r, std::nullopt), r);
// OptRect, NullOpt
EXPECT_TRUE(Rect::Union(std::optional(r), std::nullopt).has_value());
EXPECT_EQ(Rect::Union(std::optional(r), std::nullopt).value(), r);
// NullOpt, Rect
EXPECT_TRUE(Rect::Union(std::nullopt, r).has_value());
EXPECT_EQ(Rect::Union(std::nullopt, r).value(), r);
EXPECT_EQ(Rect::Union(std::nullopt, r), r);
// NullOpt, OptRect
EXPECT_TRUE(Rect::Union(std::nullopt, std::optional(r)).has_value());
@ -1481,12 +1479,10 @@ TEST(RectTest, OptRectUnion) {
ASSERT_EQ(a.Union(b), u);
// Rect, OptRect
EXPECT_TRUE(Rect::Union(a, std::optional(b)).has_value());
EXPECT_EQ(Rect::Union(a, std::optional(b)).value(), u);
EXPECT_EQ(Rect::Union(a, std::optional(b)), u);
// OptRect, Rect
EXPECT_TRUE(Rect::Union(std::optional(a), b).has_value());
EXPECT_EQ(Rect::Union(std::optional(a), b).value(), u);
EXPECT_EQ(Rect::Union(std::optional(a), b), u);
// OptRect, OptRect
EXPECT_TRUE(Rect::Union(std::optional(a), std::optional(b)).has_value());
@ -1581,16 +1577,14 @@ TEST(RectTest, OptIRectUnion) {
auto test1 = [](const IRect& r) {
// Rect, NullOpt
EXPECT_TRUE(IRect::Union(r, std::nullopt).has_value());
EXPECT_EQ(IRect::Union(r, std::nullopt).value(), r);
EXPECT_EQ(IRect::Union(r, std::nullopt), r);
// OptRect, NullOpt
EXPECT_TRUE(IRect::Union(std::optional(r), std::nullopt).has_value());
EXPECT_EQ(IRect::Union(std::optional(r), std::nullopt).value(), r);
// NullOpt, Rect
EXPECT_TRUE(IRect::Union(std::nullopt, r).has_value());
EXPECT_EQ(IRect::Union(std::nullopt, r).value(), r);
EXPECT_EQ(IRect::Union(std::nullopt, r), r);
// NullOpt, OptRect
EXPECT_TRUE(IRect::Union(std::nullopt, std::optional(r)).has_value());
@ -1605,12 +1599,10 @@ TEST(RectTest, OptIRectUnion) {
ASSERT_EQ(a.Union(b), u);
// Rect, OptRect
EXPECT_TRUE(IRect::Union(a, std::optional(b)).has_value());
EXPECT_EQ(IRect::Union(a, std::optional(b)).value(), u);
EXPECT_EQ(IRect::Union(a, std::optional(b)), u);
// OptRect, Rect
EXPECT_TRUE(IRect::Union(std::optional(a), b).has_value());
EXPECT_EQ(IRect::Union(std::optional(a), b).value(), u);
EXPECT_EQ(IRect::Union(std::optional(a), b), u);
// OptRect, OptRect
EXPECT_TRUE(IRect::Union(std::optional(a), std::optional(b)).has_value());

View File

@ -0,0 +1,57 @@
// Copyright 2013 The Flutter 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 "flutter/impeller/geometry/rstransform.h"
#include "flutter/impeller/geometry/matrix.h"
namespace impeller {
bool RSTransform::IsAxisAligned() const {
return scaled_cos == 0.0f || scaled_sin == 0.0f;
}
Matrix RSTransform::GetMatrix() const {
// clang-format off
return Matrix::MakeRow(scaled_cos, -scaled_sin, 0.0f, translate_x,
scaled_sin, scaled_cos, 0.0f, translate_y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
// clang-format on
}
void RSTransform::GetQuad(Scalar width, Scalar height, Quad& quad) const {
Point origin = {translate_x, translate_y};
Point dx = width * Point{scaled_cos, scaled_sin};
Point dy = height * Point{-scaled_sin, scaled_cos};
quad = {
// Ordered in the same Z pattern as Rect::GetPoints()
origin,
origin + dx,
origin + dy,
origin + dx + dy,
};
}
Quad RSTransform::GetQuad(Scalar width, Scalar height) const {
Quad quad;
GetQuad(width, height, quad);
return quad;
}
Quad RSTransform::GetQuad(Size size) const {
Quad quad;
GetQuad(size.width, size.height, quad);
return quad;
}
std::optional<Rect> RSTransform::GetBounds(Scalar width, Scalar height) const {
return Rect::MakePointBounds(GetQuad(width, height));
}
std::optional<Rect> RSTransform::GetBounds(Size size) const {
return Rect::MakePointBounds(GetQuad(size));
}
} // namespace impeller

View File

@ -0,0 +1,89 @@
// Copyright 2013 The Flutter 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 FLUTTER_IMPELLER_GEOMETRY_RSTRANSFORM_H_
#define FLUTTER_IMPELLER_GEOMETRY_RSTRANSFORM_H_
#include "flutter/impeller/geometry/point.h"
#include "flutter/impeller/geometry/rect.h"
#include "flutter/impeller/geometry/scalar.h"
#include "flutter/impeller/geometry/size.h"
namespace impeller {
/// A utility struct that stores a simple transform composed of a translation,
/// a rotation, and a uniform scale.
///
/// This transform is used by drawAtlas to transform the sprites.
/// This structure mirrors the Flutter RSTransform class.
struct RSTransform {
constexpr RSTransform()
: scaled_cos(1.0f),
scaled_sin(0.0f),
translate_x(0.0f),
translate_y(0.0f) {}
constexpr RSTransform(Scalar scaled_cos,
Scalar scaled_sin,
Scalar translate_x,
Scalar translate_y)
: scaled_cos(scaled_cos),
scaled_sin(scaled_sin),
translate_x(translate_x),
translate_y(translate_y) {}
/// Constructs an RSTransform from the indicated origin, uniform scale,
/// and radians rotation.
static constexpr RSTransform Make(Point origin,
Scalar scale,
Radians radians) {
auto scaled_cos_sin = Matrix::CosSin(radians) * scale;
return {scaled_cos_sin.x, scaled_cos_sin.y, origin.x, origin.y};
}
Scalar scaled_cos;
Scalar scaled_sin;
Scalar translate_x;
Scalar translate_y;
/// Returns true iff the resulting transformed quad will be aligned with
/// the axes, even if rotated by a quadrant rotation.
bool IsAxisAligned() const;
Matrix GetMatrix() const;
/// Returns the 4 corner points of the transformed quad for a sub-image
/// of the indicated size in the same order as Rect::GetPoints.
///
/// The order is UpperLeft, UpperRight, LowerLeft, LowerRight
void GetQuad(Scalar width, Scalar height, Quad& quad) const;
Quad GetQuad(Scalar width, Scalar height) const;
Quad GetQuad(Size size) const;
/// Returns the bounds of the 4 corner points of the transformed quad
/// for a sub-image of the indicated size.
std::optional<Rect> GetBounds(Scalar width, Scalar height) const;
std::optional<Rect> GetBounds(Size size) const;
};
} // namespace impeller
namespace std {
inline std::ostream& operator<<(std::ostream& out,
const impeller::RSTransform& rst) {
// clang-format off
out << "("
<< "scos: " << rst.scaled_cos << ", "
<< "ssin: " << rst.scaled_sin << ", "
<< "origin: (" << rst.translate_x << ", "
<< rst.translate_y << ")"
<< ")";
// clang-format on
return out;
}
} // namespace std
#endif // FLUTTER_IMPELLER_GEOMETRY_RSTRANSFORM_H_

View File

@ -0,0 +1,61 @@
// Copyright 2013 The Flutter 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 "gtest/gtest.h"
#include "flutter/impeller/geometry/rstransform.h"
#include "flutter/impeller/geometry/geometry_asserts.h"
namespace impeller {
namespace testing {
TEST(RSTransformTest, Construction) {
RSTransform transform = RSTransform::Make({10.0f, 12.0f}, 2.0f, Degrees(90));
EXPECT_EQ(transform.scaled_cos, 0.0f);
EXPECT_EQ(transform.scaled_sin, 2.0f);
EXPECT_EQ(transform.translate_x, 10.0f);
EXPECT_EQ(transform.translate_y, 12.0f);
EXPECT_EQ(transform.GetBounds(20.0f, 30.0f),
// relative corners are at
// 0, 0
// 0, 40
// -60, 0
// -60, 40
// then add 10, 12 to all values
Rect::MakeLTRB(10 + -2 * 30, 12 + 0, 10 + 0, 12 + 40));
}
TEST(RSTransformTest, CompareToMatrix) {
for (int tx = 0; tx <= 100; tx += 10) {
for (int ty = 0; ty <= 100; ty += 10) {
Point origin(tx, ty);
for (int scale = 1; scale <= 20; scale += 5) {
// Overshoot a full circle by 30 degrees
for (int degrees = 0; degrees <= 390; degrees += 45) {
auto matrix = Matrix::MakeTranslation(origin) *
Matrix::MakeRotationZ(Degrees(degrees)) *
Matrix::MakeScale(Vector2(scale, scale));
auto rst = RSTransform::Make(origin, scale, Degrees(degrees));
EXPECT_MATRIX_NEAR(rst.GetMatrix(), matrix);
for (int w = 10; w <= 100; w += 10) {
for (int h = 10; h <= 100; h += 10) {
Quad q = rst.GetQuad(w, h);
auto points = Rect::MakeWH(w, h).GetTransformedPoints(matrix);
for (int i = 0; i < 4; i++) {
EXPECT_NEAR(q[i].x, points[i].x, kEhCloseEnough);
EXPECT_NEAR(q[i].y, points[i].y, kEhCloseEnough);
}
}
}
}
}
}
}
}
} // namespace testing
} // namespace impeller

View File

@ -1747,7 +1747,7 @@ class RRect {
///
/// Used by [Canvas.drawAtlas]. This is a more efficient way to represent these
/// simple transformations than a full matrix.
// Modeled after Skia's SkRSXform.
// Modeled after Impeller's RSTransform.
class RSTransform {
/// Creates an RSTransform.
///

View File

@ -581,8 +581,8 @@ Dart_Handle Canvas::drawAtlas(Dart_Handle paint_objects,
return ToDart(error.value());
}
static_assert(sizeof(SkRSXform) == sizeof(float) * 4,
"SkRSXform doesn't use floats.");
static_assert(sizeof(DlRSTransform) == sizeof(float) * 4,
"DlRSTransform doesn't use floats.");
static_assert(sizeof(DlRect) == sizeof(float) * 4,
"DlRect doesn't use floats.");
@ -605,7 +605,7 @@ Dart_Handle Canvas::drawAtlas(Dart_Handle paint_objects,
const DlPaint* opt_paint =
paint.paint(dl_paint, kDrawAtlasWithPaintFlags, DlTileMode::kClamp);
builder()->DrawAtlas(
dl_image, reinterpret_cast<const SkRSXform*>(transforms.data()),
dl_image, reinterpret_cast<const DlRSTransform*>(transforms.data()),
reinterpret_cast<const DlRect*>(rects.data()), dl_color.data(),
rects.num_elements() / 4, // DlRect have four floats.
blend_mode, sampling, reinterpret_cast<const DlRect*>(cull_rect.data()),

View File

@ -110,7 +110,7 @@ void DlOpSpy::drawImageNine(const sk_sp<DlImage> image,
did_draw_ = true;
}
void DlOpSpy::drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -84,7 +84,7 @@ class DlOpSpy final : public virtual DlOpReceiver,
DlFilterMode filter,
bool render_with_attributes) override;
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -7,7 +7,6 @@
#include "flutter/display_list/testing/dl_test_snippets.h"
#include "flutter/shell/common/dl_op_spy.h"
#include "flutter/testing/testing.h"
#include "third_party/skia/include/core/SkRSXform.h"
namespace flutter {
namespace testing {
@ -456,7 +455,9 @@ TEST(DlOpSpy, Images) {
{ // DrawAtlas
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
const SkRSXform xform[] = {SkRSXform::Make(1, 0, 0, 0)};
const DlRSTransform xform[] = {
DlRSTransform::Make({0, 0}, 1.0f, DlDegrees(0)),
};
const DlRect tex[] = {DlRect::MakeXYWH(10, 10, 10, 10)};
DlRect cull_rect = DlRect::MakeWH(5, 5);
builder.DrawAtlas(kTestImage1, xform, tex, nullptr, 1, DlBlendMode::kSrc,

View File

@ -205,14 +205,6 @@ extern std::ostream& operator<<(std::ostream& os, const DlPath& path) {
<< ")";
}
static std::ostream& operator<<(std::ostream& os, const SkRSXform& xform) {
return os << "SkRSXform("
<< "scos: " << xform.fSCos << ", "
<< "ssin: " << xform.fSSin << ", "
<< "tx: " << xform.fTx << ", "
<< "ty: " << xform.fTy << ")";
}
std::ostream& operator<<(std::ostream& os, const DlCanvas::ClipOp& op) {
switch (op) {
case DlCanvas::ClipOp::kDifference: return os << "ClipOp::kDifference";
@ -912,7 +904,7 @@ void DisplayListStreamDispatcher::drawImageNine(const sk_sp<DlImage> image,
<< ");" << std::endl;
}
void DisplayListStreamDispatcher::drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,

View File

@ -177,7 +177,7 @@ class DisplayListStreamDispatcher final : public DlOpReceiver {
DlFilterMode filter,
bool render_with_attributes) override;
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,
@ -500,7 +500,7 @@ class DisplayListGeneralReceiver : public DlOpReceiver {
}
}
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const DlRSTransform xform[],
const DlRect tex[],
const DlColor colors[],
int count,