1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include <functional> 9 #include "SkBitmap.h" 10 #include "SkCanvas.h" 11 #include "SkColor.h" 12 #include "SkColorPriv.h" 13 #include "SkSurface.h" 14 #include "SkTaskGroup.h" 15 #include "SkUtils.h" 16 #include "Test.h" 17 18 #if SK_SUPPORT_GPU 19 #include "GrContext.h" 20 #include "GrContextPriv.h" 21 #include "GrResourceProvider.h" 22 #include "GrSurfaceContext.h" 23 #include "GrSurfaceProxy.h" 24 #include "GrTest.h" 25 #include "GrTexture.h" 26 #endif 27 28 struct Results { int diffs, diffs_0x00, diffs_0xff, diffs_by_1; }; 29 30 static bool acceptable(const Results& r) { 31 #if 0 32 SkDebugf("%d diffs, %d at 0x00, %d at 0xff, %d off by 1, all out of 65536\n", 33 r.diffs, r.diffs_0x00, r.diffs_0xff, r.diffs_by_1); 34 #endif 35 return r.diffs_by_1 == r.diffs // never off by more than 1 36 && r.diffs_0x00 == 0 // transparent must stay transparent 37 && r.diffs_0xff == 0; // opaque must stay opaque 38 } 39 40 template <typename Fn> 41 static Results test(Fn&& multiply) { 42 Results r = { 0,0,0,0 }; 43 for (int x = 0; x < 256; x++) { 44 for (int y = 0; y < 256; y++) { 45 int p = multiply(x, y), 46 ideal = (x*y+127)/255; 47 if (p != ideal) { 48 r.diffs++; 49 if (x == 0x00 || y == 0x00) { r.diffs_0x00++; } 50 if (x == 0xff || y == 0xff) { r.diffs_0xff++; } 51 if (SkTAbs(ideal - p) == 1) { r.diffs_by_1++; } 52 } 53 }} 54 return r; 55 } 56 57 DEF_TEST(Blend_byte_multiply, r) { 58 // These are all temptingly close but fundamentally broken. 59 int (*broken[])(int, int) = { 60 [](int x, int y) { return (x*y)>>8; }, 61 [](int x, int y) { return (x*y+128)>>8; }, 62 [](int x, int y) { y += y>>7; return (x*y)>>8; }, 63 }; 64 for (auto multiply : broken) { REPORTER_ASSERT(r, !acceptable(test(multiply))); } 65 66 // These are fine to use, but not perfect. 67 int (*fine[])(int, int) = { 68 [](int x, int y) { return (x*y+x)>>8; }, 69 [](int x, int y) { return (x*y+y)>>8; }, 70 [](int x, int y) { return (x*y+255)>>8; }, 71 [](int x, int y) { y += y>>7; return (x*y+128)>>8; }, 72 }; 73 for (auto multiply : fine) { REPORTER_ASSERT(r, acceptable(test(multiply))); } 74 75 // These are pefect. 76 int (*perfect[])(int, int) = { 77 [](int x, int y) { return (x*y+127)/255; }, // Duh. 78 [](int x, int y) { int p = (x*y+128); return (p+(p>>8))>>8; }, 79 [](int x, int y) { return ((x*y+128)*257)>>16; }, 80 }; 81 for (auto multiply : perfect) { REPORTER_ASSERT(r, test(multiply).diffs == 0); } 82 } 83 84 #if SK_SUPPORT_GPU 85 namespace { 86 static sk_sp<SkSurface> create_gpu_surface_backend_texture_as_render_target( 87 GrContext* context, int sampleCnt, int width, int height, GrPixelConfig config, 88 GrSurfaceOrigin origin, 89 sk_sp<GrTexture>* backingSurface) { 90 GrSurfaceDesc backingDesc; 91 backingDesc.fFlags = kRenderTarget_GrSurfaceFlag; 92 backingDesc.fOrigin = origin; 93 backingDesc.fWidth = width; 94 backingDesc.fHeight = height; 95 backingDesc.fConfig = config; 96 backingDesc.fSampleCnt = sampleCnt; 97 98 *backingSurface = context->resourceProvider()->createTexture(backingDesc, SkBudgeted::kNo); 99 if (!(*backingSurface)) { 100 return nullptr; 101 } 102 103 GrBackendTexture backendTex = 104 GrTest::CreateBackendTexture(context->contextPriv().getBackend(), 105 width, 106 height, 107 config, 108 (*backingSurface)->getTextureHandle()); 109 sk_sp<SkSurface> surface = 110 SkSurface::MakeFromBackendTextureAsRenderTarget(context, backendTex, origin, 111 sampleCnt, nullptr, nullptr); 112 113 return surface; 114 } 115 } 116 117 // Tests blending to a surface with no texture available. 118 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ES2BlendWithNoTexture, reporter, ctxInfo) { 119 GrContext* context = ctxInfo.grContext(); 120 const int kWidth = 10; 121 const int kHeight = 10; 122 const GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig; 123 const SkColorType kColorType = kRGBA_8888_SkColorType; 124 125 // Build our test cases: 126 struct RectAndSamplePoint { 127 SkRect rect; 128 SkIPoint outPoint; 129 SkIPoint inPoint; 130 } allRectsAndPoints[3] = { 131 {SkRect::MakeXYWH(0, 0, 5, 5), SkIPoint::Make(7, 7), SkIPoint::Make(2, 2)}, 132 {SkRect::MakeXYWH(2, 2, 5, 5), SkIPoint::Make(1, 1), SkIPoint::Make(4, 4)}, 133 {SkRect::MakeXYWH(5, 5, 5, 5), SkIPoint::Make(2, 2), SkIPoint::Make(7, 7)}, 134 }; 135 136 struct TestCase { 137 RectAndSamplePoint fRectAndPoints; 138 SkRect fClip; 139 int fSampleCnt; 140 GrSurfaceOrigin fOrigin; 141 }; 142 std::vector<TestCase> testCases; 143 144 for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) { 145 for (int sampleCnt : {0, 4}) { 146 for (auto rectAndPoints : allRectsAndPoints) { 147 for (auto clip : {SkRect::MakeXYWH(0, 0, 10, 10), SkRect::MakeXYWH(1, 1, 8, 8)}) { 148 testCases.push_back({rectAndPoints, clip, sampleCnt, origin}); 149 } 150 } 151 } 152 } 153 154 // Run each test case: 155 for (auto testCase : testCases) { 156 int sampleCnt = testCase.fSampleCnt; 157 SkRect paintRect = testCase.fRectAndPoints.rect; 158 SkIPoint outPoint = testCase.fRectAndPoints.outPoint; 159 SkIPoint inPoint = testCase.fRectAndPoints.inPoint; 160 GrSurfaceOrigin origin = testCase.fOrigin; 161 162 sk_sp<GrTexture> backingSurface; 163 // BGRA forces a framebuffer blit on ES2. 164 sk_sp<SkSurface> surface = create_gpu_surface_backend_texture_as_render_target( 165 context, sampleCnt, kWidth, kHeight, kConfig, origin, &backingSurface); 166 167 if (!surface && sampleCnt > 0) { 168 // Some platforms don't support MSAA. 169 continue; 170 } 171 REPORTER_ASSERT(reporter, !!surface); 172 173 // Fill our canvas with 0xFFFF80 174 SkCanvas* canvas = surface->getCanvas(); 175 canvas->clipRect(testCase.fClip, false); 176 SkPaint black_paint; 177 black_paint.setColor(SkColorSetRGB(0xFF, 0xFF, 0x80)); 178 canvas->drawRect(SkRect::MakeXYWH(0, 0, kWidth, kHeight), black_paint); 179 180 // Blend 2x2 pixels at 5,5 with 0x80FFFF. Use multiply blend mode as this will trigger 181 // a copy of the destination. 182 SkPaint white_paint; 183 white_paint.setColor(SkColorSetRGB(0x80, 0xFF, 0xFF)); 184 white_paint.setBlendMode(SkBlendMode::kMultiply); 185 canvas->drawRect(paintRect, white_paint); 186 187 // Read the result into a bitmap. 188 SkBitmap bitmap; 189 REPORTER_ASSERT(reporter, bitmap.tryAllocPixels(SkImageInfo::Make( 190 kWidth, kHeight, kColorType, kPremul_SkAlphaType))); 191 REPORTER_ASSERT( 192 reporter, 193 surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0)); 194 195 // Check the in/out pixels. 196 REPORTER_ASSERT(reporter, bitmap.getColor(outPoint.x(), outPoint.y()) == 197 SkColorSetRGB(0xFF, 0xFF, 0x80)); 198 REPORTER_ASSERT(reporter, bitmap.getColor(inPoint.x(), inPoint.y()) == 199 SkColorSetRGB(0x80, 0xFF, 0x80)); 200 201 // Clean up - surface depends on backingSurface and must be released first. 202 surface.reset(); 203 backingSurface.reset(); 204 } 205 } 206 #endif 207