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