/* * Copyright 2021 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gm/gm.h" #include "include/core/SkCanvas.h" #include "include/core/SkFont.h" #include "include/core/SkSurface.h" #include "tools/Resources.h" static const skcms_TransferFunction gTFs[] = { SkNamedTransferFn::kSRGB, SkNamedTransferFn::k2Dot2, SkNamedTransferFn::kLinear, SkNamedTransferFn::kRec2020, SkNamedTransferFn::kPQ, SkNamedTransferFn::kHLG, {-3.0f, 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f, 3.0f }, // HLG scaled 4x }; static const skcms_Matrix3x3 gGamuts[] = { SkNamedGamut::kSRGB, SkNamedGamut::kAdobeRGB, SkNamedGamut::kDisplayP3, SkNamedGamut::kRec2020, SkNamedGamut::kXYZ, }; static const int W = 128, H = 128; // These GMs demonstrate that our color space management is self-consistent. // (Important to note, self-consistent, not necessarily correct in an objective sense.) // // Let's let, // // SkColorSpace* imgCS = img->colorSpace(); // SkColorSpace* dstCS = canvas->imageInfo().colorSpace(); // // Ordinarily we'd just // // canvas->drawImage(img, 0,0); // // which would convert that img's pixels from imgCS to dstCS while drawing. // // But before we draw in these GMs, we convert the image to an arbitrarily different color space, // letting midCS range over the cross-product gTFs × gGamuts: // // canvas->drawImage(img->makeColorSpace(midCS), 0,0); // // This converts img first from imgCS to midCS, treating midCS as a destination color space, // and then draws that midCS image to the dstCS canvas, treating midCS as a source color space. // This should draw a grid of images that look identical except for small precision loss. // // If instead of calling SkImage::makeColorSpace() we use SkCanvas::makeSurface() to create a // midCS offscreen, we construct the same logical imgCS -> midCS -> dstCS transform chain while // exercising different drawing code paths. Both strategies should draw roughly the same. namespace { enum Strategy { SkImage_makeColorSpace, SkCanvas_makeSurface }; } static void draw_colorspace_gm(Strategy strategy, SkCanvas* canvas) { if (!canvas->imageInfo().colorSpace()) { canvas->drawString("This GM only makes sense with color-managed drawing.", W,H, SkFont{}, SkPaint{}); return; } sk_sp img = GetResourceAsImage("images/mandrill_128.png"); if (!img) { canvas->drawString("Could not load our test image!", W,H, SkFont{}, SkPaint{}); return; } SkASSERT(img->width() == W); SkASSERT(img->height() == H); SkASSERT(img->colorSpace()); for (skcms_Matrix3x3 gamut : gGamuts) { canvas->save(); for (skcms_TransferFunction tf : gTFs) { sk_sp midCS = SkColorSpace::MakeRGB(tf, gamut); switch (strategy) { case SkImage_makeColorSpace: { canvas->drawImage(img->makeColorSpace(midCS), 0,0); } break; case SkCanvas_makeSurface: { sk_sp offscreen = canvas->makeSurface(canvas->imageInfo().makeColorSpace(midCS)); if (!offscreen) { canvas->drawString("Could not allocate offscreen surface!", W,H, SkFont{}, SkPaint{}); return; } offscreen->getCanvas()->drawImage(img, 0,0); canvas->drawImage(offscreen->makeImageSnapshot(), 0,0); } break; } canvas->translate(W, 0); } canvas->restore(); canvas->translate(0, H); } } DEF_SIMPLE_GM(colorspace, canvas, W*SK_ARRAY_COUNT(gTFs), H*SK_ARRAY_COUNT(gGamuts)) { draw_colorspace_gm(SkImage_makeColorSpace, canvas); } DEF_SIMPLE_GM(colorspace2, canvas, W*SK_ARRAY_COUNT(gTFs), H*SK_ARRAY_COUNT(gGamuts)) { draw_colorspace_gm(SkCanvas_makeSurface, canvas); }