Added --deepColor option to SampleApp, triggers creation of a ten-bit/channel
buffer. (Only on Windows at the moment). Uses new effect to do the final
gamma adjustment

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

Review-Url: https://codereview.chromium.org/1919993002
diff --git a/example/HelloWorld.cpp b/example/HelloWorld.cpp
index 892dce1..c2dd7d8 100644
--- a/example/HelloWorld.cpp
+++ b/example/HelloWorld.cpp
@@ -62,7 +62,7 @@
     this->setVisibleP(true);
     this->setClipToBounds(false);
 
-    bool result = attach(kNativeGL_BackEndType, 0 /*msaa*/, &fAttachmentInfo);
+    bool result = attach(kNativeGL_BackEndType, 0 /*msaa*/, false, &fAttachmentInfo);
     if (false == result) {
         SkDebugf("Not possible to create backend.\n");
         release();
diff --git a/experimental/SkV8Example/SkV8Example.cpp b/experimental/SkV8Example/SkV8Example.cpp
index fe205d7..bb448ba 100644
--- a/experimental/SkV8Example/SkV8Example.cpp
+++ b/experimental/SkV8Example/SkV8Example.cpp
@@ -74,7 +74,7 @@
     if (FLAGS_gpu) {
         SkOSWindow::AttachmentInfo attachmentInfo;
         bool result = this->attach(
-                SkOSWindow::kNativeGL_BackEndType, 0, &attachmentInfo);
+                SkOSWindow::kNativeGL_BackEndType, 0, false, &attachmentInfo);
         if (!result) {
             printf("Failed to attach.");
             exit(1);
diff --git a/experimental/iOSSampleApp/SkSampleUIView.mm b/experimental/iOSSampleApp/SkSampleUIView.mm
index bd6d533..8bf7c90 100644
--- a/experimental/iOSSampleApp/SkSampleUIView.mm
+++ b/experimental/iOSSampleApp/SkSampleUIView.mm
@@ -47,7 +47,7 @@
 #endif
     }
     
-    void setUpBackend(SampleWindow* win, int msaaSampleCount) override {
+    void setUpBackend(SampleWindow* win, int msaaSampleCount, bool deepColor) override {
         SkASSERT(SkOSWindow::kNone_BackEndType == fBackend);
         
         fBackend = SkOSWindow::kNone_BackEndType;
@@ -65,7 +65,7 @@
                 break;
         }
         SkOSWindow::AttachmentInfo info;
-        bool result = win->attach(fBackend, msaaSampleCount, &info);
+        bool result = win->attach(fBackend, msaaSampleCount, false, &info);
         if (!result) {
             SkDebugf("Failed to initialize GL");
             return;
@@ -145,7 +145,7 @@
         if (NULL != fCurContext) {
             SkOSWindow::AttachmentInfo info;
 
-            win->attach(fBackend, fMSAASampleCount, &info);
+            win->attach(fBackend, fMSAASampleCount, false, &info);
             
             glBindFramebuffer(GL_FRAMEBUFFER, fLayerFBO);
             GrBackendRenderTargetDesc desc;
@@ -177,7 +177,11 @@
         return NULL;
 #endif
     }
-    
+
+    int getColorBits() override {
+        return 24;
+    }
+
     bool isUsingGL() const { return SkOSWindow::kNone_BackEndType != fBackend; }
     
 private:
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 5919e24..f0bb333 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -285,6 +285,8 @@
       '<(skia_src_path)/gpu/effects/GrDistanceFieldGeoProc.h',
       '<(skia_src_path)/gpu/effects/GrDitherEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrDitherEffect.h',
+      '<(skia_src_path)/gpu/effects/GrGammaEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrGammaEffect.h',
       '<(skia_src_path)/gpu/effects/GrMatrixConvolutionEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrMatrixConvolutionEffect.h',
       '<(skia_src_path)/gpu/effects/GrOvalEffect.cpp',
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 6f5ca98..b6fa30b 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -293,8 +293,16 @@
                             uint32_t pixelOpsFlags = 0);
 
     /**
+     * Copies contents of src to dst, while applying a gamma curve. Fails if the two surfaces
+     * are not identically sized.
+     * @param dst           the surface to copy to.
+     * @param src           the texture to copy from.
+     * @param gamma         the gamma value to apply.
+     */
+    bool applyGamma(GrRenderTarget* dst, GrTexture* src, SkScalar gamma);
+
+    /**
      * Copies a rectangle of texels from src to dst.
-     * bounds.
      * @param dst           the surface to copy to.
      * @param src           the surface to copy from.
      * @param srcRect       the rectangle of the src that should be copied.
diff --git a/include/gpu/GrSurface.h b/include/gpu/GrSurface.h
index b87b2db..8c6930a 100644
--- a/include/gpu/GrSurface.h
+++ b/include/gpu/GrSurface.h
@@ -100,7 +100,7 @@
      *                      packed.
      * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum.
      *
-     * @return true if the read succeeded, false if not. The read can fail because of an
+     * @return true if the write succeeded, false if not. The write can fail because of an
      *              unsupported pixel config.
      */
     bool writePixels(int left, int top, int width, int height,
diff --git a/include/views/SkOSWindow_Android.h b/include/views/SkOSWindow_Android.h
index 74175ba..234e65c 100644
--- a/include/views/SkOSWindow_Android.h
+++ b/include/views/SkOSWindow_Android.h
@@ -28,7 +28,8 @@
         kNativeGL_BackEndType,
     };
 
-    bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info);
+    bool attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor,
+                AttachmentInfo* info);
     void release();
     void present();
     bool makeFullscreen() { return true; }
diff --git a/include/views/SkOSWindow_Mac.h b/include/views/SkOSWindow_Mac.h
index efa97bf..4558a58 100644
--- a/include/views/SkOSWindow_Mac.h
+++ b/include/views/SkOSWindow_Mac.h
@@ -33,7 +33,8 @@
     };
 
     void    release();
-    bool    attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*);
+    bool    attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor,
+                   AttachmentInfo*);
     void    present();
 
     bool    makeFullscreen();
diff --git a/include/views/SkOSWindow_SDL.h b/include/views/SkOSWindow_SDL.h
index 6823441..1ab51ca 100644
--- a/include/views/SkOSWindow_SDL.h
+++ b/include/views/SkOSWindow_SDL.h
@@ -29,7 +29,7 @@
     };
 
     void release();
