From 2d1ee7936e3536e45c963db004e3b512bb415fd8 Mon Sep 17 00:00:00 2001 From: brianosman Date: Thu, 5 May 2016 12:24:31 -0700 Subject: 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 --- example/HelloWorld.cpp | 2 +- experimental/SkV8Example/SkV8Example.cpp | 2 +- experimental/iOSSampleApp/SkSampleUIView.mm | 12 +- gyp/gpu.gypi | 2 + include/gpu/GrContext.h | 10 +- include/gpu/GrSurface.h | 2 +- include/views/SkOSWindow_Android.h | 3 +- include/views/SkOSWindow_Mac.h | 3 +- include/views/SkOSWindow_SDL.h | 2 +- include/views/SkOSWindow_Unix.h | 2 +- include/views/SkOSWindow_Win.h | 4 +- include/views/SkOSWindow_iOS.h | 3 +- include/views/SkWindow.h | 6 + .../src/main/jni/com_skia_SkiaSampleRenderer.cpp | 3 +- .../src/main/jni/SkOSWindow_AndroidNative.cpp | 1 + samplecode/SampleApp.cpp | 90 +++++++----- samplecode/SampleApp.h | 7 +- src/gpu/GrContext.cpp | 39 ++++++ src/gpu/GrProcessor.cpp | 2 +- src/gpu/effects/GrGammaEffect.cpp | 136 ++++++++++++++++++ src/gpu/effects/GrGammaEffect.h | 42 ++++++ src/utils/win/SkWGL.h | 5 +- src/utils/win/SkWGL_win.cpp | 57 ++++---- src/views/SkWindow.cpp | 11 +- src/views/ios/SkOSWindow_iOS.mm | 1 + src/views/mac/SkOSWindow_Mac.mm | 3 +- src/views/sdl/SkOSWindow_SDL.cpp | 3 +- src/views/unix/SkOSWindow_Unix.cpp | 3 +- src/views/win/SkOSWindow_win.cpp | 13 +- tests/ApplyGammaTest.cpp | 156 +++++++++++++++++++++ tools/VisualBench/VisualBench.cpp | 2 +- .../gpu/gl/win/CreatePlatformGLTestContext_win.cpp | 2 +- tools/viewer/android/Window_android.h | 6 +- 33 files changed, 541 insertions(+), 94 deletions(-) create mode 100644 src/gpu/effects/GrGammaEffect.cpp create mode 100644 src/gpu/effects/GrGammaEffect.h create mode 100644 tests/ApplyGammaTest.cpp diff --git a/example/HelloWorld.cpp b/example/HelloWorld.cpp index 892dce1c80..c2dd7d89f1 100644 --- a/example/HelloWorld.cpp +++ b/example/HelloWorld.cpp @@ -62,7 +62,7 @@ bool HelloWorldWindow::setUpBackend() { 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 fe205d740d..bb448bad15 100644 --- a/experimental/SkV8Example/SkV8Example.cpp +++ b/experimental/SkV8Example/SkV8Example.cpp @@ -74,7 +74,7 @@ void SkV8ExampleWindow::windowSizeChanged() { 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 bd6d533cc8..8bf7c904e1 100644 --- a/experimental/iOSSampleApp/SkSampleUIView.mm +++ b/experimental/iOSSampleApp/SkSampleUIView.mm @@ -47,7 +47,7 @@ public: #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 @@ public: 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 @@ public: 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 @@ public: 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 5919e24b59..f0bb333e94 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 6f5ca981e5..b6fa30bc35 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -292,9 +292,17 @@ public: size_t rowBytes, 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 b87b2dbaf0..8c6930a7a7 100644 --- a/include/gpu/GrSurface.h +++ b/include/gpu/GrSurface.h @@ -100,7 +100,7 @@ public: * 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 74175bafd1..234e65c256 100644 --- a/include/views/SkOSWindow_Android.h +++ b/include/views/SkOSWindow_Android.h @@ -28,7 +28,8 @@ public: 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 efa97bf872..4558a58833 100644 --- a/include/views/SkOSWindow_Mac.h +++ b/include/views/SkOSWindow_Mac.h @@ -33,7 +33,8 @@ public: }; 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 6823441715..1ab51ca415 100644 --- a/include/views/SkOSWindow_SDL.h +++ b/include/views/SkOSWindow_SDL.h @@ -29,7 +29,7 @@ public: }; 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 30386da183..2e1b9ccc81 100644 --- a/include/views/SkOSWindow_Unix.h +++ b/include/views/SkOSWindow_Unix.h @@ -44,7 +44,7 @@ public: #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 eb8b1fc892..8d5378108e 100644 --- a/include/views/SkOSWindow_Win.h +++ b/include/views/SkOSWindow_Win.h @@ -47,7 +47,7 @@ public: #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 @@ private: 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 5a8b2e3d2f..c0b2fc3f0a 100644 --- a/include/views/SkOSWindow_iOS.h +++ b/include/views/SkOSWindow_iOS.h @@ -22,7 +22,8 @@ public: }; 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 b4fabd253e..341046aacc 100644 --- a/include/views/SkWindow.h +++ b/include/views/SkWindow.h @@ -32,8 +32,14 @@ public: 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 e5c6660921..f233a2fc39 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(void*) : fDestroyRequested(false) { 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 644191d3d7..2c53eb0812 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 @@ SkOSWindow::~SkOSWindow() { 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 6500612a52..1df96c80c8 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 @@ public: fCurIntf = nullptr; fCurRenderTarget = nullptr; fMSAASampleCount = 0; + fDeepColor = false; + fActualColorBits = 0; #endif fBackend = kNone_BackEndType; } @@ -190,7 +193,7 @@ public: #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 @@ public: 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 glInterface; @@ -294,9 +300,12 @@ public: #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 @@ public: 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 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 @@ public: #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 @@ public: #endif } + int getColorBits() override { +#if SK_SUPPORT_GPU + return fActualColorBits; +#else + return 24; +#endif + } + private: #if SK_SUPPORT_GPU @@ -376,6 +392,8 @@ private: const GrGLInterface* fCurIntf; GrRenderTarget* fCurRenderTarget; int fMSAASampleCount; + bool fDeepColor; + int fActualColorBits; #endif SkOSWindow::SkBackEndTypes fBackend; @@ -773,6 +791,7 @@ static void restrict_samples(SkTDArray& factories, const S 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 @@ SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* dev } fMSAASampleCount = FLAGS_msaa; + fDeepColor = FLAGS_deepColor; if (FLAGS_list) { listTitles(); @@ -1023,7 +1043,7 @@ SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* dev 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 @@ void SampleWindow::setDeviceType(DeviceType type) { fDevManager->tearDownBackend(this); fDeviceType = type; - fDevManager->setUpBackend(this, fMSAASampleCount); + fDevManager->setUpBackend(this, fMSAASampleCount, fDeepColor); this->updateTitle(); this->inval(nullptr); @@ -1857,7 +1877,7 @@ void SampleWindow::setDeviceColorType(SkColorType ct, SkColorProfileType pt) { 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::toggleFPS() { 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::toggleDistanceFieldFonts() { 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 @@ void SampleWindow::updateTitle() { 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 18e75640d7..ad7a871b98 100644 --- a/samplecode/SampleApp.h +++ b/samplecode/SampleApp.h @@ -74,7 +74,7 @@ public: 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 @@ public: // 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 @@ private: unsigned fFlipAxis; int fMSAASampleCount; + bool fDeepColor; SkScalar fZoomCenterX, fZoomCenterY; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 31845f9d6d..3ea67c9f01 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 @@ bool GrContext::readSurfacePixels(GrSurface* src, 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 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 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 0d3f8d9166..29482ccc63 100644 --- a/src/gpu/GrProcessor.cpp +++ b/src/gpu/GrProcessor.cpp @@ -48,7 +48,7 @@ GrProcessorTestFactory::GetFactories() { * 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 0000000000..b2aa48aad2 --- /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(); + 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(); + if (!ge.gammaIsSRGB()) { + pdman.set1f(fGammaUni, ge.gamma()); + } + } + + static inline void GenKey(const GrProcessor& processor, const GrGLSLCaps&, + GrProcessorKeyBuilder* b) { + const GrGammaEffect& ge = processor.cast(); + 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(); + + fGamma = gamma; + fGammaIsSRGB = SkScalarNearlyEqual(gamma, 1.0f / 2.2f); +} + +bool GrGammaEffect::onIsEqual(const GrFragmentProcessor& s) const { + const GrGammaEffect& other = s.cast(); + 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 0000000000..44d6d6707c --- /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 5322d916b5..ebd95e7a83 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 @@ enum SkWGLContextRequest { * (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 65ffaf1ecf..dc1b4caf1a 100644 --- a/src/utils/win/SkWGL_win.cpp +++ b/src/utils/win/SkWGL_win.cpp @@ -288,38 +288,41 @@ SkWGLExtensions::SkWGLExtensions() /////////////////////////////////////////////////////////////////////////////// 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& attrs, int attr, int value) { + attrs.push(attr); + attrs.push(value); }; + SkTDArray 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 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 @@ static void get_pixel_formats_to_try(HDC dc, const SkWGLExtensions& extensions, // 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 @@ static HGLRC create_gl_context(HDC dc, SkWGLExtensions extensions, SkWGLContextR 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 @@ HGLRC SkCreateWGLContext(HDC dc, int msaaSampleCount, SkWGLContextRequest contex 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 @@ SkWGLPbufferContext* SkWGLPbufferContext::Create(HDC parentDC, int msaaSampleCou 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 244bc49742..d06d6bb9eb 100644 --- a/src/views/SkWindow.cpp +++ b/src/views/SkWindow.cpp @@ -340,9 +340,14 @@ GrRenderTarget* SkWindow::renderTarget(const AttachmentInfo& attachmentInfo, // // 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 2a74ed6b2c..aa7d3759bd 100755 --- a/src/views/ios/SkOSWindow_iOS.mm +++ b/src/views/ios/SkOSWindow_iOS.mm @@ -59,6 +59,7 @@ void SkOSWindow::onUpdateMenu(SkOSMenu* menu) { 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 ee5372113d..faf1bbafd2 100644 --- a/src/views/mac/SkOSWindow_Mac.mm +++ b/src/views/mac/SkOSWindow_Mac.mm @@ -65,7 +65,8 @@ void SkOSWindow::onUpdateMenu(const SkOSMenu* menu) { [(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 76d43498e8..88b8353b6c 100644 --- a/src/views/sdl/SkOSWindow_SDL.cpp +++ b/src/views/sdl/SkOSWindow_SDL.cpp @@ -57,7 +57,8 @@ void SkOSWindow::release() { } } -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 7f9d168cb8..2f195927e0 100644 --- a/src/views/unix/SkOSWindow_Unix.cpp +++ b/src/views/unix/SkOSWindow_Unix.cpp @@ -348,7 +348,8 @@ static void glXSwapInterval(Display* dsp, GLXDrawable drawable, int interval) { ///////////////////////////////////////////////////////////////////////// -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 a8d05d96f7..a5aafd05d1 100644 --- a/src/views/win/SkOSWindow_win.cpp +++ b/src/views/win/SkOSWindow_win.cpp @@ -339,10 +339,10 @@ void SkEvent::SignalQueueTimer(SkMSec delay) #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 @@ bool SkOSWindow::attachGL(int msaaSampleCount, AttachmentInfo* info) { 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 @@ void SkOSWindow::presentCommandBuffer() { #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 @@ bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, Attachme 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 0000000000..dd79b879b5 --- /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 +#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 srcPixels(kW * kH); + for (int i = 0; i < kW * kH; ++i) { + srcPixels.get()[i] = i; + } + + SkAutoTMalloc dstPixels(kW * kH); + for (int i = 0; i < kW * kH; ++i) { + dstPixels.get()[i] = ~i; + } + + SkAutoTMalloc 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 src( + context->textureProvider()->createTexture(srcDesc, SkBudgeted::kNo, + srcPixels.get(), + kRowBytes)); + SkAutoTUnref 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 154c84b6f2..7f2e90c43f 100644 --- a/tools/VisualBench/VisualBench.cpp +++ b/tools/VisualBench/VisualBench.cpp @@ -106,7 +106,7 @@ void VisualBench::resetContext() { 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 3d70be82cd..4162b8f42d 100644 --- a/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp +++ b/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp @@ -92,7 +92,7 @@ WinGLTestContext::WinGLTestContext(GrGLStandard forcedGpuAPI) 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 6099f8983e..d41f0a54c2 100644 --- a/tools/viewer/android/Window_android.h +++ b/tools/viewer/android/Window_android.h @@ -17,12 +17,12 @@ enum { /** * 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 @@ public: 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(); -- cgit v1.2.3