lots of sRGB and F16 blits
authorreed <reed@google.com>
Mon, 22 Feb 2016 14:42:31 +0000 (06:42 -0800)
committerCommit bot <commit-bot@chromium.org>
Mon, 22 Feb 2016 14:42:31 +0000 (06:42 -0800)
- generalize F16 xfermode procs
- spriteblits for F16 and sRGB
- saveLayer now respects colortype and profiletype

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1686013002

Review URL: https://codereview.chromium.org/1685203002

17 files changed:
bench/XferU64Bench.cpp
gm/xfermodes.cpp
gyp/core.gypi
include/core/SkXfermode.h
src/core/SkBitmapProcShader.h
src/core/SkBlitter.cpp
src/core/SkBlitter_PM4f.cpp
src/core/SkBlitter_Sprite.cpp
src/core/SkCanvas.cpp
src/core/SkSpanProcs.cpp [new file with mode: 0644]
src/core/SkSpanProcs.h [new file with mode: 0644]
src/core/SkSpriteBlitter.h
src/core/SkSpriteBlitter4f.cpp [new file with mode: 0644]
src/core/SkSpriteBlitter_ARGB32.cpp
src/core/SkXfermode.cpp
src/core/SkXfermode4f.cpp
src/core/SkXfermodeU64.cpp

