Ethan Nicholas | 839872c | 2017-10-05 12:36:59 -0400 | [diff] [blame] | 1 | @header { |
| 2 | #include "GrClip.h" |
| 3 | #include "GrContext.h" |
| 4 | #include "GrRenderTargetContext.h" |
| 5 | } |
| 6 | |
| 7 | @class { |
| 8 | enum PMConversion { |
| 9 | kToPremul_PMConversion = 0, |
| 10 | kToUnpremul_PMConversion = 1, |
| 11 | kPMConversionCnt = 2 |
| 12 | }; |
| 13 | |
| 14 | static bool TestForPreservingPMConversions(GrContext* context) { |
| 15 | static constexpr int kSize = 256; |
| 16 | static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig; |
| 17 | SkAutoTMalloc<uint32_t> data(kSize * kSize * 3); |
| 18 | uint32_t* srcData = data.get(); |
| 19 | uint32_t* firstRead = data.get() + kSize * kSize; |
| 20 | uint32_t* secondRead = data.get() + 2 * kSize * kSize; |
| 21 | |
| 22 | // Fill with every possible premultiplied A, color channel value. There will be 256-y |
| 23 | // duplicate values in row y. We set r, g, and b to the same value since they are handled |
| 24 | // identically. |
| 25 | for (int y = 0; y < kSize; ++y) { |
| 26 | for (int x = 0; x < kSize; ++x) { |
| 27 | uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize*y + x]); |
| 28 | color[3] = y; |
| 29 | color[2] = SkTMin(x, y); |
| 30 | color[1] = SkTMin(x, y); |
| 31 | color[0] = SkTMin(x, y); |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | const SkImageInfo ii = SkImageInfo::Make(kSize, kSize, |
| 36 | kRGBA_8888_SkColorType, kPremul_SkAlphaType); |
| 37 | |
| 38 | sk_sp<GrRenderTargetContext> readRTC(context->makeDeferredRenderTargetContext( |
| 39 | SkBackingFit::kExact, |
| 40 | kSize, kSize, |
| 41 | kConfig, nullptr)); |
| 42 | sk_sp<GrRenderTargetContext> tempRTC(context->makeDeferredRenderTargetContext( |
| 43 | SkBackingFit::kExact, |
| 44 | kSize, kSize, |
| 45 | kConfig, nullptr)); |
| 46 | if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) { |
| 47 | return false; |
| 48 | } |
| 49 | GrSurfaceDesc desc; |
| 50 | desc.fOrigin = kTopLeft_GrSurfaceOrigin; |
| 51 | desc.fWidth = kSize; |
| 52 | desc.fHeight = kSize; |
| 53 | desc.fConfig = kConfig; |
| 54 | |
| 55 | sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), |
| 56 | desc, |
| 57 | SkBudgeted::kYes, data, 0); |
| 58 | if (!dataProxy) { |
| 59 | return false; |
| 60 | } |
| 61 | |
| 62 | static const SkRect kRect = SkRect::MakeIWH(kSize, kSize); |
| 63 | |
| 64 | // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw |
| 65 | // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data. |
| 66 | // We then verify that two reads produced the same values. |
| 67 | |
| 68 | GrPaint paint1; |
| 69 | GrPaint paint2; |
| 70 | GrPaint paint3; |
| 71 | std::unique_ptr<GrFragmentProcessor> pmToUPM( |
| 72 | new GrConfigConversionEffect(kToUnpremul_PMConversion)); |
| 73 | std::unique_ptr<GrFragmentProcessor> upmToPM( |
| 74 | new GrConfigConversionEffect(kToPremul_PMConversion)); |
| 75 | |
| 76 | paint1.addColorTextureProcessor(dataProxy, nullptr, SkMatrix::I()); |
| 77 | paint1.addColorFragmentProcessor(pmToUPM->clone()); |
| 78 | paint1.setPorterDuffXPFactory(SkBlendMode::kSrc); |
| 79 | |
| 80 | readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, |
| 81 | kRect); |
| 82 | if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) { |
| 83 | return false; |
| 84 | } |
| 85 | |
| 86 | paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), nullptr, |
| 87 | SkMatrix::I()); |
| 88 | paint2.addColorFragmentProcessor(std::move(upmToPM)); |
| 89 | paint2.setPorterDuffXPFactory(SkBlendMode::kSrc); |
| 90 | |
| 91 | tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, |
| 92 | kRect); |
| 93 | |
| 94 | paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), nullptr, |
| 95 | SkMatrix::I()); |
| 96 | paint3.addColorFragmentProcessor(std::move(pmToUPM)); |
| 97 | paint3.setPorterDuffXPFactory(SkBlendMode::kSrc); |
| 98 | |
| 99 | readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, |
| 100 | kRect); |
| 101 | |
| 102 | if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) { |
| 103 | return false; |
| 104 | } |
| 105 | |
| 106 | for (int y = 0; y < kSize; ++y) { |
| 107 | for (int x = 0; x <= y; ++x) { |
| 108 | if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) { |
| 109 | return false; |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | return true; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | @make { |
| 119 | static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp, |
| 120 | PMConversion pmConversion) { |
| 121 | if (!fp) { |
| 122 | return nullptr; |
| 123 | } |
| 124 | std::unique_ptr<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion)); |
| 125 | std::unique_ptr<GrFragmentProcessor> fpPipeline[] = { std::move(fp), std::move(ccFP) }; |
| 126 | return GrFragmentProcessor::RunInSeries(fpPipeline, 2); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | layout(key) in int pmConversion; |
| 131 | |
| 132 | @emitCode { |
| 133 | fragBuilder->forceHighPrecision(); |
| 134 | } |
| 135 | |
| 136 | void main() { |
| 137 | // Aggressively round to the nearest exact (N / 255) floating point value. This lets us find a |
| 138 | // round-trip preserving pair on some GPUs that do odd byte to float conversion. |
| 139 | sk_OutColor = floor(sk_InColor * 255 + 0.5) / 255; |
| 140 | |
| 141 | @switch (pmConversion) { |
| 142 | case 0 /* kToPremul_PMConversion */: |
| 143 | sk_OutColor.rgb = floor(sk_OutColor.rgb * sk_OutColor.a * 255 + 0.5) / 255; |
| 144 | break; |
| 145 | |
| 146 | case 1 /* kToUnpremul_PMConversion */: |
| 147 | sk_OutColor.rgb = sk_OutColor.a <= 0.0 ? |
| 148 | half3(0) : |
| 149 | floor(sk_OutColor.rgb / sk_OutColor.a * 255 + 0.5) / 255; |
| 150 | break; |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | @test(data) { |
| 155 | PMConversion pmConv = static_cast<PMConversion>(data->fRandom->nextULessThan(kPMConversionCnt)); |
| 156 | return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv)); |
| 157 | } |