Merge "Xform bitmaps to sRGB on SW and PDF canvases" into oc-dev am: c29dffed16

am: fd5d5425ee

Change-Id: If960ccd900ebc2b841c2f13ca4f25820a48a950f
This commit is contained in:
Matt Sarett
2017-04-06 22:16:49 +00:00
committed by android-build-merger
6 changed files with 94 additions and 17 deletions

View File

@@ -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() {

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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();
@@ -178,6 +182,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()

View File

@@ -97,6 +97,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.
*
@@ -104,10 +113,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.

View File

@@ -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));
}