Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / images / SkImageRef_ashmem.cpp
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 "SkImageRef_ashmem.h"
9 #include "SkImageDecoder.h"
10 #include "SkReadBuffer.h"
11 #include "SkWriteBuffer.h"
12 #include "SkThread.h"
13
14 #include "android/ashmem.h"
15
16 #include <sys/mman.h>
17 #include <unistd.h>
18
19 //#define TRACE_ASH_PURGE     // just trace purges
20
21 #ifdef DUMP_IMAGEREF_LIFECYCLE
22     #define DUMP_ASHMEM_LIFECYCLE
23 #else
24 //    #define DUMP_ASHMEM_LIFECYCLE
25 #endif
26
27 // ashmem likes lengths on page boundaries
28 static size_t roundToPageSize(size_t size) {
29     const size_t mask = getpagesize() - 1;
30     size_t newsize = (size + mask) & ~mask;
31 //    SkDebugf("---- oldsize %d newsize %d\n", size, newsize);
32     return newsize;
33 }
34
35 SkImageRef_ashmem::SkImageRef_ashmem(const SkImageInfo& info,
36                                      SkStreamRewindable* stream,
37                                      int sampleSize)
38     : SkImageRef(info, stream, sampleSize)
39 {
40     fRec.fFD = -1;
41     fRec.fAddr = NULL;
42     fRec.fSize = 0;
43     fRec.fPinned = false;
44
45     fCT = NULL;
46 }
47
48 SkImageRef_ashmem::~SkImageRef_ashmem() {
49     SkSafeUnref(fCT);
50     this->closeFD();
51 }
52
53 void SkImageRef_ashmem::closeFD() {
54     if (-1 != fRec.fFD) {
55 #ifdef DUMP_ASHMEM_LIFECYCLE
56         SkDebugf("=== ashmem close %d\n", fRec.fFD);
57 #endif
58         SkASSERT(fRec.fAddr);
59         SkASSERT(fRec.fSize);
60         munmap(fRec.fAddr, fRec.fSize);
61         close(fRec.fFD);
62         fRec.fFD = -1;
63     }
64 }
65
66 ///////////////////////////////////////////////////////////////////////////////
67
68 class AshmemAllocator : public SkBitmap::Allocator {
69 public:
70     AshmemAllocator(SkAshmemRec* rec, const char name[])
71         : fRec(rec), fName(name) {}
72
73     virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
74         const size_t size = roundToPageSize(bm->getSize());
75         int fd = fRec->fFD;
76         void* addr = fRec->fAddr;
77
78         SkASSERT(!fRec->fPinned);
79
80         if (-1 == fd) {
81             SkASSERT(NULL == addr);
82             SkASSERT(0 == fRec->fSize);
83
84             fd = ashmem_create_region(fName, size);
85 #ifdef DUMP_ASHMEM_LIFECYCLE
86             SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd);
87 #endif
88             if (-1 == fd) {
89                 SkDebugf("------- imageref_ashmem create failed <%s> %d\n",
90                          fName, size);
91                 return false;
92             }
93
94             int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
95             if (err) {
96                 SkDebugf("------ ashmem_set_prot_region(%d) failed %d\n",
97                          fd, err);
98                 close(fd);
99                 return false;
100             }
101
102             addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
103             if (-1 == (long)addr) {
104                 SkDebugf("---------- mmap failed for imageref_ashmem size=%d\n",
105                          size);
106                 close(fd);
107                 return false;
108             }
109
110             fRec->fFD = fd;
111             fRec->fAddr = addr;
112             fRec->fSize = size;
113         } else {
114             SkASSERT(addr);
115             SkASSERT(size == fRec->fSize);
116             (void)ashmem_pin_region(fd, 0, 0);
117         }
118
119         bm->setPixels(addr, ct);
120         fRec->fPinned = true;
121         return true;
122     }
123
124 private:
125     // we just point to our caller's memory, these are not copies
126     SkAshmemRec* fRec;
127     const char*  fName;
128 };
129
130 bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStreamRewindable* stream,
131                                  SkBitmap* bitmap, SkBitmap::Config config,
132                                  SkImageDecoder::Mode mode) {
133
134     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
135         return this->INHERITED::onDecode(codec, stream, bitmap, config, mode);
136     }
137
138     // Ashmem memory is guaranteed to be initialized to 0.
139     codec->setSkipWritingZeroes(true);
140
141     AshmemAllocator alloc(&fRec, this->getURI());
142
143     codec->setAllocator(&alloc);
144     bool success = this->INHERITED::onDecode(codec, stream, bitmap, config,
145                                              mode);
146     // remove the allocator, since its on the stack
147     codec->setAllocator(NULL);
148
149     if (success) {
150         // remember the colortable (if any)
151         SkRefCnt_SafeAssign(fCT, bitmap->getColorTable());
152         return true;
153     } else {
154         if (fRec.fPinned) {
155             ashmem_unpin_region(fRec.fFD, 0, 0);
156             fRec.fPinned = false;
157         }
158         this->closeFD();
159         return false;
160     }
161 }
162
163 bool SkImageRef_ashmem::onNewLockPixels(LockRec* rec) {
164     SkASSERT(fBitmap.getPixels() == NULL);
165     SkASSERT(fBitmap.getColorTable() == NULL);
166
167     // fast case: check if we can just pin and get the cached data
168     if (-1 != fRec.fFD) {
169         SkASSERT(fRec.fAddr);
170         SkASSERT(!fRec.fPinned);
171         int pin = ashmem_pin_region(fRec.fFD, 0, 0);
172
173         if (ASHMEM_NOT_PURGED == pin) { // yea, fast case!
174             fBitmap.setPixels(fRec.fAddr, fCT);
175             fRec.fPinned = true;
176         } else if (ASHMEM_WAS_PURGED == pin) {
177             ashmem_unpin_region(fRec.fFD, 0, 0);
178             // let go of our colortable if we lost the pixels. Well get it back
179             // again when we re-decode
180             if (fCT) {
181                 fCT->unref();
182                 fCT = NULL;
183             }
184 #if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE)
185             SkDebugf("===== ashmem purged %d\n", fBitmap.getSize());
186 #endif
187         } else {
188             SkDebugf("===== ashmem pin_region(%d) returned %d\n", fRec.fFD, pin);
189             return false;
190         }
191     } else {
192         // no FD, will create an ashmem region in allocator
193     }
194
195     return this->INHERITED::onNewLockPixels(rec);
196 }
197
198 void SkImageRef_ashmem::onUnlockPixels() {
199     this->INHERITED::onUnlockPixels();
200
201     if (-1 != fRec.fFD) {
202         SkASSERT(fRec.fAddr);
203         SkASSERT(fRec.fPinned);
204
205         ashmem_unpin_region(fRec.fFD, 0, 0);
206         fRec.fPinned = false;
207     }
208
209     // we clear this with or without an error, since we've either closed or
210     // unpinned the region
211     fBitmap.setPixels(NULL, NULL);
212 }
213
214 void SkImageRef_ashmem::flatten(SkWriteBuffer& buffer) const {
215     this->INHERITED::flatten(buffer);
216     buffer.writeString(getURI());
217 }
218
219 SkImageRef_ashmem::SkImageRef_ashmem(SkReadBuffer& buffer)
220         : INHERITED(buffer) {
221     fRec.fFD = -1;
222     fRec.fAddr = NULL;
223     fRec.fSize = 0;
224     fRec.fPinned = false;
225     fCT = NULL;
226
227     SkString uri;
228     buffer.readString(&uri);
229     this->setURI(uri);
230 }