-    bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*);
+    bool attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor, AttachmentInfo*);
     void present();
     bool makeFullscreen();
     void setVsync(bool);
diff --git a/include/views/SkOSWindow_Unix.h b/include/views/SkOSWindow_Unix.h
index 30386da..2e1b9cc 100644
--- a/include/views/SkOSWindow_Unix.h
+++ b/include/views/SkOSWindow_Unix.h
@@ -44,7 +44,7 @@
 #endif // SK_COMMAND_BUFFER
     };
 
-    bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*);
+    bool attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor, AttachmentInfo*);
     void release();
     void present();
 
diff --git a/include/views/SkOSWindow_Win.h b/include/views/SkOSWindow_Win.h
index eb8b1fc..8d53781 100644
--- a/include/views/SkOSWindow_Win.h
+++ b/include/views/SkOSWindow_Win.h
@@ -47,7 +47,7 @@
 #endif // SK_SUPPORT_GPU
     };
 
-    bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*);
+    bool attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor, AttachmentInfo*);
     void release();
     void present();
 
@@ -127,7 +127,7 @@
 
     void updateSize();
 #if SK_SUPPORT_GPU
-    bool attachGL(int msaaSampleCount, AttachmentInfo* info);
+    bool attachGL(int msaaSampleCount, bool deepColor, AttachmentInfo* info);
     void detachGL();
     void presentGL();
 
diff --git a/include/views/SkOSWindow_iOS.h b/include/views/SkOSWindow_iOS.h
index 5a8b2e3..c0b2fc3 100644
--- a/include/views/SkOSWindow_iOS.h
+++ b/include/views/SkOSWindow_iOS.h
@@ -22,7 +22,8 @@
     };
 
     void    release();
-    bool    attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*);
+    bool    attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor,
+                   AttachmentInfo*);
     void    present();
 
     bool makeFullscreen() { return true; }
diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h
index b4fabd2..341046a 100644
--- a/include/views/SkWindow.h
+++ b/include/views/SkWindow.h
@@ -32,8 +32,14 @@
     virtual ~SkWindow();
 
     struct AttachmentInfo {
+        AttachmentInfo()
+            : fSampleCount(0)
+            , fStencilBits(0)
+            , fColorBits(0) {}
+
         int fSampleCount;
         int fStencilBits;
+        int fColorBits;
     };
 
     SkSurfaceProps getSurfaceProps() const { return fSurfaceProps; }
diff --git a/platform_tools/android/apps/sample_app/src/main/jni/com_skia_SkiaSampleRenderer.cpp b/platform_tools/android/apps/sample_app/src/main/jni/com_skia_SkiaSampleRenderer.cpp
index e5c6660..f233a2f 100644
--- a/platform_tools/android/apps/sample_app/src/main/jni/com_skia_SkiaSampleRenderer.cpp
+++ b/platform_tools/android/apps/sample_app/src/main/jni/com_skia_SkiaSampleRenderer.cpp
@@ -61,7 +61,8 @@
 SkOSWindow::~SkOSWindow() {
 }
 
