/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_HWUI_PATH_CACHE_H
#define ANDROID_HWUI_PATH_CACHE_H
#include <GLES2/gl2.h>
#include <utils/LruCache.h>
#include <utils/Mutex.h>
#include <utils/Vector.h>
#include "Debug.h"
#include "Properties.h"
#include "Texture.h"
#include "utils/Pair.h"
class SkBitmap;
class SkCanvas;
class SkPaint;
class SkPath;
class SkRect;
namespace android {
namespace uirenderer {
class Caches;
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
// Debug
#if DEBUG_PATHS
#define PATH_LOGD(...) ALOGD(__VA_ARGS__)
#else
#define PATH_LOGD(...)
#endif
///////////////////////////////////////////////////////////////////////////////
// Classes
///////////////////////////////////////////////////////////////////////////////
/**
* Alpha texture used to represent a path.
*/
struct PathTexture: public Texture {
PathTexture(Caches& caches): Texture(caches) {
}
~PathTexture() {
clearTask();
}
/**
* Left coordinate of the path bounds.
*/
float left;
/**
* Top coordinate of the path bounds.
*/
float top;
/**
* Offset to draw the path at the correct origin.
*/
float offset;
sp<Task<SkBitmap*> > task() const {
return mTask;
}
void setTask(const sp<Task<SkBitmap*> >& task) {
mTask = task;
}
void clearTask() {
if (mTask != NULL) {
mTask.clear();
}
}
private:
sp<Task<SkBitmap*> > mTask;
}; // struct PathTexture
enum ShapeType {
kShapeNone,
kShapeRect,
kShapeRoundRect,
kShapeCircle,
kShapeOval,
kShapeArc,
kShapePath
};
struct PathDescription {
ShapeType type;
SkPaint::Join join;
SkPaint::Cap cap;
SkPaint::Style style;
float miter;
float strokeWidth;
SkPathEffect* pathEffect;
union Shape {
struct Path {
SkPath* mPath;
} path;
struct RoundRect {
float mWidth;
float mHeight;
float mRx;
float mRy;
} roundRect;
struct Circle {
float mRadius;
} circle;
struct Oval {
float mWidth;
float mHeight;
} oval;
struct Rect {
float mWidth;
float mHeight;
} rect;
struct Arc {
float mWidth;
float mHeight;
float mStartAngle;
float mSweepAngle;
bool mUseCenter;
} arc;
} shape;
PathDescription();
PathDescription(ShapeType shapeType, SkPaint* paint);
hash_t hash() const;
int compare(const PathDescription& rhs) const;
bool operator==(const PathDescription& other) const {
return compare(other) == 0;
}
bool operator!=(const PathDescription& other) const {
return compare(other) != 0;
}
friend inline int strictly_order_type(
const PathDescription& lhs, const PathDescription& rhs) {
return lhs.compare(rhs) < 0;
}
friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
return lhs.compare(rhs);
}
friend inline hash_t hash_type(const PathDescription& entry) {
return entry.hash();
}
};
/**
* A simple LRU shape cache. The cache has a maximum size expressed in bytes.
* Any texture added to the cache causing the cache to grow beyond the maximum
* allowed size will also cause the oldest texture to be kicked out.
*/
class PathCache: public OnEntryRemoved<PathDescription, PathTexture*> {
public:
PathCache();
~PathCache();
/**
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
void operator()(PathDescription& path, PathTexture*& texture);
/**
* Clears the cache. This causes all textures to be deleted.
*/
void clear();
/**
* Sets the maximum size of the cache in bytes.
*/
void setMaxSize(uint32_t maxSize);
/**
* Returns the maximum size of the cache in bytes.
*/
uint32_t getMaxSize();
/**
* Returns the current size of the cache in bytes.
*/
uint32_t getSize();
PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
PathTexture* getCircle(float radius, SkPaint* paint);
PathTexture* getOval(float width, float height, SkPaint* paint);
PathTexture* getRect(float width, float height, SkPaint* paint);
PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
bool useCenter, SkPaint* paint);
PathTexture* get(SkPath* path, SkPaint* paint);
/**
* Removes the specified path. This is meant to be called from threads
* that are not the EGL context thread.
*/
void removeDeferred(SkPath* path);
/**
* Process deferred removals.
*/
void clearGarbage();
/**
* Trims the contents of the cache, removing items until it's under its
* specified limit.
*
* Trimming is used for caches that support pre-caching from a worker
* thread. During pre-caching the maximum limit of the cache can be
* exceeded for the duration of the frame. It is therefore required to
* trim the cache at the end of the frame to keep the total amount of
* memory used under control.
*/
void trim();
/**
* Precaches the specified path using background threads.
*/
void precache(SkPath* path, SkPaint* paint);
static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint);
static void computePathBounds(const SkPath* path, const SkPaint* paint,
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
static void computeBounds(const SkRect& bounds, const SkPaint* paint,
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
private:
typedef Pair<SkPath*, SkPath*> path_pair_t;
PathTexture* addTexture(const PathDescription& entry,
const SkPath *path, const SkPaint* paint);
PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
/**
* Generates the texture from a bitmap into the specified texture structure.
*/
void generateTexture(SkBitmap& bitmap, Texture* texture);
void generateTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture,
bool addToCache = true);
PathTexture* get(const PathDescription& entry) {
return mCache.get(entry);
}
/**
* Removes an entry.
* The pair must define first=path, second=sourcePath
*/
void remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair);
/**
* Ensures there is enough space in the cache for a texture of the specified
* dimensions.
*/
void purgeCache(uint32_t width, uint32_t height);
void removeTexture(PathTexture* texture);
bool checkTextureSize(uint32_t width, uint32_t height) {
if (width > mMaxTextureSize || height > mMaxTextureSize) {
ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)",
width, height, mMaxTextureSize, mMaxTextureSize);
return false;
}
return true;
}
void init();
class PathTask: public Task<SkBitmap*> {
public:
PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
path(path), paint(paint), texture(texture) {
}
~PathTask() {
delete future()->get();
}
SkPath* path;
SkPaint* paint;
PathTexture* texture;
};
class PathProcessor: public TaskProcessor<SkBitmap*> {
public:
PathProcessor(Caches& caches);
~PathProcessor() { }
virtual void onProcess(const sp<Task<SkBitmap*> >& task);
private:
uint32_t mMaxTextureSize;
};
LruCache<PathDescription, PathTexture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;
GLuint mMaxTextureSize;
bool mDebugEnabled;
sp<PathProcessor> mProcessor;
Vector<path_pair_t> mGarbage;
mutable Mutex mLock;
}; // class PathCache
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_HWUI_PATH_CACHE_H