/* * 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