-bool SkOSWindow::attach(SkBackEndTypes /* attachType */, int /*msaaSampleCount*/, AttachmentInfo* info)
+bool SkOSWindow::attach(SkBackEndTypes /* attachType */, int /*msaaSampleCount*/,
+                        bool /*deepColor*/, AttachmentInfo* info)
 {
     JNIEnv* env = gActivityGlue.m_env;
     if (!env || !gWindowGlue.m_getMSAASampleCount || !gWindowGlue.m_obj) {
diff --git a/platform_tools/android/apps/visualbench/src/main/jni/SkOSWindow_AndroidNative.cpp b/platform_tools/android/apps/visualbench/src/main/jni/SkOSWindow_AndroidNative.cpp
index 644191d..2c53eb0 100644
--- a/platform_tools/android/apps/visualbench/src/main/jni/SkOSWindow_AndroidNative.cpp
+++ b/platform_tools/android/apps/visualbench/src/main/jni/SkOSWindow_AndroidNative.cpp
@@ -23,6 +23,7 @@
 
 bool SkOSWindow::attach(SkBackEndTypes attachType,
                         int /*msaaSampleCount*/,
+                        bool /*deepColor*/
                         AttachmentInfo* info) {
     static const EGLint kEGLContextAttribsForOpenGL[] = {
         EGL_NONE
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index 6500612..1df96c8 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -16,6 +16,7 @@
 #include "SkData.h"
 #include "SkDocument.h"
 #include "SkGraphics.h"
+#include "SkImage_Base.h"
 #include "SkImageEncoder.h"
 #include "SkOSFile.h"
 #include "SkPaint.h"
@@ -178,6 +179,8 @@
         fCurIntf = nullptr;
         fCurRenderTarget = nullptr;
         fMSAASampleCount = 0;
+        fDeepColor = false;
+        fActualColorBits = 0;
 #endif
         fBackend = kNone_BackEndType;
     }
@@ -190,7 +193,7 @@
 #endif
     }
 
-    void setUpBackend(SampleWindow* win, int msaaSampleCount) override {
+    void setUpBackend(SampleWindow* win, int msaaSampleCount, bool deepColor) override {
         SkASSERT(kNone_BackEndType == fBackend);
 
         fBackend = kNone_BackEndType;
@@ -219,12 +222,15 @@
                 break;
         }
         AttachmentInfo attachmentInfo;
-        bool result = win->attach(fBackend, msaaSampleCount, &attachmentInfo);
+        bool result = win->attach(fBackend, msaaSampleCount, deepColor, &attachmentInfo);
         if (!result) {
             SkDebugf("Failed to initialize GL");
             return;
         }
         fMSAASampleCount = msaaSampleCount;
+        fDeepColor = deepColor;
+        // Assume that we have at least 24-bit output, for backends that don't supply this data
+        fActualColorBits = SkTMax(attachmentInfo.fColorBits, 24);
 
         SkASSERT(nullptr == fCurIntf);
         SkAutoTUnref<const GrGLInterface> glInterface;
@@ -294,9 +300,12 @@
 #if SK_SUPPORT_GPU
         if (IsGpuDeviceType(dType) && fCurContext) {
             SkSurfaceProps props(win->getSurfaceProps());
-            if (kRGBA_F16_SkColorType == win->info().colorType()) {
-                // Need to make an off-screen F16 surface - the current render target is
-                // (probably) the wrong format.
+            if (kRGBA_F16_SkColorType == win->info().colorType() || fActualColorBits > 24) {
+                // If we're rendering to F16, we need an off-screen surface - the current render
+                // target is most likely the wrong format.
+                //
+                // If we're using a deep (10-bit or higher) surface, we probably need an off-screen
+                // surface. 10-bit, in particular, has strange gamma behavior.
                 return SkSurface::MakeRenderTarget(fCurContext, SkBudgeted::kNo, win->info(),
                                                    fMSAASampleCount, &props).release();
             } else {
@@ -313,29 +322,27 @@
         if (fCurContext) {
             // in case we have queued drawing calls
             fCurContext->flush();
+        }
 
-            if (!IsGpuDeviceType(dType)) {
-                // need to send the raster bits to the (gpu) window
-                const SkBitmap& bm = win->getBitmap();
-                fCurRenderTarget->writePixels(0, 0, bm.width(), bm.height(),
-                                             SkImageInfo2GrPixelConfig(bm.colorType(),
-                                                                       bm.alphaType(),
-                                                                       bm.profileType(),
-                                                                       *fCurContext->caps()),
-                                             bm.getPixels(),
-                                             bm.rowBytes(),
-                                             GrContext::kFlushWrites_PixelOp);
-            } else if (kRGBA_F16_SkColorType == win->info().colorType()) {
-                SkBitmap bm;
-                bm.allocPixels(win->info());
-                canvas->readPixels(&bm, 0, 0);
-                fCurRenderTarget->writePixels(0, 0, bm.width(), bm.height(),
-                                              SkImageInfo2GrPixelConfig(bm.info(),
-                                                                        *fCurContext->caps()),
-                                              bm.getPixels(),
-                                              bm.rowBytes(),
-                                              GrContext::kFlushWrites_PixelOp);
-            }
+        if (!IsGpuDeviceType(dType) ||
+            kRGBA_F16_SkColorType == win->info().colorType() ||
+            fActualColorBits > 24) {
+            // We made/have an off-screen surface. Get the contents as an SkImage:
+            SkBitmap bm;
+            bm.allocPixels(win->info());
+            canvas->readPixels(&bm, 0, 0);
+            SkPixmap pm;
+            bm.peekPixels(&pm);
+            sk_sp<SkImage> image(SkImage::MakeTextureFromPixmap(fCurContext, pm,
+                                                                SkBudgeted::kNo));
+            GrTexture* texture = as_IB(image)->peekTexture();
+            SkASSERT(texture);
+
+            // With ten-bit output, we need to manually apply the gamma of the output device
+            // (unless we're in non-gamma correct mode, in which case our data is already
+            // fake-sRGB, like we're expected to put in the 10-bit buffer):
+            bool doGamma = (fActualColorBits == 30) && SkImageInfoIsGammaCorrect(win->info());
+            fCurContext->applyGamma(fCurRenderTarget, texture, doGamma ? 1.0f / 2.2f : 1.0f);
         }
 #endif
 
@@ -346,8 +353,9 @@
 #if SK_SUPPORT_GPU
         if (fCurContext) {
             AttachmentInfo attachmentInfo;
-            win->attach(fBackend, fMSAASampleCount, &attachmentInfo);
+            win->attach(fBackend, fMSAASampleCount, fDeepColor, &attachmentInfo);
             SkSafeUnref(fCurRenderTarget);
+            fActualColorBits = SkTMax(attachmentInfo.fColorBits, 24);
             fCurRenderTarget = win->renderTarget(attachmentInfo, fCurIntf, fCurContext);
         }
 #endif
@@ -369,6 +377,14 @@
 #endif
     }
 
+    int getColorBits() override {
+#if SK_SUPPORT_GPU
+        return fActualColorBits;
+#else
+        return 24;
+#endif
+    }
+
 private:
 
 #if SK_SUPPORT_GPU
@@ -376,6 +392,8 @@
     const GrGLInterface*    fCurIntf;
     GrRenderTarget*         fCurRenderTarget;
     int fMSAASampleCount;
+    bool fDeepColor;
+    int fActualColorBits;
 #endif
 
     SkOSWindow::SkBackEndTypes fBackend;
@@ -773,6 +791,7 @@
 
 DEFINE_string(slide, "", "Start on this sample.");
 DEFINE_int32(msaa, 0, "Request multisampling with this count.");
+DEFINE_bool(deepColor, false, "Request deep color (10-bit/channel or more) display buffer.");
 DEFINE_string(pictureDir, "", "Read pictures from here.");
 DEFINE_string(picture, "", "Path to single picture.");
 DEFINE_string(sequence, "", "Path to file containing the desired samples/gms to show.");
@@ -861,6 +880,7 @@
     }
 
     fMSAASampleCount = FLAGS_msaa;
+    fDeepColor = FLAGS_deepColor;
 
     if (FLAGS_list) {
         listTitles();
@@ -1023,7 +1043,7 @@
         devManager->ref();
         fDevManager = devManager;
     }
-    fDevManager->setUpBackend(this, fMSAASampleCount);
+    fDevManager->setUpBackend(this, fMSAASampleCount, fDeepColor);
 
     // If another constructor set our dimensions, ensure that our
     // onSizeChange gets called.
@@ -1847,7 +1867,7 @@
 
     fDevManager->tearDownBackend(this);
     fDeviceType = type;
-    fDevManager->setUpBackend(this, fMSAASampleCount);
+    fDevManager->setUpBackend(this, fMSAASampleCount, fDeepColor);
 
     this->updateTitle();
     this->inval(nullptr);
@@ -1857,7 +1877,7 @@
     this->setColorType(ct, pt);
 
     fDevManager->tearDownBackend(this);
-    fDevManager->setUpBackend(this, fMSAASampleCount);
+    fDevManager->setUpBackend(this, fMSAASampleCount, fDeepColor);
 
     this->updateTitle();
     this->inval(nullptr);
@@ -1884,7 +1904,7 @@
 void SampleWindow::toggleDistanceFieldFonts() {
     // reset backend
     fDevManager->tearDownBackend(this);
-    fDevManager->setUpBackend(this, fMSAASampleCount);
+    fDevManager->setUpBackend(this, fMSAASampleCount, fDeepColor);
 
     SkSurfaceProps props = this->getSurfaceProps();
     uint32_t flags = props.flags() ^ SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
@@ -1897,7 +1917,7 @@
 void SampleWindow::setPixelGeometry(int pixelGeometryIndex) {
     // reset backend
     fDevManager->tearDownBackend(this);
-    fDevManager->setUpBackend(this, fMSAASampleCount);
+    fDevManager->setUpBackend(this, fMSAASampleCount, fDeepColor);
 
     const SkSurfaceProps& oldProps = this->getSurfaceProps();
     SkSurfaceProps newProps(oldProps.flags(), SkSurfaceProps::kLegacyFontHost_InitType);
@@ -2162,6 +2182,10 @@
 
     title.appendf(" %s", find_config_name(this->info()));
 
+    if (fDevManager && fDevManager->getColorBits() > 24) {
+        title.appendf(" %d bpc", fDevManager->getColorBits());
+    }
+
     if (gTreatSkColorAsSRGB) {
         title.append(" sRGB");
     }
diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h
index 18e7564..ad7a871 100644
--- a/samplecode/SampleApp.h
+++ b/samplecode/SampleApp.h
@@ -74,7 +74,7 @@
     public:
 
 
-        virtual void setUpBackend(SampleWindow* win, int msaaSampleCount) = 0;
+        virtual void setUpBackend(SampleWindow* win, int msaaSampleCount, bool deepColor) = 0;
 
         virtual void tearDownBackend(SampleWindow* win) = 0;
 
@@ -97,6 +97,10 @@
 
         // return the GrRenderTarget backing gpu devices (nullptr if not built with GPU support)
         virtual GrRenderTarget* getGrRenderTarget() = 0;
+
+        // return the color depth of the output device
+        virtual int getColorBits() = 0;
+
     private:
         typedef SkRefCnt INHERITED;
     };
@@ -212,6 +216,7 @@
     unsigned   fFlipAxis;
 
     int fMSAASampleCount;
+    bool fDeepColor;
 
     SkScalar fZoomCenterX, fZoomCenterY;
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 31845f9..3ea67c9 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -20,6 +20,7 @@
 
 #include "batches/GrCopySurfaceBatch.h"
 #include "effects/GrConfigConversionEffect.h"
+#include "effects/GrGammaEffect.h"
 #include "text/GrTextBlobCache.h"
 
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
@@ -529,6 +530,44 @@
     return true;
 }
 
+bool GrContext::applyGamma(GrRenderTarget* dst, GrTexture* src, SkScalar gamma){
+    ASSERT_SINGLE_OWNER
+    RETURN_FALSE_IF_ABANDONED
+    ASSERT_OWNED_RESOURCE(dst);
+    ASSERT_OWNED_RESOURCE(src);
+    GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::applyGamma");
+
+    // Dimensions must match exactly.
+    if (dst->width() != src->width() || dst->height() != src->height()) {
+        return false;
+    }
+
+    SkSurfaceProps props(SkSurfaceProps::kGammaCorrect_Flag,
+                         SkSurfaceProps::kLegacyFontHost_InitType);
+    sk_sp<GrDrawContext> drawContext(this->drawContext(sk_ref_sp(dst), &props));
+    if (!drawContext) {
+        return false;
+    }
+
+    GrPaint paint;
+    if (SkScalarNearlyEqual(gamma, 1.0f)) {
+        paint.addColorTextureProcessor(src, GrCoordTransform::MakeDivByTextureWHMatrix(src));
+    } else {
+        SkAutoTUnref<const GrFragmentProcessor> fp;
+        fp.reset(GrGammaEffect::Create(src, gamma));
+        paint.addColorFragmentProcessor(fp);
+    }
+    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
+    paint.setGammaCorrect(true);
+
+    SkRect rect;
+    src->getBoundsRect(&rect);
+    drawContext->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect);
+
+    this->flushSurfaceWrites(dst);
+    return true;
+}
+
 void GrContext::prepareSurfaceForExternalIO(GrSurface* surface) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 0d3f8d9..29482cc 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -48,7 +48,7 @@
  * we verify the count is as expected.  If a new factory is added, then these numbers must be
  * manually adjusted.
  */
-static const int kFPFactoryCount = 39;
+static const int kFPFactoryCount = 40;
 static const int kGPFactoryCount = 14;
 static const int kXPFactoryCount = 6;
 
diff --git a/src/gpu/effects/GrGammaEffect.cpp b/src/gpu/effects/GrGammaEffect.cpp
new file mode 100644
index 0000000..b2aa48a
--- /dev/null
+++ b/src/gpu/effects/GrGammaEffect.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "GrGammaEffect.h"
+
+#include "GrContext.h"
+#include "GrCoordTransform.h"
+#include "GrFragmentProcessor.h"
+#include "GrInvariantOutput.h"
+#include "GrProcessor.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+class GrGLGammaEffect : public GrGLSLFragmentProcessor {
+public:
+    void emitCode(EmitArgs& args) override {
+        const GrGammaEffect& ge = args.fFp.cast<GrGammaEffect>();
+        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+
+        const char* gammaUniName = nullptr;
+        if (!ge.gammaIsSRGB()) {
+            fGammaUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
+                                                   kDefault_GrSLPrecision, "Gamma", &gammaUniName);
+        }
+
+        GrGLSLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision);
+        SkString tmpDecl;
+        tmpVar.appendDecl(args.fGLSLCaps, &tmpDecl);
+
+        SkString srgbFuncName;
+        if (ge.gammaIsSRGB()) {
+            static const GrGLSLShaderVar gSrgbArgs[] = {
+                GrGLSLShaderVar("x", kFloat_GrSLType),
+            };
+
+            fragBuilder->emitFunction(kFloat_GrSLType,
+                                        "linear_to_srgb",
+                                        SK_ARRAY_COUNT(gSrgbArgs),
+                                        gSrgbArgs,
+                                        "return (x <= 0.0031308) ? (x * 12.92) "
+                                        ": (1.055 * pow(x, 0.416666667) - 0.055);",
+                                        &srgbFuncName);
+        }
+
+        fragBuilder->codeAppendf("%s;", tmpDecl.c_str());
+
+        fragBuilder->codeAppendf("%s = ", tmpVar.c_str());
+        fragBuilder->appendTextureLookup(args.fTexSamplers[0], args.fCoords[0].c_str(),
+                                            args.fCoords[0].getType());
+        fragBuilder->codeAppend(";");
+
+        if (ge.gammaIsSRGB()) {
+            fragBuilder->codeAppendf("%s = vec4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);",
+                                        args.fOutputColor,
+                                        srgbFuncName.c_str(), tmpVar.c_str(),
+                                        srgbFuncName.c_str(), tmpVar.c_str(),
+                                        srgbFuncName.c_str(), tmpVar.c_str(),
+                                        tmpVar.c_str());
+        } else {
+            fragBuilder->codeAppendf("%s = vec4(pow(%s.rgb, vec3(%s)), %s.a);",
+                                        args.fOutputColor, tmpVar.c_str(), gammaUniName,
+                                        tmpVar.c_str());
+        }
+    }
+
+    void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
+        const GrGammaEffect& ge = proc.cast<GrGammaEffect>();
+        if (!ge.gammaIsSRGB()) {
+            pdman.set1f(fGammaUni, ge.gamma());
+        }
+    }
+
+    static inline void GenKey(const GrProcessor& processor, const GrGLSLCaps&,
+                              GrProcessorKeyBuilder* b) {
+        const GrGammaEffect& ge = processor.cast<GrGammaEffect>();
+        uint32_t key = ge.gammaIsSRGB() ? 0x1 : 0x0;
+        b->add32(key);
+    }
+
+private:
+    GrGLSLProgramDataManager::UniformHandle fGammaUni;
+
+    typedef GrGLSLFragmentProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGammaEffect::GrGammaEffect(GrTexture* texture, SkScalar gamma)
+    : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)) {
+    this->initClassID<GrGammaEffect>();
+
+    fGamma = gamma;
+    fGammaIsSRGB = SkScalarNearlyEqual(gamma, 1.0f / 2.2f);
+}
+
+bool GrGammaEffect::onIsEqual(const GrFragmentProcessor& s) const {
+    const GrGammaEffect& other = s.cast<GrGammaEffect>();
+    return
+        other.fGammaIsSRGB == fGammaIsSRGB &&
+        other.fGamma == fGamma;
+}
+
+void GrGammaEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
+    inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGammaEffect);
+
+const GrFragmentProcessor* GrGammaEffect::TestCreate(GrProcessorTestData* d) {
+    // We want to be sure and test sRGB sometimes
+    bool srgb = d->fRandom->nextBool();
+    return new GrGammaEffect(d->fTextures[GrProcessorUnitTest::kSkiaPMTextureIdx],
+                             srgb ? 1.0f / 2.2f : d->fRandom->nextRangeScalar(0.5f, 2.0f));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGammaEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
+                                               GrProcessorKeyBuilder* b) const {
+    GrGLGammaEffect::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* GrGammaEffect::onCreateGLSLInstance() const {
+    return new GrGLGammaEffect();
+}
+
+const GrFragmentProcessor* GrGammaEffect::Create(GrTexture* texture, SkScalar gamma) {
+    return new GrGammaEffect(texture, gamma);
+}
diff --git a/src/gpu/effects/GrGammaEffect.h b/src/gpu/effects/GrGammaEffect.h
new file mode 100644
index 0000000..44d6d67
--- /dev/null
+++ b/src/gpu/effects/GrGammaEffect.h
@@ -0,0 +1,42 @@
+/*
+ * 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 GrGammaEffect_DEFINED
+#define GrGammaEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+
+class GrGammaEffect : public GrSingleTextureEffect {
+public:
+    /**
+     * Creates an effect that applies a gamma curve. The source texture is always
+     * sampled unfiltered and with clamping.
+     */
+    static const GrFragmentProcessor* Create(GrTexture*, SkScalar gamma);
+
+    const char* name() const override { return "Gamma"; }
+
+    bool gammaIsSRGB() const { return fGammaIsSRGB; }
+    SkScalar gamma() const { return fGamma; }
+
+private:
+    GrGammaEffect(GrTexture*, SkScalar gamma);
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+    void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
+
+    bool fGammaIsSRGB;
+    SkScalar fGamma;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/utils/win/SkWGL.h b/src/utils/win/SkWGL.h
index 5322d91..ebd95e7 100644
--- a/src/utils/win/SkWGL.h
+++ b/src/utils/win/SkWGL.h
@@ -37,6 +37,9 @@
 #define SK_WGL_SUPPORT_OPENGL                       0x2010
 #define SK_WGL_DOUBLE_BUFFER                        0x2011
 #define SK_WGL_COLOR_BITS                           0x2014
+#define SK_WGL_RED_BITS                             0x2015
+#define SK_WGL_GREEN_BITS                           0x2017
+#define SK_WGL_BLUE_BITS                            0x2019
 #define SK_WGL_ALPHA_BITS                           0x201B
 #define SK_WGL_STENCIL_BITS                         0x2023
 #define SK_WGL_FULL_ACCELERATION                    0x2027
@@ -138,7 +141,7 @@
  * (including non-MSAA) will be created. If preferCoreProfile is true but a core profile cannot be
  * created then a compatible profile context will be created.
  */
-HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest context);
+HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, bool deepColor, SkWGLContextRequest context);
 
 /**
  * Helper class for creating a pbuffer context and deleting all the handles when finished. This
diff --git a/src/utils/win/SkWGL_win.cpp b/src/utils/win/SkWGL_win.cpp
index 65ffaf1..dc1b4ca 100644
--- a/src/utils/win/SkWGL_win.cpp
+++ b/src/utils/win/SkWGL_win.cpp
@@ -288,38 +288,41 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static void get_pixel_formats_to_try(HDC dc, const SkWGLExtensions& extensions,
-                                     bool doubleBuffered, int msaaSampleCount,
+                                     bool doubleBuffered, int msaaSampleCount, bool deepColor,
                                      int formatsToTry[2]) {
-    int iAttrs[] = {
-        SK_WGL_DRAW_TO_WINDOW, TRUE,
-        SK_WGL_DOUBLE_BUFFER, (doubleBuffered ? TRUE : FALSE),
-        SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION,
-        SK_WGL_SUPPORT_OPENGL, TRUE,
-        SK_WGL_COLOR_BITS, 24,
-        SK_WGL_ALPHA_BITS, 8,
-        SK_WGL_STENCIL_BITS, 8,
-        0, 0
+    auto appendAttr = [](SkTDArray<int>& attrs, int attr, int value) {
+        attrs.push(attr);
+        attrs.push(value);
     };
 
+    SkTDArray<int> iAttrs;
+    appendAttr(iAttrs, SK_WGL_DRAW_TO_WINDOW, TRUE);
+    appendAttr(iAttrs, SK_WGL_DOUBLE_BUFFER, (doubleBuffered ? TRUE : FALSE));
+    appendAttr(iAttrs, SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION);
+    appendAttr(iAttrs, SK_WGL_SUPPORT_OPENGL, TRUE);
+    if (deepColor) {
+        appendAttr(iAttrs, SK_WGL_RED_BITS, 10);
+        appendAttr(iAttrs, SK_WGL_GREEN_BITS, 10);
+        appendAttr(iAttrs, SK_WGL_BLUE_BITS, 10);
+        appendAttr(iAttrs, SK_WGL_ALPHA_BITS, 2);
+    } else {
+        appendAttr(iAttrs, SK_WGL_COLOR_BITS, 24);
+        appendAttr(iAttrs, SK_WGL_ALPHA_BITS, 8);
+    }
+    appendAttr(iAttrs, SK_WGL_STENCIL_BITS, 8);
+
     float fAttrs[] = {0, 0};
 
     // Get a MSAA format if requested and possible.
     if (msaaSampleCount > 0 &&
         extensions.hasExtension(dc, "WGL_ARB_multisample")) {
-        static const int kIAttrsCount = SK_ARRAY_COUNT(iAttrs);
-        int msaaIAttrs[kIAttrsCount + 4];
-        memcpy(msaaIAttrs, iAttrs, sizeof(int) * kIAttrsCount);
-        SkASSERT(0 == msaaIAttrs[kIAttrsCount - 2] &&
-                 0 == msaaIAttrs[kIAttrsCount - 1]);
-        msaaIAttrs[kIAttrsCount - 2] = SK_WGL_SAMPLE_BUFFERS;
-        msaaIAttrs[kIAttrsCount - 1] = TRUE;
-        msaaIAttrs[kIAttrsCount + 0] = SK_WGL_SAMPLES;
-        msaaIAttrs[kIAttrsCount + 1] = msaaSampleCount;
-        msaaIAttrs[kIAttrsCount + 2] = 0;
-        msaaIAttrs[kIAttrsCount + 3] = 0;
+        SkTDArray<int> msaaIAttrs = iAttrs;
+        appendAttr(msaaIAttrs, SK_WGL_SAMPLE_BUFFERS, TRUE);
+        appendAttr(msaaIAttrs, SK_WGL_SAMPLES, msaaSampleCount);
+        appendAttr(msaaIAttrs, 0, 0);
         unsigned int num;
         int formats[64];
-        extensions.choosePixelFormat(dc, msaaIAttrs, fAttrs, 64, formats, &num);
+        extensions.choosePixelFormat(dc, msaaIAttrs.begin(), fAttrs, 64, formats, &num);
         num = SkTMin(num, 64U);
         formatsToTry[0] = extensions.selectFormat(formats, num, dc, msaaSampleCount);
     }
@@ -327,7 +330,8 @@
     // Get a non-MSAA format
     int* format = -1 == formatsToTry[0] ? &formatsToTry[0] : &formatsToTry[1];
     unsigned int num;
-    extensions.choosePixelFormat(dc, iAttrs, fAttrs, 1, format, &num);
+    appendAttr(iAttrs, 0, 0);
+    extensions.choosePixelFormat(dc, iAttrs.begin(), fAttrs, 1, format, &num);
 }
 
 static HGLRC create_gl_context(HDC dc, SkWGLExtensions extensions, SkWGLContextRequest contextType) {
@@ -393,7 +397,8 @@
     return glrc;
 }
 
-HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest contextType) {
+HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, bool deepColor,
+                         SkWGLContextRequest contextType) {
     SkWGLExtensions extensions;
     if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) {
         return nullptr;
@@ -402,7 +407,7 @@
     BOOL set = FALSE;
 
     int pixelFormatsToTry[] = { -1, -1 };
-    get_pixel_formats_to_try(dc, extensions, true, msaaSampleCount, pixelFormatsToTry);
+    get_pixel_formats_to_try(dc, extensions, true, msaaSampleCount, deepColor, pixelFormatsToTry);
     for (int f = 0;
          !set && -1 != pixelFormatsToTry[f] && f < SK_ARRAY_COUNT(pixelFormatsToTry);
          ++f) {
@@ -429,7 +434,7 @@
     for (int dblBuffer = 0; dblBuffer < 2; ++dblBuffer) {
         int pixelFormatsToTry[] = { -1, -1 };
         get_pixel_formats_to_try(parentDC, extensions, (0 != dblBuffer), msaaSampleCount,
-                                 pixelFormatsToTry);
+                                 false, pixelFormatsToTry);
         for (int f = 0; -1 != pixelFormatsToTry[f] && f < SK_ARRAY_COUNT(pixelFormatsToTry); ++f) {
             HPBUFFER pbuf = extensions.createPbuffer(parentDC, pixelFormatsToTry[f], 1, 1, nullptr);
             if (0 != pbuf) {
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
index 244bc49..d06d6bb 100644
--- a/src/views/SkWindow.cpp
+++ b/src/views/SkWindow.cpp
@@ -340,9 +340,14 @@
     //
     // Also, we may not have real sRGB support (ANGLE, in particular), so check for
     // that, and fall back to L32:
-    desc.fConfig = grContext->caps()->srgbSupport() && SkImageInfoIsGammaCorrect(info())
-        ? kSkiaGamma8888_GrPixelConfig
-        : kSkia8888_GrPixelConfig;
+    //
+    // ... and, if we're using a 10-bit/channel FB0, it doesn't do sRGB conversion on write,
+    // so pretend that it's non-sRGB 8888:
+    desc.fConfig =
+        grContext->caps()->srgbSupport() &&
+        SkImageInfoIsGammaCorrect(info()) &&
+        (attachmentInfo.fColorBits != 30)
+        ? kSkiaGamma8888_GrPixelConfig : kSkia8888_GrPixelConfig;
     desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
     desc.fSampleCnt = attachmentInfo.fSampleCount;
     desc.fStencilBits = attachmentInfo.fStencilBits;
diff --git a/src/views/ios/SkOSWindow_iOS.mm b/src/views/ios/SkOSWindow_iOS.mm
index 2a74ed6..aa7d375 100755
--- a/src/views/ios/SkOSWindow_iOS.mm
+++ b/src/views/ios/SkOSWindow_iOS.mm
@@ -59,6 +59,7 @@
 
 bool SkOSWindow::attach(SkBackEndTypes /* attachType */,
                         int /* msaaSampleCount */,
