aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/flippity.cpp255
-rw-r--r--gn/gm.gni1
-rw-r--r--include/core/SkMatrix.h24
3 files changed, 280 insertions, 0 deletions
diff --git a/gm/flippity.cpp b/gm/flippity.cpp
new file mode 100644
index 0000000000..62b6d4327c
--- /dev/null
+++ b/gm/flippity.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "sk_tool_utils.h"
+
+#include "SkSurface.h"
+
+#if SK_SUPPORT_GPU
+
+#include "SkImage_Gpu.h"
+
+static const int kNumMatrices = 6;
+static const int kImageSize = 128;
+static const int kLabelSize = 32;
+static const int kNumLabels = 4;
+
+static const int kCellSize = kImageSize+2*kLabelSize;
+static const int kGMWidth = kNumMatrices*kCellSize;
+static const int kGMHeight = 2*kCellSize;
+
+static const SkPoint kPoints[kNumLabels] = {
+ { 0, kImageSize }, // kLL
+ { kImageSize, kImageSize }, // kLR
+ { 0, 0 }, // kUL
+ { kImageSize, 0 }, // kUR
+};
+
+static const SkMatrix kUVMatrices[kNumMatrices] = {
+ SkMatrix::MakeAll( 0, -1, 1,
+ -1, 0, 1,
+ 0, 0, 1),
+ SkMatrix::MakeAll( 1, 0, 0,
+ 0, -1, 1,
+ 0, 0, 1),
+ // flip x
+ SkMatrix::MakeAll(-1, 0, 1,
+ 0, 1, 0,
+ 0, 0, 1),
+ SkMatrix::MakeAll( 0, 1, 0,
+ -1, 0, 1,
+ 0, 0, 1),
+ // flip both x & y == rotate 180
+ SkMatrix::MakeAll(-1, 0, 1,
+ 0, -1, 1,
+ 0, 0, 1),
+ // identity
+ SkMatrix::MakeAll(1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1)
+};
+
+// Create a fixed size text label like "LL" or "LR".
+static sk_sp<SkImage> make_text_image(GrContext* context, const char* text, SkColor color) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(32);
+ paint.setColor(color);
+
+ SkRect bounds;
+ paint.measureText(text, strlen(text), &bounds);
+ const SkMatrix mat = SkMatrix::MakeRectToRect(bounds, SkRect::MakeWH(kLabelSize, kLabelSize),
+ SkMatrix::kFill_ScaleToFit);
+
+ const SkImageInfo ii = SkImageInfo::MakeN32Premul(kLabelSize, kLabelSize);
+ sk_sp<SkSurface> surf = SkSurface::MakeRaster(ii);
+
+ SkCanvas* canvas = surf->getCanvas();
+
+ canvas->clear(SK_ColorWHITE);
+ canvas->concat(mat);
+ canvas->drawText(text, strlen(text), 0, 0, paint);
+
+ sk_sp<SkImage> image = surf->makeImageSnapshot();
+
+ return image->makeTextureImage(context, nullptr);
+}
+
+static SkColor swap_red_and_blue(SkColor c) {
+ return SkColorSetRGB(SkColorGetB(c), SkColorGetG(c), SkColorGetR(c));
+}
+
+// Create an image with each corner marked w/ "LL", "LR", etc., with the origin either bottom-left
+// or top-left.
+static sk_sp<SkImage> make_image(GrContext* context, const SkTArray<sk_sp<SkImage>>& labels,
+ bool bottomLeftOrigin) {
+ SkASSERT(kNumLabels == labels.count());
+
+ SkImageInfo ii = SkImageInfo::Make(kImageSize, kImageSize,
+ kN32_SkColorType, kOpaque_SkAlphaType);
+ SkBitmap bm;
+ bm.allocPixels(ii);
+ SkCanvas canvas(bm);
+
+ canvas.clear(SK_ColorWHITE);
+ for (int i = 0; i < kNumLabels; ++i) {
+ canvas.drawImage(labels[i],
+ 0.0 != kPoints[i].fX ? kPoints[i].fX-kLabelSize : 0,
+ 0.0 != kPoints[i].fY ? kPoints[i].fY-kLabelSize : 0);
+ }
+
+ GrSurfaceDesc desc;
+ desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ desc.fWidth = kImageSize;
+ desc.fHeight = kImageSize;
+ desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+ if (bottomLeftOrigin) {
+ // Note that Ganesh will flip the data when it is uploaded
+ desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+ }
+
+ if (kN32_SkColorType == kBGRA_8888_SkColorType) {
+ // We're playing a game here and uploading N32 data into an RGB dest. We might have
+ // to swap red & blue to compensate.
+ for (int y = 0; y < bm.height(); ++y) {
+ uint32_t *sl = bm.getAddr32(0, y);
+ for (int x = 0; x < bm.width(); ++x) {
+ sl[x] = swap_red_and_blue(sl[x]);
+ }
+ }
+ }
+
+ sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+ desc, SkBudgeted::kYes,
+ bm.getPixels(), bm.rowBytes());
+
+ return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, kOpaque_SkAlphaType,
+ std::move(proxy), nullptr, SkBudgeted::kYes);
+}
+
+// Here we're converting from a matrix that is intended for UVs to a matrix that is intended
+// for rect geometry used for a drawImage call. They are, in some sense, inverses of each
+// other but we also need a scale to map from the [0..1] uv range to the actual size of
+// image.
+static bool UVMatToGeomMatForImage(SkMatrix* geomMat, const SkMatrix& uvMat) {
+ SkMatrix tmp = uvMat;
+ tmp.preScale(1.0f/kImageSize, 1.0f/kImageSize);
+ tmp.postScale(kImageSize, kImageSize);
+
+ return tmp.invert(geomMat);
+}
+
+// This GM exercises drawImage with a set of matrices that use an unusual amount of flips and
+// rotates.
+class FlippityGM : public skiagm::GM {
+public:
+ FlippityGM() {
+ this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
+ }
+
+protected:
+
+ SkString onShortName() override {
+ return SkString("flippity");
+ }
+
+ SkISize onISize() override {
+ return SkISize::Make(kGMWidth, kGMHeight);
+ }
+
+ // Draw the reference image and the four corner labels in the matrix's coordinate space
+ void drawImageWithMatrixAndLabels(SkCanvas* canvas, SkImage* image, int matIndex) {
+ SkMatrix imageGeomMat;
+ SkAssertResult(UVMatToGeomMatForImage(&imageGeomMat, kUVMatrices[matIndex]));
+
+ canvas->save();
+ canvas->concat(imageGeomMat);
+
+ // draw the reference image
+ canvas->drawImage(image, 0, 0);
+
+ // draw the labels
+ for (int i = 0; i < kNumLabels; ++i) {
+ canvas->drawImage(fLabels[i],
+ 0.0f == kPoints[i].fX ? -kLabelSize : kPoints[i].fX,
+ 0.0f == kPoints[i].fY ? -kLabelSize : kPoints[i].fY);
+ }
+ canvas->restore();
+ }
+
+ void makeLabels(GrContext* context) {
+ static const char* kLabelText[kNumLabels] = { "LL", "LR", "UL", "UR" };
+
+ static const SkColor kLabelColors[kNumLabels] = {
+ SK_ColorRED,
+ SK_ColorGREEN,
+ SK_ColorBLUE,
+ SK_ColorCYAN
+ };
+
+ SkASSERT(!fLabels.count());
+ for (int i = 0; i < kNumLabels; ++i) {
+ fLabels.push_back(make_text_image(context, kLabelText[i], kLabelColors[i]));
+ }
+ SkASSERT(kNumLabels == fLabels.count());
+ }
+
+ void onDraw(SkCanvas* canvas) override {
+ GrContext* context = canvas->getGrContext();
+ if (!context) {
+ skiagm::GM::DrawGpuOnlyMessage(canvas);
+ return;
+ }
+
+ this->makeLabels(context);
+
+ // Top row gets TL image
+ {
+ sk_sp<SkImage> referenceImage = make_image(context, fLabels, false);
+
+ canvas->save();
+ canvas->translate(kLabelSize, kLabelSize);
+
+ for (int i = 0; i < kNumMatrices; ++i) {
+ this->drawImageWithMatrixAndLabels(canvas, referenceImage.get(), i);
+ canvas->translate(kImageSize+2*kLabelSize, 0);
+ }
+ canvas->restore();
+ }
+
+ // Bottom row gets BL image
+ {
+ sk_sp<SkImage> referenceImage = make_image(context, fLabels, true);
+
+ canvas->save();
+ canvas->translate(kLabelSize, kImageSize+3*kLabelSize);
+
+ for (int i = 0; i < kNumMatrices; ++i) {
+ this->drawImageWithMatrixAndLabels(canvas, referenceImage.get(), i);
+ canvas->translate(kCellSize, 0);
+ }
+ canvas->restore();
+ }
+
+ // separator grid
+ canvas->drawLine(0, kCellSize, kGMWidth, kCellSize, SkPaint());
+ for (int i = 0; i < kNumMatrices; ++i) {
+ canvas->drawLine(i * kCellSize, 0, i * kCellSize, kGMHeight, SkPaint());
+ }
+ }
+
+private:
+ SkTArray<sk_sp<SkImage>> fLabels;
+
+ typedef GM INHERITED;
+};
+
+DEF_GM(return new FlippityGM;)
+
+#endif
diff --git a/gn/gm.gni b/gn/gm.gni
index 3ccf8a8948..441c041e6f 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -131,6 +131,7 @@ gm_sources = [
"$_gm/filterbug.cpp",
"$_gm/filterfastbounds.cpp",
"$_gm/filterindiabox.cpp",
+ "$_gm/flippity.cpp",
"$_gm/fontcache.cpp",
"$_gm/fontmgr.cpp",
"$_gm/fontscaler.cpp",
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 58bcff8fc4..e88232834b 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -79,6 +79,30 @@ public:
return m;
}
+ /** Sets matrix to be:
+
+ | scaleX skewX transX |
+ | skewY scaleY transY |
+ | pers0 pers1 pers2 |
+
+ @param scaleX horizontal scale factor to store
+ @param skewX horizontal skew factor to store
+ @param transX horizontal translation to store
+ @param skewY vertical skew factor to store
+ @param scaleY vertical scale factor to store
+ @param transY vertical translation to store
+ @param pers0 input x perspective factor to store
+ @param pers1 input y perspective factor to store
+ @param pers2 perspective scale factor to store
+ */
+ static SkMatrix SK_WARN_UNUSED_RESULT MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
+ SkScalar skewY, SkScalar scaleY, SkScalar transY,
+ SkScalar pers0, SkScalar pers1, SkScalar pers2) {
+ SkMatrix m;
+ m.setAll(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2);
+ return m;
+ }
+
/** \enum SkMatrix::TypeMask
Enum of bit fields for mask returned by getType().
Used to identify the complexity of SkMatrix, to optimize performance.