skia2/include/core/SkPixelRef.h
mtklein 0b544ae222 Add SkRacy
SkRacy<T> is a zero-overhead wrapper for a T, except it also
silences race warnings when TSAN is running.

Here we apply in several classes.  In SkMatrix and SkPathRef,
we use it to opportunistically cache some idempotent work.

In SkPixelRef, we wrap the genIDs.  We think the worst that
can happen here is we'll increment the global next-genID a
few times instead of once when we go to get another ID.

BUG=skia:

Committed: https://skia.googlesource.com/skia/+/d5e3e6ae1b3434ad1158f441902ff65f1eeaa3a7

CQ_EXTRA_TRYBOTS=tryserver.skia:Canary-Chrome-Ubuntu13.10-Ninja-x86_64-ToT-Trybot,Canary-Chrome-Win7-Ninja-x86-SharedLib_ToT-Trybot,Test-Ubuntu13.10-GCE-NoGPU-x86_64-Release-TSAN-Trybot
R=reed@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/371363004
2014-07-08 19:37:47 -07:00

394 lines
13 KiB
C++

/*
* Copyright 2008 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkPixelRef_DEFINED
#define SkPixelRef_DEFINED
#include "SkBitmap.h"
#include "SkDynamicAnnotations.h"
#include "SkRefCnt.h"
#include "SkString.h"
#include "SkFlattenable.h"
#include "SkImageInfo.h"
#include "SkTDArray.h"
//#define xed
#ifdef SK_DEBUG
/**
* Defining SK_IGNORE_PIXELREF_SETPRELOCKED will force all pixelref
* subclasses to correctly handle lock/unlock pixels. For performance
* reasons, simple malloc-based subclasses call setPreLocked() to skip
* the overhead of implementing these calls.
*
* This build-flag disables that optimization, to add in debugging our
* call-sites, to ensure that they correctly balance their calls of
* lock and unlock.
*/
// #define SK_IGNORE_PIXELREF_SETPRELOCKED
#endif
#ifdef SK_SUPPORT_LEGACY_PIXELREF_UNFLATTENABLE
// we only support unflattening, not flattening
#define SK_PIXELREF_BASECLASS SkFlattenable
#else
#define SK_PIXELREF_BASECLASS SkRefCnt
#endif
class SkColorTable;
class SkData;
struct SkIRect;
class SkMutex;
class GrTexture;
/** \class SkPixelRef
This class is the smart container for pixel memory, and is used with
SkBitmap. A pixelref is installed into a bitmap, and then the bitmap can
access the actual pixel memory by calling lockPixels/unlockPixels.
This class can be shared/accessed between multiple threads.
*/
class SK_API SkPixelRef : public SK_PIXELREF_BASECLASS {
public:
SK_DECLARE_INST_COUNT(SkPixelRef)
explicit SkPixelRef(const SkImageInfo&);
SkPixelRef(const SkImageInfo&, SkBaseMutex* mutex);
virtual ~SkPixelRef();
const SkImageInfo& info() const {
return fInfo;
}
/** Return the pixel memory returned from lockPixels, or null if the
lockCount is 0.
*/
void* pixels() const { return fRec.fPixels; }
/** Return the current colorTable (if any) if pixels are locked, or null.
*/
SkColorTable* colorTable() const { return fRec.fColorTable; }
size_t rowBytes() const { return fRec.fRowBytes; }
/**
* To access the actual pixels of a pixelref, it must be "locked".
* Calling lockPixels returns a LockRec struct (on success).
*/
struct LockRec {
void* fPixels;
SkColorTable* fColorTable;
size_t fRowBytes;
void zero() { sk_bzero(this, sizeof(*this)); }
bool isZero() const {
return NULL == fPixels && NULL == fColorTable && 0 == fRowBytes;
}
};
/**
* Returns true if the lockcount > 0
*/
bool isLocked() const { return fLockCount > 0; }
SkDEBUGCODE(int getLockCount() const { return fLockCount; })
/**
* Call to access the pixel memory. Return true on success. Balance this
* with a call to unlockPixels().
*/
bool lockPixels();
/**
* Call to access the pixel memory. On success, return true and fill out
* the specified rec. On failure, return false and ignore the rec parameter.
* Balance this with a call to unlockPixels().
*/
bool lockPixels(LockRec* rec);
/** Call to balanace a previous call to lockPixels(). Returns the pixels
(or null) after the unlock. NOTE: lock calls can be nested, but the
matching number of unlock calls must be made in order to free the
memory (if the subclass implements caching/deferred-decoding.)
*/
void unlockPixels();
/**
* Some bitmaps can return a copy of their pixels for lockPixels(), but
* that copy, if modified, will not be pushed back. These bitmaps should
* not be used as targets for a raster device/canvas (since all pixels
* modifications will be lost when unlockPixels() is called.)
*/
bool lockPixelsAreWritable() const;
/** Returns a non-zero, unique value corresponding to the pixels in this
pixelref. Each time the pixels are changed (and notifyPixelsChanged is
called), a different generation ID will be returned.
*/
uint32_t getGenerationID() const;
/**
* Call this if you have changed the contents of the pixels. This will in-
* turn cause a different generation ID value to be returned from
* getGenerationID().
*/
void notifyPixelsChanged();
/**
* Change the info's AlphaType. Note that this does not automatically
* invalidate the generation ID. If the pixel values themselves have
* changed, then you must explicitly call notifyPixelsChanged() as well.
*/
void changeAlphaType(SkAlphaType at);
/** Returns true if this pixelref is marked as immutable, meaning that the
contents of its pixels will not change for the lifetime of the pixelref.
*/
bool isImmutable() const { return fIsImmutable; }
/** Marks this pixelref is immutable, meaning that the contents of its
pixels will not change for the lifetime of the pixelref. This state can
be set on a pixelref, but it cannot be cleared once it is set.
*/
void setImmutable();
/** Return the optional URI string associated with this pixelref. May be
null.
*/
const char* getURI() const { return fURI.size() ? fURI.c_str() : NULL; }
/** Copy a URI string to this pixelref, or clear the URI if the uri is null
*/
void setURI(const char uri[]) {
fURI.set(uri);
}
/** Copy a URI string to this pixelref
*/
void setURI(const char uri[], size_t len) {
fURI.set(uri, len);
}
/** Assign a URI string to this pixelref.
*/
void setURI(const SkString& uri) { fURI = uri; }
/**
* If the pixelRef has an encoded (i.e. compressed) representation,
* return a ref to its data. If the pixelRef
* is uncompressed or otherwise does not have this form, return NULL.
*
* If non-null is returned, the caller is responsible for calling unref()
* on the data when it is finished.
*/
SkData* refEncodedData() {
return this->onRefEncodedData();
}
/**
* Experimental -- tells the caller if it is worth it to call decodeInto().
* Just an optimization at this point, to avoid checking the cache first.
* We may remove/change this call in the future.
*/
bool implementsDecodeInto() {
return this->onImplementsDecodeInto();
}
/**
* Return a decoded instance of this pixelRef in bitmap. If this cannot be
* done, return false and the bitmap parameter is ignored/unchanged.
*
* pow2 is the requeste power-of-two downscale that the caller needs. This
* can be ignored, and the "original" size can be returned, but if the
* underlying codec can efficiently return a smaller size, that should be
* done. Some examples:
*
* To request the "base" version (original scale), pass 0 for pow2
* To request 1/2 scale version (1/2 width, 1/2 height), pass 1 for pow2
* To request 1/4 scale version (1/4 width, 1/4 height), pass 2 for pow2
* ...
*
* If this returns true, then bitmap must be "locked" such that
* bitmap->getPixels() will return the correct address.
*/
bool decodeInto(int pow2, SkBitmap* bitmap) {
SkASSERT(pow2 >= 0);
return this->onDecodeInto(pow2, bitmap);
}
/** Are we really wrapping a texture instead of a bitmap?
*/
virtual GrTexture* getTexture() { return NULL; }
bool readPixels(SkBitmap* dst, const SkIRect* subset = NULL);
/**
* Makes a deep copy of this PixelRef, respecting the requested config.
* @param colorType Desired colortype.
* @param subset Subset of this PixelRef to copy. Must be fully contained within the bounds of
* of this PixelRef.
* @return A new SkPixelRef, or NULL if either there is an error (e.g. the destination could
* not be created with the given config), or this PixelRef does not support deep
* copies.
*/
virtual SkPixelRef* deepCopy(SkColorType colortype, const SkIRect* subset) {
return NULL;
}
#ifdef SK_BUILD_FOR_ANDROID
/**
* Acquire a "global" ref on this object.
* The default implementation just calls ref(), but subclasses can override
* this method to implement additional behavior.
*/
virtual void globalRef(void* data=NULL);
/**
* Release a "global" ref on this object.
* The default implementation just calls unref(), but subclasses can override
* this method to implement additional behavior.
*/
virtual void globalUnref();
#endif
#ifdef SK_SUPPORT_LEGACY_PIXELREF_UNFLATTENABLE
SK_DEFINE_FLATTENABLE_TYPE(SkPixelRef)
#endif
// Register a listener that may be called the next time our generation ID changes.
//
// We'll only call the listener if we're confident that we are the only SkPixelRef with this
// generation ID. If our generation ID changes and we decide not to call the listener, we'll
// never call it: you must add a new listener for each generation ID change. We also won't call
// the listener when we're certain no one knows what our generation ID is.
//
// This can be used to invalidate caches keyed by SkPixelRef generation ID.
struct GenIDChangeListener {
virtual ~GenIDChangeListener() {}
virtual void onChange() = 0;
};
// Takes ownership of listener.
void addGenIDChangeListener(GenIDChangeListener* listener);
protected:
/**
* On success, returns true and fills out the LockRec for the pixels. On
* failure returns false and ignores the LockRec parameter.
*
* The caller will have already acquired a mutex for thread safety, so this
* method need not do that.
*/
virtual bool onNewLockPixels(LockRec*) = 0;
/**
* Balancing the previous successful call to onNewLockPixels. The locked
* pixel address will no longer be referenced, so the subclass is free to
* move or discard that memory.
*
* The caller will have already acquired a mutex for thread safety, so this
* method need not do that.
*/
virtual void onUnlockPixels() = 0;
/** Default impl returns true */
virtual bool onLockPixelsAreWritable() const;
// returns false;
virtual bool onImplementsDecodeInto();
// returns false;
virtual bool onDecodeInto(int pow2, SkBitmap* bitmap);
/**
* For pixelrefs that don't have access to their raw pixels, they may be
* able to make a copy of them (e.g. if the pixels are on the GPU).
*
* The base class implementation returns false;
*/
virtual bool onReadPixels(SkBitmap* dst, const SkIRect* subsetOrNull);
// default impl returns NULL.
virtual SkData* onRefEncodedData();
/**
* Returns the size (in bytes) of the internally allocated memory.
* This should be implemented in all serializable SkPixelRef derived classes.
* SkBitmap::fPixelRefOffset + SkBitmap::getSafeSize() should never overflow this value,
* otherwise the rendering code may attempt to read memory out of bounds.
*
* @return default impl returns 0.
*/
virtual size_t getAllocatedSizeInBytes() const;
/** Return the mutex associated with this pixelref. This value is assigned
in the constructor, and cannot change during the lifetime of the object.
*/
SkBaseMutex* mutex() const { return fMutex; }
// serialization
SkPixelRef(SkReadBuffer&, SkBaseMutex*);
// only call from constructor. Flags this to always be locked, removing
// the need to grab the mutex and call onLockPixels/onUnlockPixels.
// Performance tweak to avoid those calls (esp. in multi-thread use case).
void setPreLocked(void*, size_t rowBytes, SkColorTable*);
private:
SkBaseMutex* fMutex; // must remain in scope for the life of this object
// mostly const. fInfo.fAlpahType can be changed at runtime.
const SkImageInfo fInfo;
// LockRec is only valid if we're in a locked state (isLocked())
LockRec fRec;
int fLockCount;
mutable SkTRacy<uint32_t> fGenerationID;
mutable SkTRacy<bool> fUniqueGenerationID;
SkTDArray<GenIDChangeListener*> fGenIDChangeListeners; // pointers are owned
SkString fURI;
// can go from false to true, but never from true to false
bool fIsImmutable;
// only ever set in constructor, const after that
bool fPreLocked;
void needsNewGenID();
void callGenIDChangeListeners();
void setMutex(SkBaseMutex* mutex);
// When copying a bitmap to another with the same shape and config, we can safely
// clone the pixelref generation ID too, which makes them equivalent under caching.
friend class SkBitmap; // only for cloneGenID
void cloneGenID(const SkPixelRef&);
#ifdef SK_SUPPORT_LEGACY_PIXELREF_UNFLATTENABLE
virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE { sk_throw(); }
#endif
typedef SK_PIXELREF_BASECLASS INHERITED;
};
class SkPixelRefFactory : public SkRefCnt {
public:
/**
* Allocate a new pixelref matching the specified ImageInfo, allocating
* the memory for the pixels. If the ImageInfo requires a ColorTable,
* the pixelref will ref() the colortable.
* On failure return NULL.
*/
virtual SkPixelRef* create(const SkImageInfo&, size_t rowBytes, SkColorTable*) = 0;
};
#endif