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 "SkBitmap.h"
9#include "SkBitmapCache.h"
10#include "SkImage_Base.h"
11#include "SkImageCacherator.h"
12#include "SkMallocPixelRef.h"
13#include "SkNextID.h"
14#include "SkPixelRef.h"
15#include "SkResourceCache.h"
16
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
19#include "GrGpuResourcePriv.h"
20#include "GrImageIDTextureAdjuster.h"
21#include "GrResourceKey.h"
22#include "GrTextureParams.h"
23#include "GrYUVProvider.h"
24#include "SkGr.h"
25#include "SkGrPriv.h"
26#endif
27
28SkImageCacherator* SkImageCacherator::NewFromGenerator(SkImageGenerator* gen,
29                                                       const SkIRect* subset) {
30    if (!gen) {
31        return nullptr;
32    }
33
34    // We are required to take ownership of gen, regardless of if we return a cacherator or not
35    SkAutoTDelete<SkImageGenerator> genHolder(gen);
36
37    const SkImageInfo& info = gen->getInfo();
38    if (info.isEmpty()) {
39        return nullptr;
40    }
41
42    uint32_t uniqueID = gen->uniqueID();
43    const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
44    if (subset) {
45        if (!bounds.contains(*subset)) {
46            return nullptr;
47        }
48        if (*subset != bounds) {
49            // we need a different uniqueID since we really are a subset of the raw generator
50            uniqueID = SkNextID::ImageID();
51        }
52    } else {
53        subset = &bounds;
54    }
55
56    // Now that we know we can hand-off the generator (to be owned by the cacherator) we can
57    // release our holder. (we DONT want to delete it here anymore)
58    genHolder.detach();
59
60    return new SkImageCacherator(gen, gen->getInfo().makeWH(subset->width(), subset->height()),
61                                 SkIPoint::Make(subset->x(), subset->y()), uniqueID);
62}
63
64SkImageCacherator::SkImageCacherator(SkImageGenerator* gen, const SkImageInfo& info,
65                                     const SkIPoint& origin, uint32_t uniqueID)
66    : fNotThreadSafeGenerator(gen)
67    , fInfo(info)
68    , fOrigin(origin)
69    , fUniqueID(uniqueID)
70{}
71
72SkData* SkImageCacherator::refEncoded(GrContext* ctx) {
73    ScopedGenerator generator(this);
74    return generator->refEncodedData(ctx);
75}
76
77static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
78    SkASSERT(bitmap.getGenerationID() == expectedID);
79    SkASSERT(bitmap.isImmutable());
80    SkASSERT(bitmap.getPixels());
81    return true;
82}
83
84// Note, this returns a new, mutable, bitmap, with a new genID.
85// If you want the immutable bitmap with the same ID as our cacherator, call tryLockAsBitmap()
86//
87bool SkImageCacherator::generateBitmap(SkBitmap* bitmap) {
88    SkBitmap::Allocator* allocator = SkResourceCache::GetAllocator();
89
90    ScopedGenerator generator(this);
91    const SkImageInfo& genInfo = generator->getInfo();
92    if (fInfo.dimensions() == genInfo.dimensions()) {
93        SkASSERT(fOrigin.x() == 0 && fOrigin.y() == 0);
94        // fast-case, no copy needed
95        return generator->tryGenerateBitmap(bitmap, fInfo, allocator);
96    } else {
97        // need to handle subsetting, so we first generate the full size version, and then
98        // "read" from it to get our subset. See https://bug.skia.org/4213
99
100        SkBitmap full;
101        if (!generator->tryGenerateBitmap(&full, genInfo, allocator)) {
102            return false;
103        }
104        if (!bitmap->tryAllocPixels(fInfo, nullptr, full.getColorTable())) {
105            return false;
106        }
107        return full.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
108                               fOrigin.x(), fOrigin.y());
109    }
110}
111
112bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
113                                             int srcX, int srcY) {
114    ScopedGenerator generator(this);
115    const SkImageInfo& genInfo = generator->getInfo();
116    // Currently generators do not natively handle subsets, so check that first.
117    if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
118        return false;
119    }
120    return generator->getPixels(info, pixels, rb);
121}
122
123//////////////////////////////////////////////////////////////////////////////////////////////////
124
125bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap) {
126    return SkBitmapCache::Find(fUniqueID, bitmap) && check_output_bitmap(*bitmap, fUniqueID);
127}
128
129bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
130                                        SkImage::CachingHint chint) {
131    if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap)) {
132        return true;
133    }
134    if (!this->generateBitmap(bitmap)) {
135        return false;
136    }
137
138    bitmap->pixelRef()->setImmutableWithID(fUniqueID);
139    if (SkImage::kAllow_CachingHint == chint) {
140        SkBitmapCache::Add(fUniqueID, *bitmap);
141        if (client) {
142            as_IB(client)->notifyAddedToCache();
143        }
144    }
145    return true;
146}
147
148bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
149                                     SkImage::CachingHint chint) {
150    if (this->tryLockAsBitmap(bitmap, client, chint)) {
151        return check_output_bitmap(*bitmap, fUniqueID);
152    }
153
154#if SK_SUPPORT_GPU
155    // Try to get a texture and read it back to raster (and then cache that with our ID)
156    SkAutoTUnref<GrTexture> tex;
157
158    {
159        ScopedGenerator generator(this);
160        SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
161        tex.reset(generator->generateTexture(nullptr, &subset));
162    }
163    if (!tex) {
164        bitmap->reset();
165        return false;
166    }
167
168    if (!bitmap->tryAllocPixels(fInfo)) {
169        bitmap->reset();
170        return false;
171    }
172
173    const uint32_t pixelOpsFlags = 0;
174    if (!tex->readPixels(0, 0, bitmap->width(), bitmap->height(), SkImageInfo2GrPixelConfig(fInfo),
175                         bitmap->getPixels(), bitmap->rowBytes(), pixelOpsFlags)) {
176        bitmap->reset();
177        return false;
178    }
179
180    bitmap->pixelRef()->setImmutableWithID(fUniqueID);
181    if (SkImage::kAllow_CachingHint == chint) {
182        SkBitmapCache::Add(fUniqueID, *bitmap);
183        if (client) {
184            as_IB(client)->notifyAddedToCache();
185        }
186    }
187    return check_output_bitmap(*bitmap, fUniqueID);
188#else
189    return false;
190#endif
191}
192
193//////////////////////////////////////////////////////////////////////////////////////////////////
194
195#if SK_SUPPORT_GPU
196
197static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrSurfaceDesc desc) {
198    const void* rawStart;
199    GrPixelConfig config = GrIsCompressedTextureDataSupported(ctx, data, desc.fWidth, desc.fHeight,
200                                                              &rawStart);
201    if (kUnknown_GrPixelConfig == config) {
202        return nullptr;
203    }
204
205    desc.fConfig = config;
206    return ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes, rawStart, 0);
207}
208
209class Generator_GrYUVProvider : public GrYUVProvider {
210    SkImageGenerator* fGen;
211
212public:
213    Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
214
215    uint32_t onGetID() override { return fGen->uniqueID(); }
216    bool onGetYUVSizes(SkISize sizes[3]) override {
217        return fGen->getYUV8Planes(sizes, nullptr, nullptr, nullptr);
218    }
219    bool onGetYUVPlanes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
220                        SkYUVColorSpace* space) override {
221        return fGen->getYUV8Planes(sizes, planes, rowBytes, space);
222    }
223};
224
225static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) {
226    if (key.isValid()) {
227        tex->resourcePriv().setUniqueKey(key);
228    }
229    return tex;
230}
231
232/*
233 *  We have a 5 ways to try to return a texture (in sorted order)
234 *
235 *  1. Check the cache for a pre-existing one
236 *  2. Ask the generator to natively create one
237 *  3. Ask the generator to return a compressed form that the GPU might support
238 *  4. Ask the generator to return YUV planes, which the GPU can convert
239 *  5. Ask the generator to return RGB(A) data, which the GPU can convert
240 */
241GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key,
242                                          const SkImage* client, SkImage::CachingHint chint) {
243    // Values representing the various texture lock paths we can take. Used for logging the path
244    // taken to a histogram.
245    enum LockTexturePath {
246        kFailure_LockTexturePath,
247        kPreExisting_LockTexturePath,
248        kNative_LockTexturePath,
249        kCompressed_LockTexturePath,
250        kYUV_LockTexturePath,
251        kRGBA_LockTexturePath,
252    };
253
254    enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
255
256    // 1. Check the cache for a pre-existing one
257    if (key.isValid()) {
258        if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
259            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
260                                     kLockTexturePathCount);
261            return tex;
262        }
263    }
264
265    // 2. Ask the generator to natively create one
266    {
267        ScopedGenerator generator(this);
268        SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
269        if (GrTexture* tex = generator->generateTexture(ctx, &subset)) {
270            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
271                                     kLockTexturePathCount);
272            return set_key_and_return(tex, key);
273        }
274    }
275
276    const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(fInfo);
277
278    // 3. Ask the generator to return a compressed form that the GPU might support
279    SkAutoTUnref<SkData> data(this->refEncoded(ctx));
280    if (data) {
281        GrTexture* tex = load_compressed_into_texture(ctx, data, desc);
282        if (tex) {
283            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kCompressed_LockTexturePath,
284                                     kLockTexturePathCount);
285            return set_key_and_return(tex, key);
286        }
287    }
288
289    // 4. Ask the generator to return YUV planes, which the GPU can convert
290    {
291        ScopedGenerator generator(this);
292        Generator_GrYUVProvider provider(generator);
293        GrTexture* tex = provider.refAsTexture(ctx, desc, true);
294        if (tex) {
295            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
296                                     kLockTexturePathCount);
297            return set_key_and_return(tex, key);
298        }
299    }
300
301    // 5. Ask the generator to return RGB(A) data, which the GPU can convert
302    SkBitmap bitmap;
303    if (this->tryLockAsBitmap(&bitmap, client, chint)) {
304        GrTexture* tex = GrUploadBitmapToTexture(ctx, bitmap);
305        if (tex) {
306            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
307                                     kLockTexturePathCount);
308            return set_key_and_return(tex, key);
309        }
310    }
311    SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
312                             kLockTexturePathCount);
313    return nullptr;
314}
315
316///////////////////////////////////////////////////////////////////////////////////////////////////
317
318GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParams& params,
319                                            const SkImage* client, SkImage::CachingHint chint) {
320    if (!ctx) {
321        return nullptr;
322    }
323
324    return GrImageTextureMaker(ctx, this, client, chint).refTextureForParams(params);
325}
326
327#else
328
329GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParams&,
330                                            const SkImage* client, SkImage::CachingHint) {
331    return nullptr;
332}
333
334#endif
335