1/*
2 * Copyright 2012 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 "Sk64.h"
9#include "SkLazyPixelRef.h"
10#include "SkColorTable.h"
11#include "SkData.h"
12#include "SkImageCache.h"
13#include "SkImagePriv.h"
14
15#if LAZY_CACHE_STATS
16#include "SkThread.h"
17
18int32_t SkLazyPixelRef::gCacheHits;
19int32_t SkLazyPixelRef::gCacheMisses;
20#endif
21
22SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, SkImageCache* cache)
23    // Pass NULL for the Mutex so that the default (ring buffer) will be used.
24    : INHERITED(NULL)
25    , fDecodeProc(proc)
26    , fImageCache(cache)
27    , fCacheId(SkImageCache::UNINITIALIZED_ID)
28    , fRowBytes(0) {
29    SkASSERT(fDecodeProc != NULL);
30    if (NULL == data) {
31        fData = SkData::NewEmpty();
32        fErrorInDecoding = true;
33    } else {
34        fData = data;
35        fData->ref();
36        fErrorInDecoding = data->size() == 0;
37    }
38    SkASSERT(cache != NULL);
39    cache->ref();
40    // Since this pixel ref bases its data on encoded data, it should never change.
41    this->setImmutable();
42}
43
44SkLazyPixelRef::~SkLazyPixelRef() {
45    SkASSERT(fData != NULL);
46    fData->unref();
47    SkASSERT(fImageCache);
48    if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
49        fImageCache->throwAwayCache(fCacheId);
50    }
51    fImageCache->unref();
52}
53
54static size_t ComputeMinRowBytesAndSize(const SkImage::Info& info, size_t* rowBytes) {
55    *rowBytes = SkImageMinRowBytes(info);
56
57    Sk64 safeSize;
58    safeSize.setZero();
59    if (info.fHeight > 0) {
60        safeSize.setMul(info.fHeight, SkToS32(*rowBytes));
61    }
62    SkASSERT(!safeSize.isNeg());
63    return safeSize.is32() ? safeSize.get32() : 0;
64}
65
66void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
67    if (fErrorInDecoding) {
68        return NULL;
69    }
70    SkBitmapFactory::Target target;
71    // Check to see if the pixels still exist in the cache.
72    if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
73        target.fAddr = NULL;
74    } else {
75        SkImageCache::DataStatus status;
76        target.fAddr = fImageCache->pinCache(fCacheId, &status);
77        if (target.fAddr == NULL) {
78            fCacheId = SkImageCache::UNINITIALIZED_ID;
79        } else {
80            if (SkImageCache::kRetained_DataStatus == status) {
81#if LAZY_CACHE_STATS
82                sk_atomic_inc(&gCacheHits);
83#endif
84                return target.fAddr;
85            }
86            SkASSERT(SkImageCache::kUninitialized_DataStatus == status);
87        }
88        // Cache miss. Either pinCache returned NULL or it returned a memory address without the old
89        // data
90#if LAZY_CACHE_STATS
91        sk_atomic_inc(&gCacheMisses);
92#endif
93    }
94    SkImage::Info info;
95    SkASSERT(fData != NULL && fData->size() > 0);
96    if (NULL == target.fAddr) {
97        // Determine the size of the image in order to determine how much memory to allocate.
98        // FIXME: As an optimization, only do this part once.
99        fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
100        if (fErrorInDecoding) {
101            // We can only reach here if fCacheId was already set to UNINITIALIZED_ID, or if
102            // pinCache returned NULL, in which case it was reset to UNINITIALIZED_ID.
103            SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
104            return NULL;
105        }
106
107        size_t bytes = ComputeMinRowBytesAndSize(info, &target.fRowBytes);
108        target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
109        if (NULL == target.fAddr) {
110            // Space could not be allocated.
111            // Just like the last assert, fCacheId must be UNINITIALIZED_ID.
112            SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
113            return NULL;
114        }
115    } else {
116        // pinCache returned purged memory to which target.fAddr already points. Set
117        // target.fRowBytes properly.
118        target.fRowBytes = fRowBytes;
119        // Assume that the size is correct, since it was determined by this same function
120        // previously.
121    }
122    SkASSERT(target.fAddr != NULL);
123    SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
124    fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
125    if (fErrorInDecoding) {
126        fImageCache->throwAwayCache(fCacheId);
127        fCacheId = SkImageCache::UNINITIALIZED_ID;
128        return NULL;
129    }
130    // Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory.
131    fRowBytes = target.fRowBytes;
132    return target.fAddr;
133}
134
135void SkLazyPixelRef::onUnlockPixels() {
136    if (fErrorInDecoding) {
137        return;
138    }
139    if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
140        fImageCache->releaseCache(fCacheId);
141    }
142}
143
144SkData* SkLazyPixelRef::onRefEncodedData() {
145    fData->ref();
146    return fData;
147}
148