diff options
author | 2017-03-29 16:25:10 -0400 | |
---|---|---|
committer | 2017-04-06 15:14:00 +0000 | |
commit | ea70d22dc8dc5d61f075edf6d03f86f6a68169cd (patch) | |
tree | 076905e7bf8d2a09631a597d8c1c4975964e8ab6 | |
parent | 3cc32f2aa205db9770c694a823f6d9a532ff0901 (diff) |
Xform bitmaps to sRGB on SW and PDF canvases
For picture-backed canvases, we will defer the xform
until playback.
Test: Unit tests and cts test.
Bug: 32984164
Change-Id: Ib74663bcb688b74b6ba8792b403b0475126732af
-rw-r--r-- | core/jni/android/graphics/Picture.cpp | 2 | ||||
-rw-r--r-- | core/jni/android/graphics/pdf/PdfDocument.cpp | 7 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.cpp | 28 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.h | 7 | ||||
-rw-r--r-- | libs/hwui/hwui/Canvas.h | 13 | ||||
-rw-r--r-- | libs/hwui/tests/unit/SkiaCanvasTests.cpp | 54 |
6 files changed, 94 insertions, 17 deletions
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp index 7b381b41bde5..bfb25113a7e9 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -44,7 +44,7 @@ Canvas* Picture::beginRecording(int width, int height) { mWidth = width; mHeight = height; SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height)); - return Canvas::create_canvas(canvas); + return Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer); } void Picture::endRecording() { diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp index d233f7b6805e..abc3599a48c1 100644 --- a/core/jni/android/graphics/pdf/PdfDocument.cpp +++ b/core/jni/android/graphics/pdf/PdfDocument.cpp @@ -21,6 +21,7 @@ #include "CreateJavaOutputStreamAdaptor.h" +#include "SkColorSpaceXformCanvas.h" #include "SkDocument.h" #include "SkPicture.h" #include "SkPictureRecorder.h" @@ -94,8 +95,10 @@ public: SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight, &(page->mContentRect)); + std::unique_ptr<SkCanvas> toSRGBCanvas = + SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB()); - canvas->drawPicture(page->mPicture); + toSRGBCanvas->drawPicture(page->mPicture); document->endPage(); } @@ -128,7 +131,7 @@ static jlong nativeStartPage(JNIEnv* env, jobject thiz, jlong documentPtr, PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr); SkCanvas* canvas = document->startPage(pageWidth, pageHeight, contentLeft, contentTop, contentRight, contentBottom); - return reinterpret_cast<jlong>(Canvas::create_canvas(canvas)); + return reinterpret_cast<jlong>(Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer)); } static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index daf14af87288..13d7e09b6e79 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -23,6 +23,7 @@ #include "hwui/MinikinUtils.h" #include "pipeline/skia/AnimatedDrawables.h" +#include <SkColorSpaceXformCanvas.h> #include <SkDrawable.h> #include <SkDeque.h> #include <SkDrawFilter.h> @@ -44,18 +45,22 @@ Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { return new SkiaCanvas(bitmap); } -Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) { - return new SkiaCanvas(skiaCanvas); +Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB) { + return new SkiaCanvas(skiaCanvas, xformToSRGB); } SkiaCanvas::SkiaCanvas() {} -SkiaCanvas::SkiaCanvas(SkCanvas* canvas) - : mCanvas(canvas) {} +SkiaCanvas::SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB) + : mCanvas(canvas) +{ + LOG_ALWAYS_FATAL_IF(XformToSRGB::kImmediate == xformToSRGB); +} SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) { mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap)); - mCanvas = mCanvasOwned.get(); + mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), SkColorSpace::MakeSRGB()); + mCanvas = mCanvasWrapper.get(); } SkiaCanvas::~SkiaCanvas() {} @@ -92,19 +97,22 @@ private: }; void SkiaCanvas::setBitmap(const SkBitmap& bitmap) { - SkCanvas* newCanvas = new SkCanvas(bitmap); + std::unique_ptr<SkCanvas> newCanvas = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap)); + std::unique_ptr<SkCanvas> newCanvasWrapper = + SkCreateColorSpaceXformCanvas(newCanvas.get(), SkColorSpace::MakeSRGB()); if (!bitmap.isNull()) { // Copy the canvas matrix & clip state. - newCanvas->setMatrix(mCanvas->getTotalMatrix()); + newCanvasWrapper->setMatrix(mCanvas->getTotalMatrix()); - ClipCopier copier(newCanvas); + ClipCopier copier(newCanvasWrapper.get()); mCanvas->replayClips(&copier); } // deletes the previously owned canvas (if any) - mCanvasOwned = std::unique_ptr<SkCanvas>(newCanvas); - mCanvas = newCanvas; + mCanvasOwned = std::move(newCanvas); + mCanvasWrapper = std::move(newCanvasWrapper); + mCanvas = mCanvasWrapper.get(); // clean up the old save stack mSaveStack.reset(nullptr); diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 34c3717557ff..13f979cf7d71 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -37,8 +37,12 @@ public: * @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must * not be NULL. This constructor does not take ownership, so the caller * must guarantee that it remains valid while the SkiaCanvas is valid. + * @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB + * color space before drawing. This makes sense for software rendering. + * For the picture case, it may make more sense to leave bitmaps as is, + * and handle the xform when replaying the picture. */ - explicit SkiaCanvas(SkCanvas* canvas); + explicit SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB); virtual ~SkiaCanvas(); @@ -181,6 +185,7 @@ private: class Clip; + std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us // unless it is the same as mCanvasOwned.get() diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 969d87716ce6..ed328328dbd6 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -93,6 +93,15 @@ public: static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode = nullptr); + enum class XformToSRGB { + // Transform any Bitmaps to the sRGB color space before drawing. + kImmediate, + + // Draw the Bitmap as is. This likely means that we are recording and that the + // transform can be handled at playback time. + kDefer, + }; + /** * Create a new Canvas object which delegates to an SkCanvas. * @@ -100,10 +109,12 @@ public: * delegated to this object. This function will call ref() on the * SkCanvas, and the returned Canvas will unref() it upon * destruction. + * @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB + * color space before drawing. * @return new non-null Canvas Object. The type of DisplayList produced by this canvas is * determined based on Properties::getRenderPipelineType(). */ - static Canvas* create_canvas(SkCanvas* skiaCanvas); + static Canvas* create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB); /** * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas. diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 7fb75dce8a19..44476af742f1 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -44,7 +44,8 @@ OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { // record the same text draw into a SkPicture and replay it into a Recording canvas SkPictureRecorder recorder; SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0); - std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas)); + std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas, + Canvas::XformToSRGB::kDefer)); TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25); sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); @@ -63,7 +64,7 @@ OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { TEST(SkiaCanvas, drawShadowLayer) { auto surface = SkSurface::MakeRasterN32Premul(10, 10); - SkiaCanvas canvas(surface->getCanvas()); + SkiaCanvas canvas(surface->getCanvas(), Canvas::XformToSRGB::kDefer); // clear to white canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc); @@ -78,3 +79,52 @@ TEST(SkiaCanvas, drawShadowLayer) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); ASSERT_NE(TestUtils::getColor(surface, 5, 5), SK_ColorWHITE); } + +TEST(SkiaCanvas, colorSpaceXform) { + sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kAdobeRGB_Gamut); + + SkImageInfo adobeInfo = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType, adobe); + sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo); + SkBitmap adobeSkBitmap; + adobeBitmap->getSkBitmap(&adobeSkBitmap); + adobeSkBitmap.lockPixels(); + *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red + + SkImageInfo info = adobeInfo.makeColorSpace(nullptr); + sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + + // Create a software canvas. + SkiaCanvas canvas(skBitmap); + canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr); + // The result should be fully red, since we convert to sRGB at draw time. + skBitmap.lockPixels(); + ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0)); + + // Now try in kDefer mode. This is a little strange given that, in practice, all software + // canvases are kImmediate. + SkCanvas skCanvas(skBitmap); + SkiaCanvas deferCanvas(&skCanvas, Canvas::XformToSRGB::kDefer); + deferCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr); + // The result should be as initialized, since we deferred the conversion to sRGB. + skBitmap.lockPixels(); + ASSERT_EQ(0xFF0000F0, *skBitmap.getAddr32(0, 0)); + + // Test picture recording. We will kDefer the xform at recording time, but handle it when + // we playback to the software canvas. + SkPictureRecorder recorder; + SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0); + SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer); + picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr); + sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); + + // Playback to a deferred canvas. The result should be as initialized. + deferCanvas.asSkCanvas()->drawPicture(picture); + ASSERT_EQ(0xFF0000F0, *skBitmap.getAddr32(0, 0)); + + // Playback to an immediate canvas. The result should be fully red. + canvas.asSkCanvas()->drawPicture(picture); + ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0)); +} |