index 75aada6..b81bc1e 100644 (file)
 // Benchmark that draws non-AA rects or AA text with an SkXfermode::Mode.
 class XferU64Bench : public Benchmark {
 public:
-    XferU64Bench(bool doN, uint32_t flags)
+    XferU64Bench(SkXfermode::Mode mode, const char name[], bool doN, uint32_t flags)
         : fDoN(doN)
         , fFlags(flags & ~USE_AA)
     {
-        SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
+        fXfer.reset(SkXfermode::Create(mode));
 
         fProc1 = SkXfermode::GetU64Proc1(mode, fFlags);
         fProcN = SkXfermode::GetU64ProcN(mode, fFlags);
-        fName.printf("xferu64_%s_%c_%s_%s",
+        fName.printf("xferu64_%s_%s_%c_%s_%s",
+                     name,
                      (flags & USE_AA) ? "aa" : "bw",
                      fDoN ? 'N' : '1',
                      (flags & SkXfermode::kSrcIsOpaque_U64Flag) ? "opaque" : "alpha",
@@ -50,7 +51,7 @@ protected:
     const char* onGetName() override { return fName.c_str(); }
 
     void onDraw(int loops, SkCanvas*) override {
-        const SkXfermode::U64State state{ nullptr, fFlags };
+        const SkXfermode::U64State state{ fXfer, fFlags };
 
         for (int i = 0; i < loops * INNER_LOOPS; ++i) {
             if (fDoN) {
@@ -62,6 +63,7 @@ protected:
     }
 
 private:
+    SkAutoTUnref<SkXfermode> fXfer;
     SkString             fName;
     SkXfermode::U64Proc1 fProc1;
     SkXfermode::U64ProcN fProcN;
@@ -84,24 +86,25 @@ private:
 #define F10 (SkXfermode::kDstIsFloat16_U64Flag)
 #define F11 (SkXfermode::kDstIsFloat16_U64Flag | SkXfermode::kSrcIsOpaque_U64Flag)
 
-#if 0
-DEF_BENCH( return new XferU64Bench(true,  F10 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(true,  F11 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(true,  F10); )
-DEF_BENCH( return new XferU64Bench(true,  F11); )
-
-DEF_BENCH( return new XferU64Bench(true,  F00 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(true,  F01 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(true,  F00); )
-DEF_BENCH( return new XferU64Bench(true,  F01); )
-#endif
-
-DEF_BENCH( return new XferU64Bench(false, F10 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(false, F11 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(false, F10); )
-DEF_BENCH( return new XferU64Bench(false, F11); )
-
-DEF_BENCH( return new XferU64Bench(false, F00 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(false, F01 | USE_AA); )
-DEF_BENCH( return new XferU64Bench(false, F00); )
-DEF_BENCH( return new XferU64Bench(false, F01); )
+#define MODE    SkXfermode::kSrcOver_Mode
+#define NAME    "srcover"
+
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F10 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F11 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F10); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F11); )
+
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F00 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F01 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F00); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, true,  F01); )
+
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F10 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F11 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F10); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F11); )
+
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F00 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F01 | USE_AA); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F00); )
+DEF_BENCH( return new XferU64Bench(MODE, NAME, false, F01); )
index 9e87c04..c109574 100644 (file)
@@ -293,55 +293,17 @@ private:
 DEF_GM( return new XfermodesGM; )
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-#include "SkNx.h"
-
-static SkPMColor apply_proc(SkPMColor src, SkPMColor dst, SkXfermodeProc4f proc, float src_alpha) {
-    SkPM4f src4 = SkPM4f::FromPMColor(src);
-    for (int i = 0; i < 4; ++i) {
-        src4.fVec[i] *= src_alpha;
-    }
-    SkPM4f dst4 = SkPM4f::FromPMColor(dst);
-    SkPM4f res4 = proc(src4, dst4);
-    SkPMColor res;
-    SkNx_cast<uint8_t>(Sk4f::Load(res4.fVec) * Sk4f(255) + Sk4f(0.5f)).store(&res);
-    return res;
-}
-
-static bool apply_mode(const SkPixmap& res, const SkPixmap& src, const SkPixmap& dst,
-                       SkXfermode* xfer, float src_alpha) {
-    SkXfermode::Mode mode;
-    if (!xfer) {
-        mode = SkXfermode::kSrcOver_Mode;
-    } else if (!xfer->asMode(&mode)) {
-        return false;
-    }
-    SkXfermodeProc4f proc = SkXfermode::GetProc4f(mode);
-    if (!proc) {
-        return false;
-    }
-
-    for (int y = 0; y < res.height(); ++y) {
-        for (int x = 0; x < res.width(); ++x) {
-            *res.writable_addr32(x, y) = apply_proc(*src.addr32(x, y), *dst.addr32(x, y),
-                                                    proc, src_alpha);
-        }
-    }
-    return true;
-}
 
 void draw_mode(const SkBitmap& srcB, const SkBitmap& dstB,
                SkCanvas* canvas, SkXfermode* mode, SkScalar x, SkScalar y, float src_alpha) {
-    SkBitmap resB;
-    resB.allocN32Pixels(64, 64);
+    canvas->saveLayer(SkRect::MakeXYWH(x, y, 64, 64), nullptr);
+    canvas->drawBitmap(srcB, x, y, nullptr);
 
-    SkPixmap srcPM, dstPM, resPM;
-    srcB.peekPixels(&srcPM);
-    dstB.peekPixels(&dstPM);
-    resB.peekPixels(&resPM);
+    SkPaint paint;
+    paint.setXfermode(mode);
+    canvas->drawBitmap(dstB, x, y, &paint);
 
-    if (apply_mode(resPM, srcPM, dstPM, mode, src_alpha)) {
-        canvas->drawBitmap(resB, x, y, nullptr);
-    }
+    canvas->restore();
 }
 
 DEF_SIMPLE_GM(xfermodes_proc4f, canvas, 1000, 1000) {
index 748eb99..67ed9d7 100644 (file)
         '<(skia_src_path)/core/SkSharedMutex.cpp',
         '<(skia_src_path)/core/SkSharedMutex.h',
         '<(skia_src_path)/core/SkSmallAllocator.h',
+        '<(skia_src_path)/core/SkSpanProcs.cpp',
         '<(skia_src_path)/core/SkSpecialImage.cpp',
         '<(skia_src_path)/core/SkSpecialImage.h',
         '<(skia_src_path)/core/SkSpecialSurface.cpp',
         '<(skia_src_path)/core/SkSpriteBlitter_RGB16.cpp',
         '<(skia_src_path)/core/SkSpriteBlitter.h',
         '<(skia_src_path)/core/SkSpriteBlitterTemplate.h',
+        '<(skia_src_path)/core/SkSpriteBlitter4f.cpp',
         '<(skia_src_path)/core/SkStream.cpp',
         '<(skia_src_path)/core/SkStreamPriv.h',
         '<(skia_src_path)/core/SkString.cpp',
index 94f49fa..04630e7 100644 (file)
@@ -149,6 +149,8 @@ public:
     static SkXfermodeProc GetProc(Mode mode);
     static SkXfermodeProc4f GetProc4f(Mode);
 
+    virtual SkXfermodeProc4f getProc4f() const;
+
     /**
      *  If the specified mode can be represented by a pair of Coeff, then return
      *  true and set (if not NULL) the corresponding coeffs. If the mode is
index 0346eff..2134927 100644 (file)
@@ -75,7 +75,7 @@ private:
 // an Sk3DBlitter in SkDraw.cpp
 // Note that some contexts may contain other contexts (e.g. for compose shaders), but we've not
 // yet found a situation where the size below isn't big enough.
-typedef SkSmallAllocator<3, 1160> SkTBlitterAllocator;
+typedef SkSmallAllocator<3, 1280> SkTBlitterAllocator;
 
 // If alloc is non-nullptr, it will be used to allocate the returned SkShader, and MUST outlive
 // the SkShader.
index a3dbe5e..41d6071 100644 (file)
@@ -938,11 +938,13 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device,
             break;
 
         default:
-            SkDEBUGFAIL("unsupported device config");
-            blitter = allocator->createT<SkNullBlitter>();
             break;
     }
 
+    if (!blitter) {
+        blitter = allocator->createT<SkNullBlitter>();
+    }
+
     if (shader3D) {
         SkBlitter* innerBlitter = blitter;
         // innerBlitter was allocated by allocator, which will delete it.
index 3c13baa..24fc4a3 100644 (file)
@@ -356,7 +356,7 @@ struct State64 : SkXfermode::U64State {
         }
         
         SkXfermode::Mode mode;
-        if (SkXfermode::AsMode(fXfer, &mode)) {
+        if (!SkXfermode::AsMode(fXfer, &mode)) {
             mode = SkXfermode::kSrcOver_Mode;
         }
         fProc1 = SkXfermode::GetU64Proc1(mode, fFlags);
index 605fa43..27cbd61 100644 (file)
@@ -60,7 +60,14 @@ SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint,
             blitter = SkSpriteBlitter::ChooseD16(source, paint, allocator);
             break;
         case kN32_SkColorType:
-            blitter = SkSpriteBlitter::ChooseD32(source, paint, allocator);
+            if (dst.info().isSRGB()) {
+                blitter = SkSpriteBlitter::ChooseS32(source, paint, allocator);
+            } else {
+                blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator);
+            }
+            break;
+        case kRGBA_F16_SkColorType:
+            blitter = SkSpriteBlitter::ChooseF16(source, paint, allocator);
             break;
         default:
             blitter = nullptr;
index 653f4b1..f0476cd 100644 (file)
@@ -1167,6 +1167,22 @@ static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filt
     c.drawBitmap(srcBM, x, y, &p);
 }
 
+static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
+                                   const SkPaint* paint) {
+    // need to force L32 for now if we have an image filter. Once filters support other colortypes
+    // e.g. sRGB or F16, we can remove this check
+    const bool hasImageFilter = paint && paint->getImageFilter();
+
+    SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+    if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
+        // force to L32
+        return SkImageInfo::MakeN32(w, h, alphaType);
+    } else {
+        // keep the same characteristics as the prev
+        return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType());
+    }
+}
+
 void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
     const SkRect* bounds = rec.fBounds;
     const SkPaint* paint = rec.fPaint;
@@ -1202,8 +1218,6 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
             geo = kUnknown_SkPixelGeometry;
         }
     }
-    SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
-                        isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
 
     SkBaseDevice* device = this->getTopDevice();
     if (nullptr == device) {
@@ -1211,6 +1225,9 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
         return;
     }
 
+    SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
+                                       paint);
+
     bool forceSpriteOnRestore = false;
     {
         const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
@@ -2200,8 +2217,20 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S
         paint = lazy.init();
     }
 
-    const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
-                                                          *paint);
+    bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
+                                                    *paint);
+    if (drawAsSprite && paint->getImageFilter()) {
+        SkBitmap bitmap;
+        if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
+            drawAsSprite = false;
+        } else{
+            // Until imagefilters are updated, they cannot handle any src type but N32...
+            if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
+                drawAsSprite = false;
+            }
+        }
+    }
+
     LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
 
     while (iter.next()) {
@@ -2277,8 +2306,15 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons
         bounds = &storage;
     }
 
-    const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
-                                                                    bitmap.height(), *paint);
+    bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
+                                                              *paint);
+    if (drawAsSprite && paint->getImageFilter()) {
+        // Until imagefilters are updated, they cannot handle any src type but N32...
+        if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
+            drawAsSprite = false;
+        }
+    }
+
     LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
 
     while (iter.next()) {
diff --git a/src/core/SkSpanProcs.cpp b/src/core/SkSpanProcs.cpp
new file mode 100644 (file)
index 0000000..efbce97
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorFilter.h"
+#include "SkHalf.h"
+#include "SkNx.h"
+#include "SkPaint.h"
+#include "SkPixmap.h"
+#include "SkPM4f.h"
+#include "SkPM4fPriv.h"
+#include "SkSpanProcs.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void load_l32(const SkPixmap& src, int x, int y, SkPM4f span[], int count) {
+    SkASSERT(count > 0);
+    const uint32_t* addr = src.addr32(x, y);
+    SkASSERT(src.addr32(x + count - 1, y));
+
+    for (int i = 0; i < count; ++i) {
+        (SkNx_cast<float>(Sk4b::Load(&addr[i])) * Sk4f(1.0f/255)).store(span[i].fVec);
+    }
+}
+
+static void load_s32(const SkPixmap& src, int x, int y, SkPM4f span[], int count) {
+    SkASSERT(count > 0);
+    const uint32_t* addr = src.addr32(x, y);
+    SkASSERT(src.addr32(x + count - 1, y));
+
+    for (int i = 0; i < count; ++i) {
+        srgb_to_linear(SkNx_cast<float>(Sk4b::Load(&addr[i])) * Sk4f(1.0f/255)).store(span[i].fVec);
+    }
+}
+
+static void load_f16(const SkPixmap& src, int x, int y, SkPM4f span[], int count) {
+    SkASSERT(count > 0);
+    const uint64_t* addr = src.addr64(x, y);
+    SkASSERT(src.addr64(x + count - 1, y));
+
+    for (int i = 0; i < count; ++i) {
+        SkHalfToFloat_01(addr[i]).store(span[i].fVec);
+    }
+}
+
+SkLoadSpanProc SkLoadSpanProc_Choose(const SkImageInfo& info) {
+    switch (info.colorType()) {
+        case kN32_SkColorType:
+            return info.isSRGB() ? load_s32 : load_l32;
+        case kRGBA_F16_SkColorType:
+            return load_f16;
+        default:
+            return nullptr;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void noop_filterspan(const SkPaint& paint, SkPM4f[], int) {
+    SkASSERT(!paint.getColorFilter());
+    SkASSERT(0xFF == paint.getAlpha());
+}
+
+static void alpha_filterspan(const SkPaint& paint, SkPM4f span[], int count) {
+    SkASSERT(!paint.getColorFilter());
+    SkASSERT(0xFF != paint.getAlpha());
+    const Sk4f scale = Sk4f(paint.getAlpha() * (1.0f/255));
+    for (int i = 0; i < count; ++i) {
+        (Sk4f::Load(span[i].fVec) * scale).store(span[i].fVec);
+    }
+}
+
+static void colorfilter_filterspan(const SkPaint& paint, SkPM4f span[], int count) {
+    SkASSERT(paint.getColorFilter());
+    SkASSERT(0xFF == paint.getAlpha());
+    paint.getColorFilter()->filterSpan4f(span, count, span);
+}
+
+static void colorfilter_alpha_filterspan(const SkPaint& paint, SkPM4f span[], int count) {
+    SkASSERT(paint.getColorFilter());
+    SkASSERT(0xFF != paint.getAlpha());
+    alpha_filterspan(paint, span, count);
+    paint.getColorFilter()->filterSpan4f(span, count, span);
+}
+
+SkFilterSpanProc SkFilterSpanProc_Choose(const SkPaint& paint) {
+    if (paint.getColorFilter()) {
+        return 0xFF == paint.getAlpha() ? colorfilter_filterspan : colorfilter_alpha_filterspan;
+    } else {
+        return 0xFF == paint.getAlpha() ? noop_filterspan : alpha_filterspan;
+    }
+}
diff --git a/src/core/SkSpanProcs.h b/src/core/SkSpanProcs.h
new file mode 100644 (file)
index 0000000..891f4e2
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSpanProcs_DEFINED
+#define SkSpanProcs_DEFINED
+
+#include "SkPM4f.h"
+
+struct SkImageInfo;
+class SkPaint;
+class SkPixmap;
+struct SkPM4f;
+
+typedef void (*SkLoadSpanProc)(const SkPixmap&, int x, int y, SkPM4f span[], int count);
+typedef void (*SkFilterSpanProc)(const SkPaint& paint, SkPM4f span[], int count);
+
+SkLoadSpanProc SkLoadSpanProc_Choose(const SkImageInfo&);
+SkFilterSpanProc SkFilterSpanProc_Choose(const SkPaint&);
+
+#endif
index 536c892..62c50c8 100644 (file)
@@ -29,7 +29,9 @@ public:
 #endif
 
     static SkSpriteBlitter* ChooseD16(const SkPixmap& source, const SkPaint&, SkTBlitterAllocator*);
-    static SkSpriteBlitter* ChooseD32(const SkPixmap& source, const SkPaint&, SkTBlitterAllocator*);
+    static SkSpriteBlitter* ChooseL32(const SkPixmap& source, const SkPaint&, SkTBlitterAllocator*);
+    static SkSpriteBlitter* ChooseS32(const SkPixmap& source, const SkPaint&, SkTBlitterAllocator*);
+    static SkSpriteBlitter* ChooseF16(const SkPixmap& source, const SkPaint&, SkTBlitterAllocator*);
 
 protected:
     SkPixmap        fDst;
diff --git a/src/core/SkSpriteBlitter4f.cpp b/src/core/SkSpriteBlitter4f.cpp
new file mode 100644 (file)
index 0000000..fc4b480
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSpriteBlitter.h"
+#include "SkSpanProcs.h"
+#include "SkTemplates.h"
+#include "SkXfermode.h"
+
+class Sprite_4f : public SkSpriteBlitter {
+public:
+    Sprite_4f(const SkPixmap& src, const SkPaint& paint) : INHERITED(src) {
+        fLoader = SkLoadSpanProc_Choose(src.info());
+        fFilter = SkFilterSpanProc_Choose(paint);
+        fBuffer.reset(src.width());
+    }
+    
+protected:
+    SkLoadSpanProc          fLoader;
+    SkFilterSpanProc        fFilter;
+    SkAutoTMalloc<SkPM4f>   fBuffer;
+    
+private:
+    typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static SkXfermode::Mode get_mode(const SkXfermode* xfer) {
+    SkXfermode::Mode mode;
+    if (!SkXfermode::AsMode(xfer, &mode)) {
+        mode = SkXfermode::kSrcOver_Mode;
+    }
+    return mode;
+}
+
+class Sprite_F16 : public Sprite_4f {
+public:
+    Sprite_F16(const SkPixmap& src, const SkPaint& paint) : INHERITED(src, paint) {
+        fState = { paint.getXfermode(), SkXfermode::kDstIsFloat16_U64Flag };
+        if (src.isOpaque()) {
+            fState.fFlags |= SkXfermode::kSrcIsOpaque_U64Flag;
+        }
+        fXfer = SkXfermode::GetU64ProcN(get_mode(fState.fXfer), fState.fFlags);
+    }
+
+    void blitRect(int x, int y, int width, int height) override {
+        SkASSERT(width > 0 && height > 0);
+        uint64_t* SK_RESTRICT dst = fDst.writable_addr64(x, y);
+        size_t dstRB = fDst.rowBytes();
+
+        for (int bottom = y + height; y < bottom; ++y) {
+            fLoader(fSource, x - fLeft, y - fTop, fBuffer, width);
+            fFilter(*fPaint, fBuffer, width);
+            fXfer(fState, dst, fBuffer, width, nullptr);
+            dst = (uint64_t* SK_RESTRICT)((char*)dst + dstRB);
+        }
+    }
+    
+private:
+    SkXfermode::U64State    fState;
+    SkXfermode::U64ProcN    fXfer;
+
+    typedef Sprite_4f INHERITED;
+};
+
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseF16(const SkPixmap& source, const SkPaint& paint,
+                                            SkTBlitterAllocator* allocator) {
+    SkASSERT(allocator != nullptr);
+
+    if (paint.getMaskFilter() != nullptr) {
+        return nullptr;
+    }
+
+    switch (source.colorType()) {
+        case kN32_SkColorType:
+        case kRGBA_F16_SkColorType:
+            return allocator->createT<Sprite_F16>(source, paint);
+        default:
+            return nullptr;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class Sprite_sRGB : public Sprite_4f {
+public:
+    Sprite_sRGB(const SkPixmap& src, const SkPaint& paint) : INHERITED(src, paint) {
+        fState = { paint.getXfermode(), SkXfermode::kDstIsSRGB_PM4fFlag };
+        if (src.isOpaque()) {
+            fState.fFlags |= SkXfermode::kSrcIsOpaque_PM4fFlag;
+        }
+        fXfer = SkXfermode::GetPM4fProcN(get_mode(fState.fXfer), fState.fFlags);
+    }
+    
+    void blitRect(int x, int y, int width, int height) override {
+        SkASSERT(width > 0 && height > 0);
+        uint32_t* SK_RESTRICT dst = fDst.writable_addr32(x, y);
+        size_t dstRB = fDst.rowBytes();
+        
+        for (int bottom = y + height; y < bottom; ++y) {
+            fLoader(fSource, x - fLeft, y - fTop, fBuffer, width);
+            fFilter(*fPaint, fBuffer, width);
+            fXfer(fState, dst, fBuffer, width, nullptr);
+            dst = (uint32_t* SK_RESTRICT)((char*)dst + dstRB);
+        }
+    }
+    
+protected:
+    SkXfermode::PM4fState   fState;
+    SkXfermode::PM4fProcN   fXfer;
+    
+private:
+    typedef Sprite_4f INHERITED;
+};
+
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseS32(const SkPixmap& source, const SkPaint& paint,
+                                            SkTBlitterAllocator* allocator) {
+    SkASSERT(allocator != nullptr);
+    
+    if (paint.getMaskFilter() != nullptr) {
+        return nullptr;
+    }
+    
+    switch (source.colorType()) {
+        case kN32_SkColorType:
+        case kRGBA_F16_SkColorType:
+            return allocator->createT<Sprite_sRGB>(source, paint);
+        default:
+            return nullptr;
+    }
+}
index 9c47844..9388599 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkSpriteBlitter.h"
 #include "SkBlitRow.h"
 #include "SkColorFilter.h"
@@ -256,8 +254,8 @@ public:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkPixmap& source, const SkPaint& paint,
-        SkTBlitterAllocator* allocator) {
+SkSpriteBlitter* SkSpriteBlitter::ChooseL32(const SkPixmap& source, const SkPaint& paint,
+                                            SkTBlitterAllocator* allocator) {
     SkASSERT(allocator != nullptr);
 
     if (paint.getMaskFilter() != nullptr) {
index 7f89327..7002e9a 100644 (file)
@@ -95,7 +95,7 @@ static Sk4f overlay_4f(const Sk4f& s, const Sk4f& d) {
     Sk4f two = Sk4f(2);
     Sk4f rc = (two * d <= da).thenElse(two * s * d,
                                        sa * da - two * (da - d) * (sa - s));
-    return s + d - s * da + color_alpha(rc - d * sa, 0);
+    return pin_1(s + d - s * da + color_alpha(rc - d * sa, 0));
 }
 
 static Sk4f hardlight_4f(const Sk4f& s, const Sk4f& d) {
@@ -1343,6 +1343,15 @@ SkXfermodeProc4f SkXfermode::GetProc4f(Mode mode) {
     return proc;
 }
 
+static SkPM4f missing_proc4f(const SkPM4f& src, const SkPM4f& dst) {
+    return src;
+}
+
+SkXfermodeProc4f SkXfermode::getProc4f() const {
+    Mode mode;
+    return this->asMode(&mode) ? GetProc4f(mode) : missing_proc4f;
+}
+
 bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
     SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
 
index 1f6c674..8aa2ce2 100644 (file)
@@ -45,76 +45,53 @@ static Sk4f linear_unit_to_srgb_255f(const Sk4f& l4) {
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-static Sk4f scale_255_round(const SkPM4f& pm4) {
-    return Sk4f::Load(pm4.fVec) * Sk4f(255) + Sk4f(0.5f);
-}
-
-static void pm4f_to_linear_32(SkPMColor dst[], const SkPM4f src[], int count) {
-    while (count >= 4) {
-        src[0].assertIsUnit();
-        src[1].assertIsUnit();
-        src[2].assertIsUnit();
-        src[3].assertIsUnit();
-        Sk4f_ToBytes((uint8_t*)dst,
-                     scale_255_round(src[0]), scale_255_round(src[1]),
-                     scale_255_round(src[2]), scale_255_round(src[3]));
-        src += 4;
-        dst += 4;
-        count -= 4;
-    }
-    for (int i = 0; i < count; ++i) {
-        src[i].assertIsUnit();
-        SkNx_cast<uint8_t>(scale_255_round(src[i])).store((uint8_t*)&dst[i]);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// These are our fallback impl for the SkPM4f procs...
-//
-// They just convert the src color(s) into a linear SkPMColor value(s), and then
-// call the existing virtual xfer32. This clear throws away data (converting floats to bytes)
-// in the src, and ignores the sRGB flag, but should draw about the same as if the caller
-// had passed in SkPMColor values directly.
-//
-
-void xfer_pm4_proc_1(const SkXfermode::PM4fState& state, uint32_t dst[], const SkPM4f& src,
-                     int count, const SkAlpha aa[]) {
-    uint32_t pm;
-    pm4f_to_linear_32(&pm, &src, 1);
-
-    const int N = 128;
-    SkPMColor tmp[N];
-    sk_memset32(tmp, pm, SkMin32(count, N));
-    while (count > 0) {
-        const int n = SkMin32(count, N);
-        state.fXfer->xfer32(dst, tmp, n, aa);
-
-        dst += n;
-        if (aa) {
-            aa += n;
+template <DstType D> void general_1(const SkXfermode::PM4fState& state, uint32_t dst[],
+                                    const SkPM4f& src, int count, const SkAlpha aa[]) {
+    SkXfermodeProc4f proc = state.fXfer->getProc4f();
+    SkPM4f d;
+    if (aa) {
+        for (int i = 0; i < count; ++i) {
+            Sk4f d4 = load_dst<D>(dst[i]);
+            d4.store(d.fVec);
+            Sk4f r4 = Sk4f::Load(proc(src, d).fVec);
+            dst[i] = store_dst<D>(lerp(r4, d4, aa[i]));
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            load_dst<D>(dst[i]).store(d.fVec);
+            Sk4f r4 = Sk4f::Load(proc(src, d).fVec);
+            dst[i] = store_dst<D>(r4);
         }
-        count -= n;
     }
 }
 
-void xfer_pm4_proc_n(const SkXfermode::PM4fState& state, uint32_t dst[], const SkPM4f src[],
-                     int count, const SkAlpha aa[]) {
-    const int N = 128;
-    SkPMColor tmp[N];
-    while (count > 0) {
-        const int n = SkMin32(count, N);
-        pm4f_to_linear_32(tmp, src, n);
-        state.fXfer->xfer32(dst, tmp, n, aa);
-
-        src += n;
-        dst += n;
-        if (aa) {
-            aa += n;
+template <DstType D> void general_n(const SkXfermode::PM4fState& state, uint32_t dst[],
+                                    const SkPM4f src[], int count, const SkAlpha aa[]) {
+    SkXfermodeProc4f proc = state.fXfer->getProc4f();
+    SkPM4f d;
+    if (aa) {
+        for (int i = 0; i < count; ++i) {
+            Sk4f d4 = load_dst<D>(dst[i]);
+            d4.store(d.fVec);
+            Sk4f r4 = Sk4f::Load(proc(src[i], d).fVec);
+            dst[i] = store_dst<D>(lerp(r4, d4, aa[i]));
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            load_dst<D>(dst[i]).store(d.fVec);
+            Sk4f r4 = Sk4f::Load(proc(src[i], d).fVec);
+            dst[i] = store_dst<D>(r4);
         }
-        count -= n;
     }
 }
 
+const XferProcPair gProcs_General[] = {
+    { general_1<kLinear_Dst>,   general_n<kLinear_Dst>  },   // linear   alpha
+    { general_1<kLinear_Dst>,   general_n<kLinear_Dst>  },   // linear   opaque
+    { general_1<kSRGB_Dst>,     general_n<kSRGB_Dst>    },   // srgb     alpha
+    { general_1<kSRGB_Dst>,     general_n<kSRGB_Dst>    },   // srgb     opaque
+};
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 static void clear_linear_n(const SkXfermode::PM4fState& state, uint32_t dst[], const SkPM4f[],
@@ -402,7 +379,7 @@ static XferProcPair find_procs(SkXfermode::Mode mode, uint32_t flags) {
         default:
             break;
     }
-    return { xfer_pm4_proc_1, xfer_pm4_proc_n };
+    return gProcs_General[flags];
 }
 
 SkXfermode::PM4fProc1 SkXfermode::GetPM4fProc1(Mode mode, uint32_t flags) {
@@ -414,13 +391,19 @@ SkXfermode::PM4fProcN SkXfermode::GetPM4fProcN(Mode mode, uint32_t flags) {
 }
 
 SkXfermode::PM4fProc1 SkXfermode::getPM4fProc1(uint32_t flags) const {
+    SkASSERT(0 == (flags & ~3));
+    flags &= 3;
+
     Mode mode;
-    return this->asMode(&mode) ? GetPM4fProc1(mode, flags) : xfer_pm4_proc_1;
+    return this->asMode(&mode) ? GetPM4fProc1(mode, flags) : gProcs_General[flags].fP1;
 }
 
 SkXfermode::PM4fProcN SkXfermode::getPM4fProcN(uint32_t flags) const {
+    SkASSERT(0 == (flags & ~3));
+    flags &= 3;
+
     Mode mode;
-    return this->asMode(&mode) ? GetPM4fProcN(mode, flags) : xfer_pm4_proc_n;
+    return this->asMode(&mode) ? GetPM4fProcN(mode, flags) : gProcs_General[flags].fPN;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
index 5b26228..5d260c1 100644 (file)
@@ -30,10 +30,14 @@ static Sk4f lerp_by_coverage(const Sk4f& src, const Sk4f& dst, uint8_t srcCovera
     return dst + (src - dst) * Sk4f(srcCoverage * (1/255.0f));
 }
 
-template <DstType D> Sk4f unit_to_dst_bias(const Sk4f& x4) {
+template <DstType D> Sk4f unit_to_bias(const Sk4f& x4) {
     return (D == kU16_Dst) ? x4 * Sk4f(65535) : x4;
 }
 
+template <DstType D> Sk4f bias_to_unit(const Sk4f& x4) {
+    return (D == kU16_Dst) ? x4 * Sk4f(1.0f/65535) : x4;
+}
+
 // returns value already biased by 65535
 static Sk4f load_from_u16(uint64_t value) {
     return SkNx_cast<float>(Sk4h::Load(&value));
@@ -68,9 +72,58 @@ static inline Sk4f pm_to_rgba_order(const Sk4f& x) {
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+template <DstType D> void xfer_u64_1(const SkXfermode::U64State& state, uint64_t dst[],
+                                     const SkPM4f& src, int count, const SkAlpha aa[]) {
+    SkXfermodeProc4f proc = state.fXfer->getProc4f();
+    SkPM4f d;
+    if (aa) {
+        for (int i = 0; i < count; ++i) {
+            Sk4f d4 = bias_to_unit<D>(load_from_dst<D>(dst[i]));
+            d4.store(d.fVec);
+            Sk4f r4 = unit_to_bias<D>(Sk4f::Load(proc(src, d).fVec));
+            dst[i] = store_to_dst<D>(lerp_by_coverage(r4, d4, aa[i]));
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            bias_to_unit<D>(load_from_dst<D>(dst[i])).store(d.fVec);
+            Sk4f r4 = unit_to_bias<D>(Sk4f::Load(proc(src, d).fVec));
+            dst[i] = store_to_dst<D>(r4);
+        }
+    }
+}
+
+template <DstType D> void xfer_u64_n(const SkXfermode::U64State& state, uint64_t dst[],
+                                     const SkPM4f src[], int count, const SkAlpha aa[]) {
+    SkXfermodeProc4f proc = state.fXfer->getProc4f();
+    SkPM4f d;
+    if (aa) {
+        for (int i = 0; i < count; ++i) {
+            Sk4f d4 = bias_to_unit<D>(load_from_dst<D>(dst[i]));
+            d4.store(d.fVec);
+            Sk4f r4 = unit_to_bias<D>(Sk4f::Load(proc(src[i], d).fVec));
+            dst[i] = store_to_dst<D>(lerp_by_coverage(r4, d4, aa[i]));
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            bias_to_unit<D>(load_from_dst<D>(dst[i])).store(d.fVec);
+            Sk4f r4 = unit_to_bias<D>(Sk4f::Load(proc(src[i], d).fVec));
+            dst[i] = store_to_dst<D>(r4);
+        }
+    }
+}
+
+const U64ProcPair gU64Procs_General[] = {
+    { xfer_u64_1<kU16_Dst>, xfer_u64_n<kU16_Dst> },   // U16     alpha
+    { xfer_u64_1<kU16_Dst>, xfer_u64_n<kU16_Dst> },   // U16     opaque
+    { xfer_u64_1<kF16_Dst>, xfer_u64_n<kF16_Dst> },   // F16     alpha
+    { xfer_u64_1<kF16_Dst>, xfer_u64_n<kF16_Dst> },   // F16     opaque
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 template <DstType D> void src_1(const SkXfermode::U64State& state, uint64_t dst[],
                                 const SkPM4f& src, int count, const SkAlpha aa[]) {
-    const Sk4f s4 = pm_to_rgba_order(unit_to_dst_bias<D>(Sk4f::Load(src.fVec)));
+    const Sk4f s4 = pm_to_rgba_order(unit_to_bias<D>(Sk4f::Load(src.fVec)));
     if (aa) {
         for (int i = 0; i < count; ++i) {
             const Sk4f d4 = load_from_dst<D>(dst[i]);
@@ -85,13 +138,13 @@ template <DstType D> void src_n(const SkXfermode::U64State& state, uint64_t dst[
                                 const SkPM4f src[], int count, const SkAlpha aa[]) {
     if (aa) {
         for (int i = 0; i < count; ++i) {
-            const Sk4f s4 = pm_to_rgba_order(unit_to_dst_bias<D>(Sk4f::Load(src[i].fVec)));
+            const Sk4f s4 = pm_to_rgba_order(unit_to_bias<D>(Sk4f::Load(src[i].fVec)));
             const Sk4f d4 = load_from_dst<D>(dst[i]);
             dst[i] = store_to_dst<D>(lerp_by_coverage(s4, d4, aa[i]));
         }
     } else {
         for (int i = 0; i < count; ++i) {
-            const Sk4f s4 = pm_to_rgba_order(unit_to_dst_bias<D>(Sk4f::Load(src[i].fVec)));
+            const Sk4f s4 = pm_to_rgba_order(unit_to_bias<D>(Sk4f::Load(src[i].fVec)));
             dst[i] = store_to_dst<D>(s4);
         }
     }
@@ -110,7 +163,7 @@ template <DstType D> void srcover_1(const SkXfermode::U64State& state, uint64_t
                                     const SkPM4f& src, int count, const SkAlpha aa[]) {
     const Sk4f s4 = pm_to_rgba_order(Sk4f::Load(src.fVec));
     const Sk4f dst_scale = Sk4f(1 - get_alpha(s4));
-    const Sk4f s4bias = unit_to_dst_bias<D>(s4);
+    const Sk4f s4bias = unit_to_bias<D>(s4);
     for (int i = 0; i < count; ++i) {
         const Sk4f d4bias = load_from_dst<D>(dst[i]);
         const Sk4f r4bias = s4bias + d4bias * dst_scale;
@@ -127,7 +180,7 @@ template <DstType D> void srcover_n(const SkXfermode::U64State& state, uint64_t
     for (int i = 0; i < count; ++i) {
         const Sk4f s4 = pm_to_rgba_order(Sk4f::Load(src[i].fVec));
         const Sk4f dst_scale = Sk4f(1 - get_alpha(s4));
-        const Sk4f s4bias = unit_to_dst_bias<D>(s4);
+        const Sk4f s4bias = unit_to_bias<D>(s4);
         const Sk4f d4bias = load_from_dst<D>(dst[i]);
         const Sk4f r4bias = s4bias + d4bias * dst_scale;
         if (aa) {
@@ -157,7 +210,7 @@ static U64ProcPair find_procs(SkXfermode::Mode mode, uint32_t flags) {
         default:
             break;
     }
-    return { nullptr, nullptr };
+    return gU64Procs_General[flags];
 }
 
 SkXfermode::U64Proc1 SkXfermode::GetU64Proc1(Mode mode, uint32_t flags) {