• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
acceptable(const Results & r)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>
test(Fn && multiply)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 
DEF_TEST(Blend_byte_multiply,r)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 {
create_gpu_surface_backend_texture_as_render_target(GrContext * context,int sampleCnt,int width,int height,SkColorType colorType,GrPixelConfig config,GrSurfaceOrigin origin,sk_sp<GrTexture> * backingSurface)86 static sk_sp<SkSurface> create_gpu_surface_backend_texture_as_render_target(
87         GrContext* context, int sampleCnt, int width, int height, SkColorType colorType,
88         GrPixelConfig config, 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     auto resourceProvider = context->contextPriv().resourceProvider();
99 
100     *backingSurface = resourceProvider->createTexture(backingDesc, SkBudgeted::kNo);
101     if (!(*backingSurface)) {
102         return nullptr;
103     }
104 
105     GrBackendTexture backendTex = (*backingSurface)->getBackendTexture();
106 
107     sk_sp<SkSurface> surface =
108             SkSurface::MakeFromBackendTextureAsRenderTarget(context, backendTex, origin,
109                                                             sampleCnt, colorType, nullptr, nullptr);
110 
111     return surface;
112 }
113 }
114 
115 // Tests blending to a surface with no texture available.
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ES2BlendWithNoTexture,reporter,ctxInfo)116 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ES2BlendWithNoTexture, reporter, ctxInfo) {
117     GrContext* context = ctxInfo.grContext();
118     const int kWidth = 10;
119     const int kHeight = 10;
120     const GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
121     const SkColorType kColorType = kRGBA_8888_SkColorType;
122 
123     // Build our test cases:
124     struct RectAndSamplePoint {
125         SkRect rect;
126         SkIPoint outPoint;
127         SkIPoint inPoint;
128     } allRectsAndPoints[3] = {
129             {SkRect::MakeXYWH(0, 0, 5, 5), SkIPoint::Make(7, 7), SkIPoint::Make(2, 2)},
130             {SkRect::MakeXYWH(2, 2, 5, 5), SkIPoint::Make(1, 1), SkIPoint::Make(4, 4)},
131             {SkRect::MakeXYWH(5, 5, 5, 5), SkIPoint::Make(2, 2), SkIPoint::Make(7, 7)},
132     };
133 
134     struct TestCase {
135         RectAndSamplePoint fRectAndPoints;
136         SkRect             fClip;
137         int                fSampleCnt;
138         GrSurfaceOrigin    fOrigin;
139     };
140     std::vector<TestCase> testCases;
141 
142     for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
143         for (int sampleCnt : {1, 4}) {
144             for (auto rectAndPoints : allRectsAndPoints) {
145                 for (auto clip : {SkRect::MakeXYWH(0, 0, 10, 10), SkRect::MakeXYWH(1, 1, 8, 8)}) {
146                     testCases.push_back({rectAndPoints, clip, sampleCnt, origin});
147                 }
148             }
149         }
150     }
151 
152     // Run each test case:
153     for (auto testCase : testCases) {
154         int sampleCnt = testCase.fSampleCnt;
155         SkRect paintRect = testCase.fRectAndPoints.rect;
156         SkIPoint outPoint = testCase.fRectAndPoints.outPoint;
157         SkIPoint inPoint = testCase.fRectAndPoints.inPoint;
158         GrSurfaceOrigin origin = testCase.fOrigin;
159 
160         sk_sp<GrTexture> backingSurface;
161         // BGRA forces a framebuffer blit on ES2.
162         sk_sp<SkSurface> surface = create_gpu_surface_backend_texture_as_render_target(
163                 context, sampleCnt, kWidth, kHeight, kColorType, kConfig, origin, &backingSurface);
164 
165         if (!surface && sampleCnt > 1) {
166             // Some platforms don't support MSAA.
167             continue;
168         }
169         REPORTER_ASSERT(reporter, !!surface);
170 
171         // Fill our canvas with 0xFFFF80
172         SkCanvas* canvas = surface->getCanvas();
173         canvas->clipRect(testCase.fClip, false);
174         SkPaint black_paint;
175         black_paint.setColor(SkColorSetRGB(0xFF, 0xFF, 0x80));
176         canvas->drawRect(SkRect::MakeXYWH(0, 0, kWidth, kHeight), black_paint);
177 
178         // Blend 2x2 pixels at 5,5 with 0x80FFFF. Use multiply blend mode as this will trigger
179         // a copy of the destination.
180         SkPaint white_paint;
181         white_paint.setColor(SkColorSetRGB(0x80, 0xFF, 0xFF));
182         white_paint.setBlendMode(SkBlendMode::kMultiply);
183         canvas->drawRect(paintRect, white_paint);
184 
185         // Read the result into a bitmap.
186         SkBitmap bitmap;
187         REPORTER_ASSERT(reporter, bitmap.tryAllocPixels(SkImageInfo::Make(
188                                           kWidth, kHeight, kColorType, kPremul_SkAlphaType)));
189         REPORTER_ASSERT(
190                 reporter,
191                 surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0));
192 
193         // Check the in/out pixels.
194         REPORTER_ASSERT(reporter, bitmap.getColor(outPoint.x(), outPoint.y()) ==
195                                           SkColorSetRGB(0xFF, 0xFF, 0x80));
196         REPORTER_ASSERT(reporter, bitmap.getColor(inPoint.x(), inPoint.y()) ==
197                                           SkColorSetRGB(0x80, 0xFF, 0x80));
198 
199         // Clean up - surface depends on backingSurface and must be released first.
200         surface.reset();
201         backingSurface.reset();
202     }
203 }
204 #endif
205