Romain Guy 4bcb7467a1 Only recreate path textures when necessary
When a drawPath command is recorded in a display list, a copy of the
source path is made to preserve against possible modifications of the
said source path. Copies are discarded when a display list is cleared,
which usually happens on invalidate(). This means that even if a path
is never modified, the texture generated to draw it on screen is
destroyed every time an invalidate() is issued. This change fixes this
problem by introducing a reference to the source path in the copy.
If both the copy and the source path have the same genID, they are
the same path and can share the same texture.

Change-Id: I34849311c183e06336a1391d2d1568a087f973f6
2012-02-23 17:08:38 -08:00

109 lines
3.2 KiB
C++

/*
* Copyright (C) 2010 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.
*/
#define LOG_TAG "OpenGLRenderer"
#include <utils/threads.h>
#include "PathCache.h"
#include "Properties.h"
namespace android {
namespace uirenderer {
// Defined in ShapeCache.h
void computePathBounds(const SkPath *path, const SkPaint* paint,
float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
const SkRect& bounds = path->getBounds();
const float pathWidth = fmax(bounds.width(), 1.0f);
const float pathHeight = fmax(bounds.height(), 1.0f);
left = bounds.fLeft;
top = bounds.fTop;
offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
width = uint32_t(pathWidth + offset * 2.0 + 0.5);
height = uint32_t(pathHeight + offset * 2.0 + 0.5);
}
///////////////////////////////////////////////////////////////////////////////
// Path cache
///////////////////////////////////////////////////////////////////////////////
PathCache::PathCache(): ShapeCache<PathCacheEntry>("path",
PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) {
}
void PathCache::remove(SkPath* path) {
// TODO: Linear search...
Vector<size_t> pathsToRemove;
for (size_t i = 0; i < mCache.size(); i++) {
if (mCache.getKeyAt(i).path == path) {
pathsToRemove.push(i);
removeTexture(mCache.getValueAt(i));
}
}
mCache.setOnEntryRemovedListener(NULL);
for (size_t i = 0; i < pathsToRemove.size(); i++) {
// This will work because pathsToRemove is sorted
// and because the cache is a sorted keyed vector
mCache.removeAt(pathsToRemove.itemAt(i) - i);
}
mCache.setOnEntryRemovedListener(this);
}
void PathCache::removeDeferred(SkPath* path) {
Mutex::Autolock _l(mLock);
mGarbage.push(path);
}
void PathCache::clearGarbage() {
Mutex::Autolock _l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
remove(mGarbage.itemAt(i));
}
mGarbage.clear();
}
PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
const SkPath* sourcePath = path->getSourcePath();
if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
path = const_cast<SkPath*>(sourcePath);
}
PathCacheEntry entry(path, paint);
PathTexture* texture = mCache.get(entry);
float left, top, offset;
uint32_t width, height;
if (!texture) {
texture = addTexture(entry, path, paint);
} else if (path->getGenerationID() != texture->generation) {
mCache.remove(entry);
texture = addTexture(entry, path, paint);
}
return texture;
}
}; // namespace uirenderer
}; // namespace android