1/*
2 * Copyright 2011 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 "include/core/SkBitmap.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkSurface.h"
11#include "include/gpu/GrBackendSurface.h"
12#include "include/gpu/GrDirectContext.h"
13#include "include/private/SkColorData.h"
14#include "include/private/SkImageInfoPriv.h"
15#include "src/core/SkMathPriv.h"
16#include "src/gpu/GrDirectContextPriv.h"
17#include "src/gpu/GrGpu.h"
18#include "src/gpu/GrProxyProvider.h"
19#include "tests/Test.h"
20#include "tools/gpu/BackendSurfaceFactory.h"
21
22#include <initializer_list>
23
24static const int DEV_W = 100, DEV_H = 100;
25static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
26static const U8CPU DEV_PAD = 0xee;
27
28static SkPMColor get_canvas_color(int x, int y) {
29    SkASSERT(x >= 0 && x < DEV_W);
30    SkASSERT(y >= 0 && y < DEV_H);
31
32    U8CPU r = x;
33    U8CPU g = y;
34    U8CPU b = 0xc;
35
36    U8CPU a = 0x0;
37    switch ((x+y) % 5) {
38        case 0:
39            a = 0xff;
40            break;
41        case 1:
42            a = 0x80;
43            break;
44        case 2:
45            a = 0xCC;
46            break;
47        case 3:
48            a = 0x00;
49            break;
50        case 4:
51            a = 0x01;
52            break;
53    }
54    return SkPremultiplyARGBInline(a, r, g, b);
55}
56
57// assumes any premu/.unpremul has been applied
58static uint32_t pack_color_type(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
59    uint32_t r32;
60    uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
61    switch (ct) {
62        case kBGRA_8888_SkColorType:
63            result[0] = b;
64            result[1] = g;
65            result[2] = r;
66            result[3] = a;
67            break;
68        case kRGBA_8888_SkColorType:  // fallthrough
69        case kRGB_888x_SkColorType:
70            result[0] = r;
71            result[1] = g;
72            result[2] = b;
73            result[3] = a;
74            break;
75        default:
76            SkASSERT(0);
77            return 0;
78    }
79    return r32;
80}
81
82static uint32_t get_bitmap_color(int x, int y, int w, SkColorType ct, SkAlphaType at) {
83    int n = y * w + x;
84    U8CPU b = n & 0xff;
85    U8CPU g = (n >> 8) & 0xff;
86    U8CPU r = (n >> 16) & 0xff;
87    U8CPU a = 0;
88    switch ((x+y) % 5) {
89        case 4:
90            a = 0xff;
91            break;
92        case 3:
93            a = 0x80;
94            break;
95        case 2:
96            a = 0xCC;
97            break;
98        case 1:
99            a = 0x01;
100            break;
101        case 0:
102            a = 0x00;
103            break;
104    }
105    if (kPremul_SkAlphaType == at) {
106        r = SkMulDiv255Ceiling(r, a);
107        g = SkMulDiv255Ceiling(g, a);
108        b = SkMulDiv255Ceiling(b, a);
109    }
110    return pack_color_type(ct, a, r, g , b);
111}
112
113static void fill_surface(SkSurface* surface) {
114    SkBitmap bmp;
115    bmp.allocN32Pixels(DEV_W, DEV_H);
116    for (int y = 0; y < DEV_H; ++y) {
117        for (int x = 0; x < DEV_W; ++x) {
118            *bmp.getAddr32(x, y) = get_canvas_color(x, y);
119        }
120    }
121    surface->writePixels(bmp, 0, 0);
122}
123
124/**
125 *  Lucky for us, alpha is always in the same spot (SK_A32_SHIFT), for both RGBA and BGRA.
126 *  Thus this routine doesn't need to know the exact colortype
127 */
128static uint32_t premul(uint32_t color) {
129    unsigned a = SkGetPackedA32(color);
130    // these next three are not necessarily r,g,b in that order, but they are r,g,b in some order.
131    unsigned c0 = SkGetPackedR32(color);
132    unsigned c1 = SkGetPackedG32(color);
133    unsigned c2 = SkGetPackedB32(color);
134    c0 = SkMulDiv255Ceiling(c0, a);
135    c1 = SkMulDiv255Ceiling(c1, a);
136    c2 = SkMulDiv255Ceiling(c2, a);
137    return SkPackARGB32NoCheck(a, c0, c1, c2);
138}
139
140static SkPMColor convert_to_PMColor(SkColorType ct, SkAlphaType at, uint32_t color) {
141    if (kUnpremul_SkAlphaType == at) {
142        color = premul(color);
143    }
144    switch (ct) {
145        case kRGBA_8888_SkColorType: // fallthrough
146        case kRGB_888x_SkColorType:
147            color = SkSwizzle_RGBA_to_PMColor(color);
148            break;
149        case kBGRA_8888_SkColorType:
150            color = SkSwizzle_BGRA_to_PMColor(color);
151            break;
152        default:
153            SkASSERT(0);
154            break;
155    }
156    return color;
157}
158
159static bool check_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
160    if (!didPremulConversion) {
161        return a == b;
162    }
163    int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
164    int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
165    int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
166    int32_t aB = SkGetPackedB32(a);
167
168    int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
169    int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
170    int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
171    int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
172
173    return aA == bA &&
174           SkAbs32(aR - bR) <= 1 &&
175           SkAbs32(aG - bG) <= 1 &&
176           SkAbs32(aB - bB) <= 1;
177}
178
179bool write_should_succeed(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, bool isGPU) {
180    if (!SkImageInfoValidConversion(dstInfo, srcInfo)) {
181        return false;
182    }
183    if (!isGPU) {
184        return true;
185    }
186    // The GPU backend supports writing unpremul data to a premul dst but not vice versa.
187    if (srcInfo.alphaType() == kPremul_SkAlphaType &&
188        dstInfo.alphaType() == kUnpremul_SkAlphaType) {
189        return false;
190    }
191    if (!SkColorTypeIsAlwaysOpaque(srcInfo.colorType()) &&
192        SkColorTypeIsAlwaysOpaque(dstInfo.colorType())) {
193        return false;
194    }
195    // The source has no alpha value and the dst is only alpha
196    if (SkColorTypeIsAlwaysOpaque(srcInfo.colorType()) &&
197        SkColorTypeIsAlphaOnly(dstInfo.colorType())) {
198        return false;
199    }
200    return true;
201}
202
203static bool check_write(skiatest::Reporter* reporter, SkSurface* surf, SkAlphaType surfaceAlphaType,
204                        const SkBitmap& bitmap, int writeX, int writeY) {
205    size_t canvasRowBytes;
206    const uint32_t* canvasPixels;
207
208    // Can't use canvas->peekPixels(), as we are trying to look at GPU pixels sometimes as well.
209    // At some point this will be unsupported, as we won't allow accessBitmap() to magically call
210    // readPixels for the client.
211    SkBitmap secretDevBitmap;
212    secretDevBitmap.allocN32Pixels(surf->width(), surf->height());
213    if (!surf->readPixels(secretDevBitmap, 0, 0)) {
214        return false;
215    }
216
217    canvasRowBytes = secretDevBitmap.rowBytes();
218    canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels());
219
220    if (nullptr == canvasPixels) {
221        return false;
222    }
223
224    if (surf->width() != DEV_W || surf->height() != DEV_H) {
225        return false;
226    }
227
228    const SkImageInfo& bmInfo = bitmap.info();
229
230    SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
231    for (int cy = 0; cy < DEV_H; ++cy) {
232        for (int cx = 0; cx < DEV_W; ++cx) {
233            SkPMColor canvasPixel = canvasPixels[cx];
234            if (writeRect.contains(cx, cy)) {
235                int bx = cx - writeX;
236                int by = cy - writeY;
237                uint32_t bmpColor8888 = get_bitmap_color(bx, by, bitmap.width(),
238                                                       bmInfo.colorType(), bmInfo.alphaType());
239                bool mul = (kUnpremul_SkAlphaType == bmInfo.alphaType());
240                SkPMColor bmpPMColor = convert_to_PMColor(bmInfo.colorType(), bmInfo.alphaType(),
241                                                          bmpColor8888);
242                if (bmInfo.alphaType() == kOpaque_SkAlphaType ||
243                    surfaceAlphaType == kOpaque_SkAlphaType) {
244                    bmpPMColor |= 0xFF000000;
245                }
246                if (!check_pixel(bmpPMColor, canvasPixel, mul)) {
247                    ERRORF(reporter, "Expected canvas pixel at %d, %d to be 0x%08x, got 0x%08x. "
248                           "Write performed premul: %d", cx, cy, bmpPMColor, canvasPixel, mul);
249                    return false;
250                }
251            } else {
252                SkPMColor testColor = get_canvas_color(cx, cy);
253                if (canvasPixel != testColor) {
254                    ERRORF(reporter, "Canvas pixel outside write rect at %d, %d changed."
255                           " Should be 0x%08x, got 0x%08x. ", cx, cy, testColor, canvasPixel);
256                    return false;
257                }
258            }
259        }
260        if (cy != DEV_H -1) {
261            const char* pad = reinterpret_cast<const char*>(canvasPixels + DEV_W);
262            for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) {
263                bool check;
264                REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
265                if (!check) {
266                    return false;
267                }
268            }
269        }
270        canvasPixels += canvasRowBytes/4;
271    }
272
273    return true;
274}
275
276#include "include/core/SkMallocPixelRef.h"
277
278// This is a tricky pattern, because we have to setConfig+rowBytes AND specify
279// a custom pixelRef (which also has to specify its rowBytes), so we have to be
280// sure that the two rowBytes match (and the infos match).
281//
282static bool alloc_row_bytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) {
283    if (!bm->setInfo(info, rowBytes)) {
284        return false;
285    }
286    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, rowBytes);
287    bm->setPixelRef(std::move(pr), 0, 0);
288    return true;
289}
290
291static void free_pixels(void* pixels, void* ctx) {
292    sk_free(pixels);
293}
294
295static bool setup_bitmap(SkBitmap* bm, SkColorType ct, SkAlphaType at, int w, int h, int tightRB) {
296    size_t rowBytes = tightRB ? 0 : 4 * w + 60;
297    SkImageInfo info = SkImageInfo::Make(w, h, ct, at);
298    if (!alloc_row_bytes(bm, info, rowBytes)) {
299        return false;
300    }
301    for (int y = 0; y < h; ++y) {
302        for (int x = 0; x < w; ++x) {
303            *bm->getAddr32(x, y) = get_bitmap_color(x, y, w, ct, at);
304        }
305    }
306    return true;
307}
308
309static void call_writepixels(SkSurface* surface) {
310    const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
311    SkPMColor pixel = 0;
312    surface->writePixels({info, &pixel, sizeof(SkPMColor)}, 0, 0);
313}
314
315DEF_TEST(WritePixelsSurfaceGenID, reporter) {
316    const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
317    auto surface(SkSurface::MakeRaster(info));
318    uint32_t genID1 = surface->generationID();
319    call_writepixels(surface.get());
320    uint32_t genID2 = surface->generationID();
321    REPORTER_ASSERT(reporter, genID1 != genID2);
322}
323
324static void test_write_pixels(skiatest::Reporter* reporter, SkSurface* surface,
325                              const SkImageInfo& surfaceInfo) {
326    const SkIRect testRects[] = {
327        // entire thing
328        DEV_RECT,
329        // larger on all sides
330        SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
331        // fully contained
332        SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
333        // outside top left
334        SkIRect::MakeLTRB(-10, -10, -1, -1),
335        // touching top left corner
336        SkIRect::MakeLTRB(-10, -10, 0, 0),
337        // overlapping top left corner
338        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
339        // overlapping top left and top right corners
340        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
341        // touching entire top edge
342        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
343        // overlapping top right corner
344        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
345        // contained in x, overlapping top edge
346        SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
347        // outside top right corner
348        SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
349        // touching top right corner
350        SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
351        // overlapping top left and bottom left corners
352        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
353        // touching entire left edge
354        SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
355        // overlapping bottom left corner
356        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
357        // contained in y, overlapping left edge
358        SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
359        // outside bottom left corner
360        SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
361        // touching bottom left corner
362        SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
363        // overlapping bottom left and bottom right corners
364        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
365        // touching entire left edge
366        SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
367        // overlapping bottom right corner
368        SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
369        // overlapping top right and bottom right corners
370        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
371    };
372
373    SkCanvas* canvas = surface->getCanvas();
374
375    static const struct {
376        SkColorType fColorType;
377        SkAlphaType fAlphaType;
378    } gSrcConfigs[] = {
379            {kRGBA_8888_SkColorType, kPremul_SkAlphaType},
380            {kRGBA_8888_SkColorType, kUnpremul_SkAlphaType},
381            {kRGB_888x_SkColorType, kOpaque_SkAlphaType},
382            {kBGRA_8888_SkColorType, kPremul_SkAlphaType},
383            {kBGRA_8888_SkColorType, kUnpremul_SkAlphaType},
384    };
385    for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
386        const SkIRect& rect = testRects[r];
387        for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
388            for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
389                const SkColorType ct = gSrcConfigs[c].fColorType;
390                const SkAlphaType at = gSrcConfigs[c].fAlphaType;
391
392                bool isGPU = SkToBool(surface->getCanvas()->recordingContext());
393                fill_surface(surface);
394                SkBitmap bmp;
395                REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(),
396                                                       rect.height(), SkToBool(tightBmp)));
397                uint32_t idBefore = surface->generationID();
398
399                surface->writePixels(bmp, rect.fLeft, rect.fTop);
400
401                uint32_t idAfter = surface->generationID();
402                REPORTER_ASSERT(reporter, check_write(reporter, surface, surfaceInfo.alphaType(),
403                                                      bmp, rect.fLeft, rect.fTop));
404
405                // we should change the genID iff pixels were actually written.
406                SkIRect canvasRect = SkIRect::MakeSize(canvas->getBaseLayerSize());
407                SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
408                                                      bmp.width(), bmp.height());
409                bool expectSuccess = SkIRect::Intersects(canvasRect, writeRect) &&
410                                     write_should_succeed(surfaceInfo, bmp.info(), isGPU);
411                REPORTER_ASSERT(reporter, expectSuccess == (idBefore != idAfter));
412            }
413        }
414    }
415}
416
417DEF_TEST(WritePixels, reporter) {
418    const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
419    for (auto& tightRowBytes : { true, false }) {
420        const size_t rowBytes = tightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100;
421        const size_t size = info.computeByteSize(rowBytes);
422        void* pixels = sk_malloc_throw(size);
423        // if rowBytes isn't tight then set the padding to a known value
424        if (!tightRowBytes) {
425            memset(pixels, DEV_PAD, size);
426        }
427        auto surface(SkSurface::MakeRasterDirectReleaseProc(info, pixels, rowBytes,
428                                                            free_pixels, nullptr));
429        test_write_pixels(reporter, surface.get(), info);
430    }
431}
432
433static void test_write_pixels(skiatest::Reporter* reporter,
434                              GrRecordingContext* rContext,
435                              int sampleCnt) {
436    const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
437    for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
438        sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(rContext,
439                                                             SkBudgeted::kNo, ii, sampleCnt,
440                                                             origin, nullptr));
441        if (surface) {
442            test_write_pixels(reporter, surface.get(), ii);
443        }
444    }
445}
446
447DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixels_Gpu, reporter, ctxInfo) {
448    test_write_pixels(reporter, ctxInfo.directContext(), 1);
449}
450
451DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsMSAA_Gpu, reporter, ctxInfo) {
452    test_write_pixels(reporter, ctxInfo.directContext(), 1);
453}
454
455static void test_write_pixels_non_texture(skiatest::Reporter* reporter,
456                                          GrDirectContext* dContext,
457                                          int sampleCnt) {
458    // Dawn currently doesn't support writePixels to a texture-as-render-target.
459    // See http://skbug.com/10336.
460    if (GrBackendApi::kDawn == dContext->backend()) {
461        return;
462    }
463    for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
464        SkColorType colorType = kN32_SkColorType;
465        auto surface = sk_gpu_test::MakeBackendRenderTargetSurface(dContext,
466                                                                   {DEV_W, DEV_H},
467                                                                   origin,
468                                                                   sampleCnt,
469                                                                   colorType);
470        if (surface) {
471            auto ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
472            test_write_pixels(reporter, surface.get(), ii);
473        }
474    }
475}
476
477DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTexture_Gpu, reporter, ctxInfo) {
478    test_write_pixels_non_texture(reporter, ctxInfo.directContext(), 1);
479}
480
481DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTextureMSAA_Gpu, reporter, ctxInfo) {
482    test_write_pixels_non_texture(reporter, ctxInfo.directContext(), 4);
483}
484
485static sk_sp<SkSurface> create_surf(GrRecordingContext* rContext, int width, int height) {
486    const SkImageInfo ii = SkImageInfo::Make(width, height,
487                                             kRGBA_8888_SkColorType, kPremul_SkAlphaType);
488
489    sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, ii);
490    surf->flushAndSubmit();
491    return surf;
492}
493
494static sk_sp<SkImage> upload(const sk_sp<SkSurface>& surf, SkColor color) {
495    const SkImageInfo smII = SkImageInfo::Make(16, 16, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
496    SkBitmap bm;
497    bm.allocPixels(smII);
498    bm.eraseColor(color);
499
500    surf->writePixels(bm, 0, 0);
501
502    return surf->makeImageSnapshot();
503}
504
505// This is tests whether the first writePixels is completed before the
506// second writePixels takes effect (i.e., that writePixels correctly flushes
507// in between uses of the shared backing resource).
508// The unit test fails on Nexus 6P/Android M with driver 129.0 without the
509// "DisallowTexSubImageForUnormConfigTexturesEverBoundToFBO" workaround enabled.
510// skbug.com/11834
511DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsPendingIO, reporter, ctxInfo) {
512    auto context = ctxInfo.directContext();
513    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
514    const GrCaps* caps = context->priv().caps();
515
516    static const int kFullSize = 62;
517    static const int kHalfSize = 31;
518
519    static const uint32_t kLeftColor = 0xFF222222;
520    static const uint32_t kRightColor = 0xFFAAAAAA;
521
522    const SkImageInfo fullII = SkImageInfo::Make(kFullSize, kFullSize,
523                                                 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
524    const SkImageInfo halfII = SkImageInfo::Make(kHalfSize, kFullSize,
525                                                 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
526
527    sk_sp<SkSurface> dest = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, fullII);
528
529    {
530        // Seed the resource cached with a scratch texture that will be reused by writePixels
531        static constexpr SkISize kDims = {32, 64};
532
533        const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
534                                                                     GrRenderable::kNo);
535
536        sk_sp<GrTextureProxy> temp = proxyProvider->createProxy(
537                format, kDims, GrRenderable::kNo, 1, GrMipmapped::kNo, SkBackingFit::kApprox,
538                SkBudgeted::kYes, GrProtected::kNo);
539        temp->instantiate(context->priv().resourceProvider());
540    }
541
542    // Create the surfaces and flush them to ensure there is no lingering pendingIO
543    sk_sp<SkSurface> leftSurf = create_surf(context, kHalfSize, kFullSize);
544    sk_sp<SkSurface> rightSurf = create_surf(context, kHalfSize, kFullSize);
545
546    sk_sp<SkImage> leftImg = upload(std::move(leftSurf), kLeftColor);
547    dest->getCanvas()->drawImage(std::move(leftImg), 0, 0);
548
549    sk_sp<SkImage> rightImg = upload(std::move(rightSurf), kRightColor);
550    dest->getCanvas()->drawImage(std::move(rightImg), kHalfSize, 0);
551
552    SkBitmap bm;
553    bm.allocPixels(fullII);
554    SkAssertResult(dest->readPixels(bm, 0, 0));
555
556    bool isCorrect = true;
557    for (int y = 0; isCorrect && y < 16; ++y) {
558        const uint32_t* sl = bm.getAddr32(0, y);
559
560        for (int x = 0; x < 16; ++x) {
561            if (kLeftColor != sl[x]) {
562                isCorrect = false;
563                break;
564            }
565        }
566        for (int x = kHalfSize; x < kHalfSize+16; ++x) {
567            if (kRightColor != sl[x]) {
568                isCorrect = false;
569                break;
570            }
571        }
572    }
573
574    REPORTER_ASSERT(reporter, isCorrect);
575}
576
577DEF_TEST(WritePixels_InvalidRowBytes, reporter) {
578    auto dstII = SkImageInfo::Make({10, 10}, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
579    auto surf = SkSurface::MakeRaster(dstII);
580    for (int ct = 0; ct < kLastEnum_SkColorType + 1; ++ct) {
581        auto colorType = static_cast<SkColorType>(ct);
582
583        size_t bpp = SkColorTypeBytesPerPixel(colorType);
584        if (bpp <= 1) {
585            continue;
586        }
587        auto srcII = dstII.makeColorType(colorType);
588        size_t badRowBytes = (surf->width() + 1)*bpp - 1;
589        auto storage = std::make_unique<char[]>(badRowBytes*surf->height());
590        memset(storage.get(), 0, badRowBytes * surf->height());
591        // SkSurface::writePixels doesn't report bool, SkCanvas's does.
592        REPORTER_ASSERT(reporter,
593                        !surf->getCanvas()->writePixels(srcII, storage.get(), badRowBytes, 0, 0));
594    }
595}
596