+                        bool /* deepColor */,
                         AttachmentInfo* info) {
     [(SkUIView*)fHWND getAttachmentInfo:info];
     bool success = true;
diff --git a/src/views/mac/SkOSWindow_Mac.mm b/src/views/mac/SkOSWindow_Mac.mm
index ee53721..faf1bba 100644
--- a/src/views/mac/SkOSWindow_Mac.mm
+++ b/src/views/mac/SkOSWindow_Mac.mm
@@ -65,7 +65,8 @@
     [(SkNSView*)fHWND onUpdateMenu:menu];
 }
 
-bool SkOSWindow::attach(SkBackEndTypes attachType, int sampleCount, AttachmentInfo* info) {
+bool SkOSWindow::attach(SkBackEndTypes attachType, int sampleCount, bool /*deepColor*/,
+                        AttachmentInfo* info) {
     return [(SkNSView*)fHWND attach:attachType withMSAASampleCount:sampleCount andGetInfo:info];
 }
 
diff --git a/src/views/sdl/SkOSWindow_SDL.cpp b/src/views/sdl/SkOSWindow_SDL.cpp
index 76d4349..88b8353 100644
--- a/src/views/sdl/SkOSWindow_SDL.cpp
+++ b/src/views/sdl/SkOSWindow_SDL.cpp
@@ -57,7 +57,8 @@
     }
 }
 
-bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) {
+bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor,
+                        AttachmentInfo* info) {
     this->createWindow(msaaSampleCount);
     if (!fWindow) {
         return false;
diff --git a/src/views/unix/SkOSWindow_Unix.cpp b/src/views/unix/SkOSWindow_Unix.cpp
index 7f9d168..2f19592 100644
--- a/src/views/unix/SkOSWindow_Unix.cpp
+++ b/src/views/unix/SkOSWindow_Unix.cpp
@@ -348,7 +348,8 @@
 
 /////////////////////////////////////////////////////////////////////////
 
-bool SkOSWindow::attach(SkBackEndTypes, int msaaSampleCount, AttachmentInfo* info) {
+bool SkOSWindow::attach(SkBackEndTypes, int msaaSampleCount, bool deepColor,
+                        AttachmentInfo* info) {
     this->initWindow(msaaSampleCount, info);
 
     if (nullptr == fUnixWindow.fDisplay) {
diff --git a/src/views/win/SkOSWindow_win.cpp b/src/views/win/SkOSWindow_win.cpp
index a8d05d9..a5aafd0 100644
--- a/src/views/win/SkOSWindow_win.cpp
+++ b/src/views/win/SkOSWindow_win.cpp
@@ -339,10 +339,10 @@
 
 #if SK_SUPPORT_GPU
 
-bool SkOSWindow::attachGL(int msaaSampleCount, AttachmentInfo* info) {
+bool SkOSWindow::attachGL(int msaaSampleCount, bool deepColor, AttachmentInfo* info) {
     HDC dc = GetDC((HWND)fHWND);
     if (NULL == fHGLRC) {
-        fHGLRC = SkCreateWGLContext(dc, msaaSampleCount,
+        fHGLRC = SkCreateWGLContext(dc, msaaSampleCount, deepColor,
                 kGLPreferCompatibilityProfile_SkWGLContextRequest);
         if (NULL == fHGLRC) {
             return false;
@@ -353,11 +353,13 @@
         glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
     }
     if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) {
-        // use DescribePixelFormat to get the stencil bit depth.
+        // use DescribePixelFormat to get the stencil and color bit depth.
         int pixelFormat = GetPixelFormat(dc);
         PIXELFORMATDESCRIPTOR pfd;
         DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd);
         info->fStencilBits = pfd.cStencilBits;
+        // pfd.cColorBits includes alpha, so it will be 32 in 8/8/8/8 and 10/10/10/2
+        info->fColorBits = pfd.cRedBits + pfd.cGreenBits + pfd.cBlueBits;
 
         // Get sample count if the MSAA WGL extension is present
         SkWGLExtensions extensions;
@@ -651,7 +653,8 @@
 #endif // SK_SUPPORT_GPU
 
 // return true on success
-bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) {
+bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, bool deepColor,
+                        AttachmentInfo* info) {
 
     // attach doubles as "windowResize" so we need to allo
     // already bound states to pass through again
@@ -665,7 +668,7 @@
         break;
 #if SK_SUPPORT_GPU
     case kNativeGL_BackEndType:
-        result = attachGL(msaaSampleCount, info);
+        result = attachGL(msaaSampleCount, deepColor, info);
         break;
 #if SK_ANGLE
     case kANGLE_BackEndType:
diff --git a/tests/ApplyGammaTest.cpp b/tests/ApplyGammaTest.cpp
new file mode 100644
index 0000000..dd79b87
--- /dev/null
+++ b/tests/ApplyGammaTest.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 <initializer_list>
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "GrTextureProvider.h"
+
+#include "SkUtils.h"
+
+ // using anonymous namespace because these functions are used as template params.
+namespace {
+/** convert 0..1 linear value to 0..1 srgb */
+float linear_to_srgb(float linear) {
+    if (linear <= 0.0031308) {
+        return linear * 12.92f;
+    } else {
+        return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f;
+    }
+}
+}
+
+bool check_gamma(uint32_t src, uint32_t dst, float gamma, float error, uint32_t* expected) {
+    if (SkScalarNearlyEqual(gamma, 1.f)) {
+        *expected = src;
+        return src == dst;
+    } else {
+        bool result = true;
+        uint32_t expectedColor = src & 0xff000000;
+
+        // Alpha should always be exactly preserved.
+        if ((src & 0xff000000) != (dst & 0xff000000)) {
+            result = false;
+        }
+
+        for (int c = 0; c < 3; ++c) {
+            uint8_t srcComponent = (src & (0xff << (c * 8))) >> (c * 8);
+            float lower = SkTMax(0.f, (float)srcComponent - error);
+            float upper = SkTMin(255.f, (float)srcComponent + error);
+            if (SkScalarNearlyEqual(gamma, 1.0f / 2.2f)) {
+                lower = linear_to_srgb(lower / 255.f);
+                upper = linear_to_srgb(upper / 255.f);
+            } else {
+                lower = powf(lower / 255.f, gamma);
+                upper = powf(upper / 255.f, gamma);
+            }
+            SkASSERT(lower >= 0.f && lower <= 255.f);
+            SkASSERT(upper >= 0.f && upper <= 255.f);
+            uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8);
+            if (dstComponent < SkScalarFloorToInt(lower * 255.f) ||
+                dstComponent > SkScalarCeilToInt(upper * 255.f)) {
+                result = false;
+            }
+            uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 127.5f);
+            expectedColor |= expectedComponent << (c * 8);
+        }
+
+        *expected = expectedColor;
+        return result;
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.fGrContext;
+    static const int kW = 10;
+    static const int kH = 10;
+    static const size_t kRowBytes = sizeof(uint32_t) * kW;
+
+    GrSurfaceDesc baseDesc;
+    baseDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    baseDesc.fWidth = kW;
+    baseDesc.fHeight = kH;
+
+    SkAutoTMalloc<uint32_t> srcPixels(kW * kH);
+    for (int i = 0; i < kW * kH; ++i) {
+        srcPixels.get()[i] = i;
+    }
+
+    SkAutoTMalloc<uint32_t> dstPixels(kW * kH);
+    for (int i = 0; i < kW * kH; ++i) {
+        dstPixels.get()[i] = ~i;
+    }
+
+    SkAutoTMalloc<uint32_t> read(kW * kH);
+
+    // We allow more error on GPUs with lower precision shader variables.
+    float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f;
+
+    for (auto sOrigin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
+        for (auto dOrigin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
+            for (auto sFlags : { kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags }) {
+                for (auto gamma : { 1.0f, 1.0f / 1.8f, 1.0f / 2.2f }) {
+                    GrSurfaceDesc srcDesc = baseDesc;
+                    srcDesc.fOrigin = sOrigin;
+                    srcDesc.fFlags = sFlags;
+                    GrSurfaceDesc dstDesc = baseDesc;
+                    dstDesc.fOrigin = dOrigin;
+                    dstDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+
+                    SkAutoTUnref<GrTexture> src(
+                        context->textureProvider()->createTexture(srcDesc, SkBudgeted::kNo,
+                                                                  srcPixels.get(),
+                                                                  kRowBytes));
+                    SkAutoTUnref<GrTexture> dst(
+                        context->textureProvider()->createTexture(dstDesc, SkBudgeted::kNo,
+                                                                  dstPixels.get(),
+                                                                  kRowBytes));
+                    if (!src || !dst) {
+                        ERRORF(reporter, "Could not create surfaces for copy surface test.");
+                        continue;
+                    }
+
+                    bool result = context->applyGamma(dst->asRenderTarget(), src, gamma);
+
+                    // To make the copied src rect correct we would apply any dst clipping
+                    // back to the src rect, but we don't use it again so don't bother.
+                    if (!result) {
+                        ERRORF(reporter, "Unexpected failure from applyGamma.");
+                        continue;
+                    }
+
+                    sk_memset32(read.get(), 0, kW * kH);
+                    if (!dst->readPixels(0, 0, kW, kH, baseDesc.fConfig, read.get(), kRowBytes)) {
+                        ERRORF(reporter, "Error calling readPixels");
+                        continue;
+                    }
+
+                    bool abort = false;
+                    // Validate that pixels were copied/transformed correctly.
+                    for (int y = 0; y < kH && !abort; ++y) {
+                        for (int x = 0; x < kW && !abort; ++x) {
+                            uint32_t r = read.get()[y * kW + x];
+                            uint32_t s = srcPixels.get()[y * kW + x];
+                            uint32_t expected;
+                            if (!check_gamma(s, r, gamma, error, &expected)) {
+                                ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x "
+                                       "from src 0x%08x and gamma %f. Got %08x",
+                                       x, y, expected, s, gamma, r);
+                                abort = true;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+#endif
diff --git a/tools/VisualBench/VisualBench.cpp b/tools/VisualBench/VisualBench.cpp
index 154c84b..7f2e90c 100644
--- a/tools/VisualBench/VisualBench.cpp
+++ b/tools/VisualBench/VisualBench.cpp
@@ -106,7 +106,7 @@
 
 void VisualBench::setupContext() {
     int screenSamples = FLAGS_offscreen ? 0 : FLAGS_msaa;
-    if (!this->attach(kNativeGL_BackEndType, screenSamples, &fAttachmentInfo)) {
+    if (!this->attach(kNativeGL_BackEndType, screenSamples, false, &fAttachmentInfo)) {
         SkDebugf("Not possible to create backend.\n");
         INHERITED::release();
         SkFAIL("Could not create backend\n");
diff --git a/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp b/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp
index 3d70be8..4162b8f 100644
--- a/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp
+++ b/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp
@@ -92,7 +92,7 @@
     HGLRC glrc;
 
     if (nullptr == fPbufferContext) {
-        if (!(fGlRenderContext = SkCreateWGLContext(fDeviceContext, 0, contextType))) {
+        if (!(fGlRenderContext = SkCreateWGLContext(fDeviceContext, 0, false, contextType))) {
             SkDebugf("Could not create rendering context.\n");
             this->destroyGLContext();
             return;
diff --git a/tools/viewer/android/Window_android.h b/tools/viewer/android/Window_android.h
index 6099f89..d41f0a5 100644
--- a/tools/viewer/android/Window_android.h
+++ b/tools/viewer/android/Window_android.h
@@ -17,12 +17,12 @@
     /**
      * Leave plenty of space between this item and the ones defined in the glue layer
      */
-	APP_CMD_INVAL_WINDOW = 64,
+    APP_CMD_INVAL_WINDOW = 64,
 };
 
 class Window_android : public Window {
 public:
-	Window_android() : Window() {}
+    Window_android() : Window() {}
     ~Window_android() override {}
 
     bool init(android_app* app_state);
@@ -31,7 +31,7 @@
     void setTitle(const char*) override;
     void show() override {}
 
-    bool attach(BackEndType attachType, int msaaSampleCount) override;
+    bool attach(BackEndType attachType, int msaaSampleCount, bool deepColor) override;
     void inval() override;
 
     void paintIfNeeded();