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 "SkBitmap.h"
9#include "SkRect.h"
10#include "SkTemplates.h"
11#include "Test.h"
12#include "sk_tool_utils.h"
13
14static void init_src(const SkBitmap& bitmap) {
15    if (bitmap.getPixels()) {
16        bitmap.eraseColor(SK_ColorWHITE);
17    }
18}
19
20struct Pair {
21    SkColorType fColorType;
22    const char* fValid;
23};
24
25// Utility functions for copyPixelsTo()/copyPixelsFrom() tests.
26// getPixel()
27// setPixel()
28// getSkConfigName()
29// struct Coordinates
30// reportCopyVerification()
31// writeCoordPixels()
32
33// Helper struct to contain pixel locations, while avoiding need for STL.
34struct Coordinates {
35
36    const int length;
37    SkIPoint* const data;
38
39    explicit Coordinates(int _length): length(_length)
40                                     , data(new SkIPoint[length]) { }
41
42    ~Coordinates(){
43        delete [] data;
44    }
45
46    SkIPoint* operator[](int i) const {
47        // Use with care, no bounds checking.
48        return data + i;
49    }
50};
51
52static const Pair gPairs[] = {
53    { kUnknown_SkColorType,     "0000000"  },
54    { kAlpha_8_SkColorType,     "0100000"  },
55    { kRGB_565_SkColorType,     "0101011"  },
56    { kARGB_4444_SkColorType,   "0101111"  },
57    { kN32_SkColorType,         "0101111"  },
58    { kRGBA_F16_SkColorType,    "0101011"  },
59};
60
61static const int W = 20;
62static const int H = 33;
63
64static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul,
65                              SkColorType ct) {
66    sk_sp<SkColorSpace> colorSpace = nullptr;
67    if (kRGBA_F16_SkColorType == ct) {
68        colorSpace = SkColorSpace::MakeSRGBLinear();
69    }
70
71    srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace));
72    srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace));
73    init_src(*srcOpaque);
74    init_src(*srcPremul);
75}
76
77DEF_TEST(BitmapCopy_extractSubset, reporter) {
78    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
79        SkBitmap srcOpaque, srcPremul;
80        setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType);
81
82        SkBitmap bitmap(srcOpaque);
83        SkBitmap subset;
84        SkIRect r;
85        // Extract a subset which has the same width as the original. This
86        // catches a bug where we cloned the genID incorrectly.
87        r.set(0, 1, W, 3);
88        bitmap.setIsVolatile(true);
89        // Relies on old behavior of extractSubset failing if colortype is unknown
90        if (kUnknown_SkColorType != bitmap.colorType() && bitmap.extractSubset(&subset, r)) {
91            REPORTER_ASSERT(reporter, subset.width() == W);
92            REPORTER_ASSERT(reporter, subset.height() == 2);
93            REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType());
94            REPORTER_ASSERT(reporter, subset.isVolatile() == true);
95
96            // Test copying an extracted subset.
97            for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
98                SkBitmap copy;
99                bool success = sk_tool_utils::copy_to(&copy, gPairs[j].fColorType, subset);
100                if (!success) {
101                    // Skip checking that success matches fValid, which is redundant
102                    // with the code below.
103                    REPORTER_ASSERT(reporter, gPairs[i].fColorType != gPairs[j].fColorType);
104                    continue;
105                }
106
107                // When performing a copy of an extracted subset, the gen id should
108                // change.
109                REPORTER_ASSERT(reporter, copy.getGenerationID() != subset.getGenerationID());
110
111                REPORTER_ASSERT(reporter, copy.width() == W);
112                REPORTER_ASSERT(reporter, copy.height() == 2);
113            }
114        }
115
116        bitmap = srcPremul;
117        bitmap.setIsVolatile(false);
118        if (bitmap.extractSubset(&subset, r)) {
119            REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType());
120            REPORTER_ASSERT(reporter, subset.isVolatile() == false);
121        }
122    }
123}
124
125#include "SkColorPriv.h"
126#include "SkUtils.h"
127
128/**
129 *  Construct 4x4 pixels where we can look at a color and determine where it should be in the grid.
130 *  alpha = 0xFF, blue = 0x80, red = x, green = y
131 */
132static void fill_4x4_pixels(SkPMColor colors[16]) {
133    for (int y = 0; y < 4; ++y) {
134        for (int x = 0; x < 4; ++x) {
135            colors[y*4+x] = SkPackARGB32(0xFF, x, y, 0x80);
136        }
137    }
138}
139
140static bool check_4x4_pixel(SkPMColor color, unsigned x, unsigned y) {
141    SkASSERT(x < 4 && y < 4);
142    return  0xFF == SkGetPackedA32(color) &&
143            x    == SkGetPackedR32(color) &&
144            y    == SkGetPackedG32(color) &&
145            0x80 == SkGetPackedB32(color);
146}
147
148/**
149 *  Fill with all zeros, which will never match any value from fill_4x4_pixels
150 */
151static void clear_4x4_pixels(SkPMColor colors[16]) {
152    sk_memset32(colors, 0, 16);
153}
154
155// Much of readPixels is exercised by copyTo testing, since readPixels is the backend for that
156// method. Here we explicitly test subset copies.
157//
158DEF_TEST(BitmapReadPixels, reporter) {
159    const int W = 4;
160    const int H = 4;
161    const size_t rowBytes = W * sizeof(SkPMColor);
162    const SkImageInfo srcInfo = SkImageInfo::MakeN32Premul(W, H);
163    SkPMColor srcPixels[16];
164    fill_4x4_pixels(srcPixels);
165    SkBitmap srcBM;
166    srcBM.installPixels(srcInfo, srcPixels, rowBytes);
167
168    SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(W, H);
169    SkPMColor dstPixels[16];
170
171    const struct {
172        bool     fExpectedSuccess;
173        SkIPoint fRequestedSrcLoc;
174        SkISize  fRequestedDstSize;
175        // If fExpectedSuccess, check these, otherwise ignore
176        SkIPoint fExpectedDstLoc;
177        SkIRect  fExpectedSrcR;
178    } gRec[] = {
179        { true,  { 0, 0 }, { 4, 4 }, { 0, 0 }, { 0, 0, 4, 4 } },
180        { true,  { 1, 1 }, { 2, 2 }, { 0, 0 }, { 1, 1, 3, 3 } },
181        { true,  { 2, 2 }, { 4, 4 }, { 0, 0 }, { 2, 2, 4, 4 } },
182        { true,  {-1,-1 }, { 2, 2 }, { 1, 1 }, { 0, 0, 1, 1 } },
183        { false, {-1,-1 }, { 1, 1 }, { 0, 0 }, { 0, 0, 0, 0 } },
184    };
185
186    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
187        clear_4x4_pixels(dstPixels);
188
189        dstInfo = dstInfo.makeWH(gRec[i].fRequestedDstSize.width(),
190                                 gRec[i].fRequestedDstSize.height());
191        bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes,
192                                        gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y());
193
194        REPORTER_ASSERT(reporter, gRec[i].fExpectedSuccess == success);
195        if (success) {
196            const SkIRect srcR = gRec[i].fExpectedSrcR;
197            const int dstX = gRec[i].fExpectedDstLoc.x();
198            const int dstY = gRec[i].fExpectedDstLoc.y();
199            // Walk the dst pixels, and check if we got what we expected
200            for (int y = 0; y < H; ++y) {
201                for (int x = 0; x < W; ++x) {
202                    SkPMColor dstC = dstPixels[y*4+x];
203                    // get into src coordinates
204                    int sx = x - dstX + srcR.x();
205                    int sy = y - dstY + srcR.y();
206                    if (srcR.contains(sx, sy)) {
207                        REPORTER_ASSERT(reporter, check_4x4_pixel(dstC, sx, sy));
208                    } else {
209                        REPORTER_ASSERT(reporter, 0 == dstC);
210                    }
211                }
212            }
213        }
214    }
215}
216