C++程序  |  336行  |  8.78 KB

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