SkDiscardablePixelRef.cpp revision 8e65712486c66108677a9b0a55ad3e7ca94db555
1/*
2 * Copyright 2013 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 "SkDiscardablePixelRef.h"
9#include "SkDiscardableMemory.h"
10#include "SkImageGenerator.h"
11
12SkDiscardablePixelRef::SkDiscardablePixelRef(const SkImageInfo& info,
13                                             SkImageGenerator* generator,
14                                             size_t rowBytes,
15                                             SkDiscardableMemory::Factory* fact)
16    : INHERITED(info)
17    , fGenerator(generator)
18    , fDMFactory(fact)
19    , fRowBytes(rowBytes)
20    , fDiscardableMemory(NULL)
21    , fDiscardableMemoryIsLocked(false)
22{
23    SkASSERT(fGenerator != NULL);
24    SkASSERT(fRowBytes > 0);
25    // The SkImageGenerator contract requires fGenerator to always
26    // decode the same image on each call to getPixels().
27    this->setImmutable();
28    SkSafeRef(fDMFactory);
29}
30
31SkDiscardablePixelRef::~SkDiscardablePixelRef() {
32    if (fDiscardableMemoryIsLocked) {
33        fDiscardableMemory->unlock();
34        fDiscardableMemoryIsLocked = false;
35    }
36    SkDELETE(fDiscardableMemory);
37    SkSafeUnref(fDMFactory);
38    SkDELETE(fGenerator);
39}
40
41bool SkDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
42    if (fDiscardableMemory != NULL) {
43        if (fDiscardableMemory->lock()) {
44            fDiscardableMemoryIsLocked = true;
45            rec->fPixels = fDiscardableMemory->data();
46            rec->fColorTable = fCTable.get();
47            rec->fRowBytes = fRowBytes;
48            return true;
49        }
50        SkDELETE(fDiscardableMemory);
51        fDiscardableMemory = NULL;
52        fDiscardableMemoryIsLocked = false;
53    }
54
55    const size_t size = this->info().getSafeSize(fRowBytes);
56
57    if (fDMFactory != NULL) {
58        fDiscardableMemory = fDMFactory->create(size);
59        fDiscardableMemoryIsLocked = true;
60    } else {
61        fDiscardableMemory = SkDiscardableMemory::Create(size);
62        fDiscardableMemoryIsLocked = true;
63    }
64    if (NULL == fDiscardableMemory) {
65        fDiscardableMemoryIsLocked = false;
66        return false;  // Memory allocation failed.
67    }
68
69    void* pixels = fDiscardableMemory->data();
70    const SkImageInfo& info = this->info();
71    SkPMColor colors[256];
72    int colorCount = 0;
73
74    const SkImageGenerator::Result result = fGenerator->getPixels(info, pixels, fRowBytes,
75                                                                  colors, &colorCount);
76    switch (result) {
77        case SkImageGenerator::kSuccess:
78        case SkImageGenerator::kIncompleteInput:
79            break;
80        default:
81            fDiscardableMemory->unlock();
82            fDiscardableMemoryIsLocked = false;
83            SkDELETE(fDiscardableMemory);
84            fDiscardableMemory = NULL;
85            return false;
86    }
87
88    // Note: our ctable is not purgeable, as it is not stored in the discardablememory block.
89    // This is because SkColorTable is refcntable, and therefore our caller could hold onto it
90    // beyond the scope of a lock/unlock. If we change the API/lifecycle for SkColorTable, we
91    // could move it into the block, but then again perhaps it is small enough that this doesn't
92    // really matter.
93    if (colorCount > 0) {
94        fCTable.reset(SkNEW_ARGS(SkColorTable, (colors, colorCount)));
95    } else {
96        fCTable.reset(NULL);
97    }
98
99    rec->fPixels = pixels;
100    rec->fColorTable = fCTable.get();
101    rec->fRowBytes = fRowBytes;
102    return true;
103}
104
105void SkDiscardablePixelRef::onUnlockPixels() {
106    fDiscardableMemory->unlock();
107    fDiscardableMemoryIsLocked = false;
108}
109
110bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst,
111                                  SkDiscardableMemory::Factory* factory) {
112    SkImageInfo info;
113    SkAutoTDelete<SkImageGenerator> autoGenerator(generator);
114    if ((NULL == autoGenerator.get())
115        || (!autoGenerator->getInfo(&info))
116        || info.isEmpty()
117        || (!dst->setInfo(info))) {
118        return false;
119    }
120    // Since dst->setInfo() may have changed/fixed-up info, we copy it back from that bitmap
121    info = dst->info();
122
123    SkASSERT(info.colorType() != kUnknown_SkColorType);
124    if (dst->empty()) {  // Use a normal pixelref.
125        return dst->tryAllocPixels();
126    }
127    SkAutoTUnref<SkDiscardablePixelRef> ref(
128        SkNEW_ARGS(SkDiscardablePixelRef,
129                   (info, autoGenerator.detach(), dst->rowBytes(), factory)));
130    dst->setPixelRef(ref);
131    return true;
132}
133
134// These are the public API
135
136bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst) {
137    return SkInstallDiscardablePixelRef(generator, dst, NULL);
138}
139
140bool SkInstallDiscardablePixelRef(SkData* encoded, SkBitmap* dst) {
141    SkImageGenerator* generator = SkImageGenerator::NewFromData(encoded);
142    return generator ? SkInstallDiscardablePixelRef(generator, dst, NULL) : false;
143}
144