diff options
Diffstat (limited to 'libs/hwui')
53 files changed, 10812 insertions, 0 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk new file mode 100644 index 0000000..437a9ef --- /dev/null +++ b/libs/hwui/Android.mk @@ -0,0 +1,47 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# Only build libhwui when USE_OPENGL_RENDERER is +# defined in the current device/board configuration +ifeq ($(USE_OPENGL_RENDERER),true) + LOCAL_SRC_FILES:= \ + utils/SortedListImpl.cpp \ + DisplayListRenderer.cpp \ + FboCache.cpp \ + FontRenderer.cpp \ + GammaFontRenderer.cpp \ + GradientCache.cpp \ + LayerCache.cpp \ + Matrix.cpp \ + OpenGLDebugRenderer.cpp \ + OpenGLRenderer.cpp \ + Patch.cpp \ + PatchCache.cpp \ + PathCache.cpp \ + Program.cpp \ + ProgramCache.cpp \ + SkiaColorFilter.cpp \ + SkiaShader.cpp \ + TextureCache.cpp \ + TextDropShadowCache.cpp + + LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + $(LOCAL_PATH)/../../include/utils \ + external/skia/include/core \ + external/skia/include/effects \ + external/skia/include/images \ + external/skia/src/ports \ + external/skia/include/utils + + LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER + LOCAL_MODULE_CLASS := SHARED_LIBRARIES + LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia + LOCAL_MODULE := libhwui + LOCAL_MODULE_TAGS := optional + LOCAL_PRELINK_MODULE := false + + include $(BUILD_SHARED_LIBRARY) + + include $(call all-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h new file mode 100644 index 0000000..2952a66 --- /dev/null +++ b/libs/hwui/Caches.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_CACHES_H +#define ANDROID_UI_CACHES_H + +#ifndef LOG_TAG + #define LOG_TAG "OpenGLRenderer" +#endif + +#include <utils/Singleton.h> + +#include "TextureCache.h" +#include "LayerCache.h" +#include "GradientCache.h" +#include "PatchCache.h" +#include "GammaFontRenderer.h" +#include "ProgramCache.h" +#include "PathCache.h" +#include "TextDropShadowCache.h" +#include "FboCache.h" +#include "Line.h" + +namespace android { +namespace uirenderer { + +struct CacheLogger { + CacheLogger() { + LOGD("Creating caches"); + } +}; // struct CacheLogger + +class Caches: public Singleton<Caches> { + Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO), + lastDstMode(GL_ZERO), currentProgram(NULL) { + } + + friend class Singleton<Caches>; + + CacheLogger logger; + +public: + bool blend; + GLenum lastSrcMode; + GLenum lastDstMode; + Program* currentProgram; + + TextureCache textureCache; + LayerCache layerCache; + GradientCache gradientCache; + ProgramCache programCache; + PathCache pathCache; + PatchCache patchCache; + TextDropShadowCache dropShadowCache; + FboCache fboCache; + GammaFontRenderer fontRenderer; + + Line line; +}; // class Caches + +}; // namespace uirenderer + +#ifdef USE_OPENGL_RENDERER +using namespace uirenderer; +ANDROID_SINGLETON_STATIC_INSTANCE(Caches); +#endif + +}; // namespace android + +#endif // ANDROID_UI_CACHES_H diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp new file mode 100644 index 0000000..b3517c4 --- /dev/null +++ b/libs/hwui/DisplayListRenderer.cpp @@ -0,0 +1,588 @@ +/* + * 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 "DisplayListRenderer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define PATH_HEAP_SIZE 64 + +/////////////////////////////////////////////////////////////////////////////// +// Helpers +/////////////////////////////////////////////////////////////////////////////// + +PathHeap::PathHeap(): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) { +} + +PathHeap::PathHeap(SkFlattenableReadBuffer& buffer): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) { + int count = buffer.readS32(); + + mPaths.setCount(count); + SkPath** ptr = mPaths.begin(); + SkPath* p = (SkPath*) mHeap.allocThrow(count * sizeof(SkPath)); + + for (int i = 0; i < count; i++) { + new (p) SkPath; + p->unflatten(buffer); + *ptr++ = p; + p++; + } +} + +PathHeap::~PathHeap() { + SkPath** iter = mPaths.begin(); + SkPath** stop = mPaths.end(); + while (iter < stop) { + (*iter)->~SkPath(); + iter++; + } +} + +int PathHeap::append(const SkPath& path) { + SkPath* p = (SkPath*) mHeap.allocThrow(sizeof(SkPath)); + new (p) SkPath(path); + *mPaths.append() = p; + return mPaths.count(); +} + +void PathHeap::flatten(SkFlattenableWriteBuffer& buffer) const { + int count = mPaths.count(); + + buffer.write32(count); + SkPath** iter = mPaths.begin(); + SkPath** stop = mPaths.end(); + while (iter < stop) { + (*iter)->flatten(buffer); + iter++; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Display list +/////////////////////////////////////////////////////////////////////////////// + +DisplayList::DisplayList(const DisplayListRenderer& recorder) { + const SkWriter32& writer = recorder.writeStream(); + init(); + + if (writer.size() == 0) { + return; + } + + size_t size = writer.size(); + void* buffer = sk_malloc_throw(size); + writer.flatten(buffer); + mReader.setMemory(buffer, size); + + mRCPlayback.reset(&recorder.mRCRecorder); + mRCPlayback.setupBuffer(mReader); + + mTFPlayback.reset(&recorder.mTFRecorder); + mTFPlayback.setupBuffer(mReader); + + const SkTDArray<const SkFlatBitmap*>& bitmaps = recorder.getBitmaps(); + mBitmapCount = bitmaps.count(); + if (mBitmapCount > 0) { + mBitmaps = new SkBitmap[mBitmapCount]; + for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin(); + flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) { + const SkFlatBitmap* flatBitmap = *flatBitmapPtr; + int index = flatBitmap->index() - 1; + flatBitmap->unflatten(&mBitmaps[index], &mRCPlayback); + } + } + + const SkTDArray<const SkFlatMatrix*>& matrices = recorder.getMatrices(); + mMatrixCount = matrices.count(); + if (mMatrixCount > 0) { + mMatrices = new SkMatrix[mMatrixCount]; + for (const SkFlatMatrix** matrixPtr = matrices.begin(); + matrixPtr != matrices.end(); matrixPtr++) { + const SkFlatMatrix* flatMatrix = *matrixPtr; + flatMatrix->unflatten(&mMatrices[flatMatrix->index() - 1]); + } + } + + const SkTDArray<const SkFlatPaint*>& paints = recorder.getPaints(); + mPaintCount = paints.count(); + if (mPaintCount > 0) { + mPaints = new SkPaint[mPaintCount]; + for (const SkFlatPaint** flatPaintPtr = paints.begin(); + flatPaintPtr != paints.end(); flatPaintPtr++) { + const SkFlatPaint* flatPaint = *flatPaintPtr; + int index = flatPaint->index() - 1; + flatPaint->unflatten(&mPaints[index], &mRCPlayback, &mTFPlayback); + } + } + + mPathHeap = recorder.mPathHeap; + mPathHeap->safeRef(); +} + +DisplayList::~DisplayList() { + sk_free((void*) mReader.base()); + + Caches& caches = Caches::getInstance(); + for (int i = 0; i < mBitmapCount; i++) { + caches.textureCache.remove(&mBitmaps[i]); + } + + delete[] mBitmaps; + delete[] mMatrices; + delete[] mPaints; + + mPathHeap->safeUnref(); +} + +void DisplayList::init() { + mBitmaps = NULL; + mMatrices = NULL; + mPaints = NULL; + mPathHeap = NULL; + mBitmapCount = mMatrixCount = mPaintCount = 0; +} + +void DisplayList::replay(OpenGLRenderer& renderer) { + TextContainer text; + mReader.rewind(); + + int saveCount = renderer.getSaveCount() - 1; + + while (!mReader.eof()) { + switch (mReader.readInt()) { + case AcquireContext: { + renderer.acquireContext(); + } + break; + case ReleaseContext: { + renderer.releaseContext(); + } + break; + case Save: { + renderer.save(getInt()); + } + break; + case Restore: { + renderer.restore(); + } + break; + case RestoreToCount: { + renderer.restoreToCount(saveCount + getInt()); + } + break; + case SaveLayer: { + renderer.saveLayer(getFloat(), getFloat(), getFloat(), getFloat(), + getPaint(), getInt()); + } + break; + case Translate: { + renderer.translate(getFloat(), getFloat()); + } + break; + case Rotate: { + renderer.rotate(getFloat()); + } + break; + case Scale: { + renderer.scale(getFloat(), getFloat()); + } + break; + case SetMatrix: { + renderer.setMatrix(getMatrix()); + } + break; + case ConcatMatrix: { + renderer.concatMatrix(getMatrix()); + } + break; + case ClipRect: { + renderer.clipRect(getFloat(), getFloat(), getFloat(), getFloat(), + (SkRegion::Op) getInt()); + } + break; + case DrawBitmap: { + renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawBitmapMatrix: { + renderer.drawBitmap(getBitmap(), getMatrix(), getPaint()); + } + break; + case DrawBitmapRect: { + renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getFloat(), getFloat(), + getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawPatch: { + int32_t* xDivs = NULL; + int32_t* yDivs = NULL; + uint32_t* colors = NULL; + uint32_t xDivsCount = 0; + uint32_t yDivsCount = 0; + int8_t numColors = 0; + + SkBitmap* bitmap = getBitmap(); + + xDivs = getInts(xDivsCount); + yDivs = getInts(yDivsCount); + colors = getUInts(numColors); + + renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount, + numColors, getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawColor: { + renderer.drawColor(getInt(), (SkXfermode::Mode) getInt()); + } + break; + case DrawRect: { + renderer.drawRect(getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawPath: { + renderer.drawPath(getPath(), getPaint()); + } + break; + case DrawLines: { + int count = 0; + float* points = getFloats(count); + renderer.drawLines(points, count, getPaint()); + } + break; + case DrawText: { + getText(&text); + renderer.drawText(text.text(), text.length(), getInt(), + getFloat(), getFloat(), getPaint()); + } + break; + case ResetShader: { + renderer.resetShader(); + } + break; + case SetupShader: { + // TODO: Implement + } + break; + case ResetColorFilter: { + renderer.resetColorFilter(); + } + break; + case SetupColorFilter: { + // TODO: Implement + } + break; + case ResetShadow: { + renderer.resetShadow(); + } + break; + case SetupShadow: { + renderer.setupShadow(getFloat(), getFloat(), getFloat(), getInt()); + } + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Base structure +/////////////////////////////////////////////////////////////////////////////// + +DisplayListRenderer::DisplayListRenderer(): + mHeap(HEAP_BLOCK_SIZE), mWriter(MIN_WRITER_SIZE) { + mBitmapIndex = mMatrixIndex = mPaintIndex = 1; + mPathHeap = NULL; +} + +DisplayListRenderer::~DisplayListRenderer() { + reset(); +} + +void DisplayListRenderer::reset() { + if (mPathHeap) { + mPathHeap->unref(); + mPathHeap = NULL; + } + + mBitmaps.reset(); + mMatrices.reset(); + mPaints.reset(); + + mWriter.reset(); + mHeap.reset(); + + mRCRecorder.reset(); + mTFRecorder.reset(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Operations +/////////////////////////////////////////////////////////////////////////////// + +void DisplayListRenderer::setViewport(int width, int height) { + mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + + mWidth = width; + mHeight = height; +} + +void DisplayListRenderer::prepare(bool opaque) { + mSnapshot = new Snapshot(mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSaveCount = 1; + mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight); +} + +void DisplayListRenderer::acquireContext() { + addOp(DisplayList::AcquireContext); + OpenGLRenderer::acquireContext(); +} + +void DisplayListRenderer::releaseContext() { + addOp(DisplayList::ReleaseContext); + OpenGLRenderer::releaseContext(); +} + +int DisplayListRenderer::save(int flags) { + addOp(DisplayList::Save); + addInt(flags); + return OpenGLRenderer::save(flags); +} + +void DisplayListRenderer::restore() { + addOp(DisplayList::Restore); + OpenGLRenderer::restore(); +} + +void DisplayListRenderer::restoreToCount(int saveCount) { + addOp(DisplayList::RestoreToCount); + addInt(saveCount); + OpenGLRenderer::restoreToCount(saveCount); +} + +int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom, + const SkPaint* p, int flags) { + addOp(DisplayList::SaveLayer); + addBounds(left, top, right, bottom); + addPaint(p); + addInt(flags); + return OpenGLRenderer::save(flags); +} + +void DisplayListRenderer::translate(float dx, float dy) { + addOp(DisplayList::Translate); + addPoint(dx, dy); + OpenGLRenderer::translate(dx, dy); +} + +void DisplayListRenderer::rotate(float degrees) { + addOp(DisplayList::Rotate); + addFloat(degrees); + OpenGLRenderer::rotate(degrees); +} + +void DisplayListRenderer::scale(float sx, float sy) { + addOp(DisplayList::Scale); + addPoint(sx, sy); + OpenGLRenderer::scale(sx, sy); +} + +void DisplayListRenderer::setMatrix(SkMatrix* matrix) { + addOp(DisplayList::SetMatrix); + addMatrix(matrix); + OpenGLRenderer::setMatrix(matrix); +} + +void DisplayListRenderer::concatMatrix(SkMatrix* matrix) { + addOp(DisplayList::ConcatMatrix); + addMatrix(matrix); + OpenGLRenderer::concatMatrix(matrix); +} + +bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom, + SkRegion::Op op) { + addOp(DisplayList::ClipRect); + addBounds(left, top, right, bottom); + addInt(op); + return OpenGLRenderer::clipRect(left, top, right, bottom, op); +} + +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { + addOp(DisplayList::DrawBitmap); + addBitmap(bitmap); + addPoint(left, top); + addPaint(paint); +} + +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) { + addOp(DisplayList::DrawBitmapMatrix); + addBitmap(bitmap); + addMatrix(matrix); + addPaint(paint); +} + +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint) { + addOp(DisplayList::DrawBitmapRect); + addBitmap(bitmap); + addBounds(srcLeft, srcTop, srcRight, srcBottom); + addBounds(dstLeft, dstTop, dstRight, dstBottom); + addPaint(paint); +} + +void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, const SkPaint* paint) { + addOp(DisplayList::DrawPatch); + addBitmap(bitmap); + addInts(xDivs, width); + addInts(yDivs, height); + addUInts(colors, numColors); + addBounds(left, top, right, bottom); + addPaint(paint); +} + +void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { + addOp(DisplayList::DrawColor); + addInt(color); + addInt(mode); +} + +void DisplayListRenderer::drawRect(float left, float top, float right, float bottom, + const SkPaint* paint) { + addOp(DisplayList::DrawRect); + addBounds(left, top, right, bottom); + addPaint(paint); +} + +void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { + addOp(DisplayList::DrawPath); + addPath(path); + addPaint(paint); +} + +void DisplayListRenderer::drawLines(float* points, int count, const SkPaint* paint) { + addOp(DisplayList::DrawLines); + addFloats(points, count); + addPaint(paint); +} + +void DisplayListRenderer::drawText(const char* text, int bytesCount, int count, + float x, float y, SkPaint* paint) { + addOp(DisplayList::DrawText); + addText(text, bytesCount); + addInt(count); + addPoint(x, y); + addPaint(paint); +} + +void DisplayListRenderer::resetShader() { + addOp(DisplayList::ResetShader); + OpenGLRenderer::resetShader(); +} + +void DisplayListRenderer::setupShader(SkiaShader* shader) { + // TODO: Implement + OpenGLRenderer::setupShader(shader); +} + +void DisplayListRenderer::resetColorFilter() { + addOp(DisplayList::ResetColorFilter); + OpenGLRenderer::resetColorFilter(); +} + +void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) { + // TODO: Implement + OpenGLRenderer::setupColorFilter(filter); +} + +void DisplayListRenderer::resetShadow() { + addOp(DisplayList::ResetShadow); + OpenGLRenderer::resetShadow(); +} + +void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) { + addOp(DisplayList::SetupShadow); + addFloat(radius); + addPoint(dx, dy); + addInt(color); + OpenGLRenderer::setupShadow(radius, dx, dy, color); +} + +/////////////////////////////////////////////////////////////////////////////// +// Recording management +/////////////////////////////////////////////////////////////////////////////// + +int DisplayListRenderer::find(SkTDArray<const SkFlatPaint*>& paints, const SkPaint* paint) { + if (paint == NULL) { + return 0; + } + + SkFlatPaint* flat = SkFlatPaint::Flatten(&mHeap, *paint, mPaintIndex, + &mRCRecorder, &mTFRecorder); + int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(), + paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) { + (void) mHeap.unalloc(flat); + return paints[index]->index(); + } + + index = ~index; + *paints.insert(index) = flat; + return mPaintIndex++; +} + +int DisplayListRenderer::find(SkTDArray<const SkFlatMatrix*>& matrices, const SkMatrix* matrix) { + if (matrix == NULL) { + return 0; + } + + SkFlatMatrix* flat = SkFlatMatrix::Flatten(&mHeap, *matrix, mMatrixIndex); + int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(), + matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) { + (void) mHeap.unalloc(flat); + return matrices[index]->index(); + } + index = ~index; + *matrices.insert(index) = flat; + return mMatrixIndex++; +} + +int DisplayListRenderer::find(SkTDArray<const SkFlatBitmap*>& bitmaps, const SkBitmap& bitmap) { + SkFlatBitmap* flat = SkFlatBitmap::Flatten(&mHeap, bitmap, mBitmapIndex, &mRCRecorder); + int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(), + bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); + if (index >= 0) { + (void) mHeap.unalloc(flat); + return bitmaps[index]->index(); + } + index = ~index; + *bitmaps.insert(index) = flat; + return mBitmapIndex++; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h new file mode 100644 index 0000000..9e6d5b1 --- /dev/null +++ b/libs/hwui/DisplayListRenderer.h @@ -0,0 +1,383 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_DISPLAY_LIST_RENDERER_H +#define ANDROID_UI_DISPLAY_LIST_RENDERER_H + +#include <SkChunkAlloc.h> +#include <SkFlattenable.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkPictureFlat.h> +#include <SkRefCnt.h> +#include <SkTDArray.h> +#include <SkTSearch.h> + +#include "OpenGLRenderer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define MIN_WRITER_SIZE 16384 +#define HEAP_BLOCK_SIZE 4096 + +/////////////////////////////////////////////////////////////////////////////// +// Helpers +/////////////////////////////////////////////////////////////////////////////// + +class PathHeap: public SkRefCnt { +public: + PathHeap(); + PathHeap(SkFlattenableReadBuffer& buffer); + ~PathHeap(); + + int append(const SkPath& path); + + int count() const { return mPaths.count(); } + + SkPath& operator[](int index) const { + return *mPaths[index]; + } + + void flatten(SkFlattenableWriteBuffer& buffer) const; + +private: + SkChunkAlloc mHeap; + SkTDArray<SkPath*> mPaths; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Display list +/////////////////////////////////////////////////////////////////////////////// + +class DisplayListRenderer; + +/** + * Replays recorded drawing commands. + */ +class DisplayList { +public: + DisplayList(const DisplayListRenderer& recorder); + ~DisplayList(); + + enum Op { + AcquireContext, + ReleaseContext, + Save, + Restore, + RestoreToCount, + SaveLayer, + Translate, + Rotate, + Scale, + SetMatrix, + ConcatMatrix, + ClipRect, + DrawBitmap, + DrawBitmapMatrix, + DrawBitmapRect, + DrawPatch, + DrawColor, + DrawRect, + DrawPath, + DrawLines, + DrawText, + ResetShader, + SetupShader, + ResetColorFilter, + SetupColorFilter, + ResetShadow, + SetupShadow + }; + + void replay(OpenGLRenderer& renderer); + +private: + void init(); + + class TextContainer { + public: + size_t length() const { + return mByteLength; + } + + const char* text() const { + return (const char*) mText; + } + + size_t mByteLength; + const char* mText; + }; + + SkBitmap* getBitmap() { + int index = getInt(); + return &mBitmaps[index - 1]; + } + + inline int getIndex() { + return mReader.readInt(); + } + + inline int getInt() { + return mReader.readInt(); + } + + SkMatrix* getMatrix() { + int index = getInt(); + if (index == 0) { + return NULL; + } + return &mMatrices[index - 1]; + } + + SkPath* getPath() { + return &(*mPathHeap)[getInt() - 1]; + } + + SkPaint* getPaint() { + int index = getInt(); + if (index == 0) { + return NULL; + } + return &mPaints[index - 1]; + } + + inline float getFloat() { + return mReader.readScalar(); + } + + int32_t* getInts(uint32_t& count) { + count = getInt(); + return (int32_t*) mReader.skip(count * sizeof(int32_t)); + } + + uint32_t* getUInts(int8_t& count) { + count = getInt(); + return (uint32_t*) mReader.skip(count * sizeof(uint32_t)); + } + + float* getFloats(int& count) { + count = getInt(); + return (float*) mReader.skip(count * sizeof(float)); + } + + void getText(TextContainer* text) { + size_t length = text->mByteLength = getInt(); + text->mText = (const char*) mReader.skip(length); + } + + PathHeap* mPathHeap; + + SkBitmap* mBitmaps; + int mBitmapCount; + + SkMatrix* mMatrices; + int mMatrixCount; + + SkPaint* mPaints; + int mPaintCount; + + mutable SkFlattenableReadBuffer mReader; + + SkRefCntPlayback mRCPlayback; + SkTypefacePlayback mTFPlayback; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + +/** + * Records drawing commands in a display list for latter playback. + */ +class DisplayListRenderer: public OpenGLRenderer { +public: + DisplayListRenderer(); + ~DisplayListRenderer(); + + void setViewport(int width, int height); + void prepare(bool opaque); + + void acquireContext(); + void releaseContext(); + + int save(int flags); + void restore(); + void restoreToCount(int saveCount); + + int saveLayer(float left, float top, float right, float bottom, + const SkPaint* p, int flags); + + void translate(float dx, float dy); + void rotate(float degrees); + void scale(float sx, float sy); + + void setMatrix(SkMatrix* matrix); + void concatMatrix(SkMatrix* matrix); + + bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + + void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint); + void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, const SkPaint* paint); + void drawColor(int color, SkXfermode::Mode mode); + void drawRect(float left, float top, float right, float bottom, const SkPaint* paint); + void drawPath(SkPath* path, SkPaint* paint); + void drawLines(float* points, int count, const SkPaint* paint); + void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint); + + void resetShader(); + void setupShader(SkiaShader* shader); + + void resetColorFilter(); + void setupColorFilter(SkiaColorFilter* filter); + + void resetShadow(); + void setupShadow(float radius, float dx, float dy, int color); + + void reset(); + + DisplayList* getDisplayList() const { + return new DisplayList(*this); + } + + const SkWriter32& writeStream() const { + return mWriter; + } + + const SkTDArray<const SkFlatBitmap*>& getBitmaps() const { + return mBitmaps; + } + + const SkTDArray<const SkFlatMatrix*>& getMatrices() const { + return mMatrices; + } + + const SkTDArray<const SkFlatPaint*>& getPaints() const { + return mPaints; + } + +private: + inline void addOp(DisplayList::Op drawOp) { + mWriter.writeInt(drawOp); + } + + inline void addInt(int value) { + mWriter.writeInt(value); + } + + void addInts(const int32_t* values, uint32_t count) { + mWriter.writeInt(count); + for (uint32_t i = 0; i < count; i++) { + mWriter.writeInt(values[i]); + } + } + + void addUInts(const uint32_t* values, int8_t count) { + mWriter.writeInt(count); + for (int8_t i = 0; i < count; i++) { + mWriter.writeInt(values[i]); + } + } + + inline void addFloat(float value) { + mWriter.writeScalar(value); + } + + void addFloats(const float* values, int count) { + mWriter.writeInt(count); + for (int i = 0; i < count; i++) { + mWriter.writeScalar(values[i]); + } + } + + inline void addPoint(float x, float y) { + mWriter.writeScalar(x); + mWriter.writeScalar(y); + } + + inline void addBounds(float left, float top, float right, float bottom) { + mWriter.writeScalar(left); + mWriter.writeScalar(top); + mWriter.writeScalar(right); + mWriter.writeScalar(bottom); + } + + inline void addText(const void* text, size_t byteLength) { + mWriter.writeInt(byteLength); + mWriter.writePad(text, byteLength); + } + + inline void addPath(const SkPath* path) { + if (mPathHeap == NULL) { + mPathHeap = new PathHeap(); + } + addInt(mPathHeap->append(*path)); + } + + int find(SkTDArray<const SkFlatPaint*>& paints, const SkPaint* paint); + + inline void addPaint(const SkPaint* paint) { + addInt(find(mPaints, paint)); + } + + int find(SkTDArray<const SkFlatMatrix*>& matrices, const SkMatrix* matrix); + + inline void addMatrix(const SkMatrix* matrix) { + addInt(find(mMatrices, matrix)); + } + + int find(SkTDArray<const SkFlatBitmap*>& bitmaps, const SkBitmap& bitmap); + + inline void addBitmap(const SkBitmap* bitmap) { + addInt(find(mBitmaps, *bitmap)); + } + + SkChunkAlloc mHeap; + + int mBitmapIndex; + SkTDArray<const SkFlatBitmap*> mBitmaps; + + int mMatrixIndex; + SkTDArray<const SkFlatMatrix*> mMatrices; + + int mPaintIndex; + SkTDArray<const SkFlatPaint*> mPaints; + + PathHeap* mPathHeap; + SkWriter32 mWriter; + + SkRefCntRecorder mRCRecorder; + SkRefCntRecorder mTFRecorder; + + friend class DisplayList; + +}; // class DisplayListRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_DISPLAY_LIST_RENDERER_H diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h new file mode 100644 index 0000000..d50d36e --- /dev/null +++ b/libs/hwui/Extensions.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_EXTENSIONS_H +#define ANDROID_UI_EXTENSIONS_H + +#include <utils/SortedVector.h> +#include <utils/String8.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#define DEBUG_EXTENSIONS 0 + +// Debug +#if DEBUG_EXTENSIONS + #define EXT_LOGD(...) LOGD(__VA_ARGS__) +#else + #define EXT_LOGD(...) +#endif + +class Extensions { +public: + Extensions() { + const char* buffer = (const char*) glGetString(GL_EXTENSIONS); + const char* current = buffer; + const char* head = current; + EXT_LOGD("Available GL extensions:"); + do { + head = strchr(current, ' '); + String8 s(current, head ? head - current : strlen(current)); + if (s.length()) { + mExtensionList.add(s); + EXT_LOGD(" %s", s.string()); + } + current = head + 1; + } while (head); + + mHasNPot = hasExtension("GL_OES_texture_npot"); + mHasDrawPath = hasExtension("GL_NV_draw_path"); + mHasCoverageSample = hasExtension("GL_NV_coverage_sample"); + mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch"); + + mExtensions = buffer; + } + + inline bool hasNPot() const { return mHasNPot; } + inline bool hasDrawPath() const { return mHasDrawPath; } + inline bool hasCoverageSample() const { return mHasCoverageSample; } + inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } + + bool hasExtension(const char* extension) const { + const String8 s(extension); + return mExtensionList.indexOf(s) >= 0; + } + + void dump() { + LOGD("Supported extensions:\n%s", mExtensions); + } + +private: + SortedVector<String8> mExtensionList; + + const char* mExtensions; + + bool mHasNPot; + bool mHasDrawPath; + bool mHasCoverageSample; + bool mHasFramebufferFetch; +}; // class Extensions + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_EXTENSIONS_H diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp new file mode 100644 index 0000000..2ef71c2 --- /dev/null +++ b/libs/hwui/FboCache.cpp @@ -0,0 +1,91 @@ +/* + * 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 <stdlib.h> + +#include "FboCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +FboCache::FboCache(): mMaxSize(DEFAULT_FBO_CACHE_SIZE) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_FBO_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting fbo cache size to %s", property); + mMaxSize = atoi(property); + } else { + LOGD(" Using default fbo cache size of %d", DEFAULT_FBO_CACHE_SIZE); + } +} + +FboCache::~FboCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t FboCache::getSize() { + return mCache.size(); +} + +uint32_t FboCache::getMaxSize() { + return mMaxSize; +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void FboCache::clear() { + for (size_t i = 0; i < mCache.size(); i++) { + const GLuint fbo = mCache.itemAt(i); + glDeleteFramebuffers(1, &fbo); + } + mCache.clear(); +} + +GLuint FboCache::get() { + GLuint fbo; + if (mCache.size() > 0) { + fbo = mCache.itemAt(mCache.size() - 1); + mCache.removeAt(mCache.size() - 1); + } else { + glGenFramebuffers(1, &fbo); + } + return fbo; +} + +bool FboCache::put(GLuint fbo) { + if (mCache.size() < mMaxSize) { + mCache.add(fbo); + return true; + } + + glDeleteFramebuffers(1, &fbo); + return false; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h new file mode 100644 index 0000000..ec4afe9 --- /dev/null +++ b/libs/hwui/FboCache.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_FBO_CACHE_H +#define ANDROID_UI_FBO_CACHE_H + +#include <GLES2/gl2.h> + +#include <utils/SortedVector.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////////////////////////// + +class FboCache { +public: + FboCache(); + ~FboCache(); + + /** + * Returns an FBO from the cache. If no FBO is available, a new one + * is created. If creating a new FBO fails, 0 is returned. + * + * When an FBO is obtained from the cache, it is removed and the + * total number of FBOs available in the cache decreases. + * + * @return The name of the FBO, or 0 if no FBO can be obtained. + */ + GLuint get(); + + /** + * Adds the specified FBO to the cache. + * + * @param fbo The FBO to add to the cache. + * + * @return True if the FBO was added, false otherwise. + */ + bool put(GLuint fbo); + + /** + * Clears the cache. This causes all FBOs to be deleted. + */ + void clear(); + + /** + * Returns the current size of the cache. + */ + uint32_t getSize(); + + /** + * Returns the maximum number of FBOs that the cache can hold. + */ + uint32_t getMaxSize(); + +private: + SortedVector<GLuint> mCache; + uint32_t mMaxSize; +}; // class FboCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_FBO_CACHE_H diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp new file mode 100644 index 0000000..1b21aee --- /dev/null +++ b/libs/hwui/FontRenderer.cpp @@ -0,0 +1,833 @@ +/* + * 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 <SkUtils.h> + +#include <cutils/properties.h> + +#include <utils/Log.h> + +#include "FontRenderer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define DEFAULT_TEXT_CACHE_WIDTH 1024 +#define DEFAULT_TEXT_CACHE_HEIGHT 256 + +/////////////////////////////////////////////////////////////////////////////// +// Font +/////////////////////////////////////////////////////////////////////////////// + +Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) : + mState(state), mFontId(fontId), mFontSize(fontSize) { +} + + +Font::~Font() { + for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { + if (mState->mActiveFonts[ct] == this) { + mState->mActiveFonts.removeAt(ct); + break; + } + } + + for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { + CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i); + delete glyph; + } +} + +void Font::invalidateTextureCache() { + for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { + mCachedGlyphs.valueAt(i)->mIsValid = false; + } +} + +void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) { + int nPenX = x + glyph->mBitmapLeft; + int nPenY = y + glyph->mBitmapTop; + + int width = (int) glyph->mBitmapWidth; + int height = (int) glyph->mBitmapHeight; + + if (bounds->bottom > nPenY) { + bounds->bottom = nPenY; + } + if (bounds->left > nPenX) { + bounds->left = nPenX; + } + if (bounds->right < nPenX + width) { + bounds->right = nPenX + width; + } + if (bounds->top < nPenY + height) { + bounds->top = nPenY + height; + } +} + +void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) { + int nPenX = x + glyph->mBitmapLeft; + int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; + + float u1 = glyph->mBitmapMinU; + float u2 = glyph->mBitmapMaxU; + float v1 = glyph->mBitmapMinV; + float v2 = glyph->mBitmapMaxV; + + int width = (int) glyph->mBitmapWidth; + int height = (int) glyph->mBitmapHeight; + + mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2, + nPenX + width, nPenY, 0, u2, v2, + nPenX + width, nPenY - height, 0, u2, v1, + nPenX, nPenY - height, 0, u1, v1); +} + +void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) { + int nPenX = x + glyph->mBitmapLeft; + int nPenY = y + glyph->mBitmapTop; + + uint32_t endX = glyph->mStartX + glyph->mBitmapWidth; + uint32_t endY = glyph->mStartY + glyph->mBitmapHeight; + + uint32_t cacheWidth = mState->getCacheWidth(); + const uint8_t* cacheBuffer = mState->getTextTextureData(); + + uint32_t cacheX = 0, cacheY = 0; + int32_t bX = 0, bY = 0; + for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) { + for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) { + if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { + LOGE("Skipping invalid index"); + continue; + } + uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; + bitmap[bY * bitmapW + bX] = tempCol; + } + } + +} + +Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) { + CachedGlyphInfo* cachedGlyph = NULL; + ssize_t index = mCachedGlyphs.indexOfKey(utfChar); + if (index >= 0) { + cachedGlyph = mCachedGlyphs.valueAt(index); + } else { + cachedGlyph = cacheGlyph(paint, utfChar); + } + + // Is the glyph still in texture cache? + if (!cachedGlyph->mIsValid) { + const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar); + updateGlyphCache(paint, skiaGlyph, cachedGlyph); + } + + return cachedGlyph; +} + +void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { + if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { + renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, + bitmapW, bitmapH, NULL); + } else { + renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL); + } + +} + +void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, Rect *bounds) { + if (bounds == NULL) { + LOGE("No return rectangle provided to measure text"); + return; + } + bounds->set(1e6, -1e6, -1e6, 1e6); + renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); +} + +#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) + +void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, + uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) { + if (numGlyphs == 0 || text == NULL || len == 0) { + return; + } + + SkFixed penX = SkIntToFixed(x); + int penY = y; + int glyphsLeft = 1; + if (numGlyphs > 0) { + glyphsLeft = numGlyphs; + } + + SkFixed prevRsbDelta = 0; + penX += SK_Fixed1 / 2; + + text += start; + + while (glyphsLeft > 0) { + int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); + + // Reached the end of the string + if (utfChar < 0) { + break; + } + + CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); + penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta); + prevRsbDelta = cachedGlyph->mRsbDelta; + + // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage + if (cachedGlyph->mIsValid) { + switch(mode) { + case FRAMEBUFFER: + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY); + break; + case BITMAP: + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH); + break; + case MEASURE: + measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds); + break; + } + } + + penX += cachedGlyph->mAdvanceX; + + // If we were given a specific number of glyphs, decrement + if (numGlyphs > 0) { + glyphsLeft--; + } + } +} + +void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { + glyph->mAdvanceX = skiaGlyph.fAdvanceX; + glyph->mAdvanceY = skiaGlyph.fAdvanceY; + glyph->mBitmapLeft = skiaGlyph.fLeft; + glyph->mBitmapTop = skiaGlyph.fTop; + glyph->mLsbDelta = skiaGlyph.fLsbDelta; + glyph->mRsbDelta = skiaGlyph.fRsbDelta; + + uint32_t startX = 0; + uint32_t startY = 0; + + // Get the bitmap for the glyph + paint->findImage(skiaGlyph); + glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY); + + if (!glyph->mIsValid) { + return; + } + + uint32_t endX = startX + skiaGlyph.fWidth; + uint32_t endY = startY + skiaGlyph.fHeight; + + glyph->mStartX = startX; + glyph->mStartY = startY; + glyph->mBitmapWidth = skiaGlyph.fWidth; + glyph->mBitmapHeight = skiaGlyph.fHeight; + + uint32_t cacheWidth = mState->getCacheWidth(); + uint32_t cacheHeight = mState->getCacheHeight(); + + glyph->mBitmapMinU = (float) startX / (float) cacheWidth; + glyph->mBitmapMinV = (float) startY / (float) cacheHeight; + glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; + glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; + + mState->mUploadTexture = true; +} + +Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) { + CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); + mCachedGlyphs.add(glyph, newGlyph); + + const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); + newGlyph->mGlyphIndex = skiaGlyph.fID; + newGlyph->mIsValid = false; + + updateGlyphCache(paint, skiaGlyph, newGlyph); + + return newGlyph; +} + +Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) { + Vector<Font*> &activeFonts = state->mActiveFonts; + + for (uint32_t i = 0; i < activeFonts.size(); i++) { + Font* font = activeFonts[i]; + if (font->mFontId == fontId && font->mFontSize == fontSize) { + return font; + } + } + + Font* newFont = new Font(state, fontId, fontSize); + activeFonts.push(newFont); + return newFont; +} + +/////////////////////////////////////////////////////////////////////////////// +// FontRenderer +/////////////////////////////////////////////////////////////////////////////// + +FontRenderer::FontRenderer() { + LOGD("Creating FontRenderer"); + + mGammaTable = NULL; + mInitialized = false; + mMaxNumberOfQuads = 1024; + mCurrentQuadIndex = 0; + mTextureId = 0; + + mTextMeshPtr = NULL; + mTextTexture = NULL; + + mIndexBufferID = 0; + + mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; + mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; + + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { + LOGD(" Setting text cache width to %s pixels", property); + mCacheWidth = atoi(property); + } else { + LOGD(" Using default text cache width of %i pixels", mCacheWidth); + } + + if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { + LOGD(" Setting text cache width to %s pixels", property); + mCacheHeight = atoi(property); + } else { + LOGD(" Using default text cache height of %i pixels", mCacheHeight); + } +} + +FontRenderer::~FontRenderer() { + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + delete mCacheLines[i]; + } + mCacheLines.clear(); + + if (mInitialized) { + delete[] mTextMeshPtr; + delete[] mTextTexture; + } + + if (mTextureId) { + glDeleteTextures(1, &mTextureId); + } + + Vector<Font*> fontsToDereference = mActiveFonts; + for (uint32_t i = 0; i < fontsToDereference.size(); i++) { + delete fontsToDereference[i]; + } +} + +void FontRenderer::flushAllAndInvalidate() { + if (mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } + for (uint32_t i = 0; i < mActiveFonts.size(); i++) { + mActiveFonts[i]->invalidateTextureCache(); + } + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + mCacheLines[i]->mCurrentCol = 0; + } +} + +bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { + // If the glyph is too tall, don't cache it + if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + LOGE("Font size to large to fit in cache. width, height = %i, %i", + (int) glyph.fWidth, (int) glyph.fHeight); + return false; + } + + // Now copy the bitmap into the cache texture + uint32_t startX = 0; + uint32_t startY = 0; + + bool bitmapFit = false; + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); + if (bitmapFit) { + break; + } + } + + // If the new glyph didn't fit, flush the state so far and invalidate everything + if (!bitmapFit) { + flushAllAndInvalidate(); + + // Try to fit it again + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); + if (bitmapFit) { + break; + } + } + + // if we still don't fit, something is wrong and we shouldn't draw + if (!bitmapFit) { + LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", + (int) glyph.fWidth, (int) glyph.fHeight); + return false; + } + } + + *retOriginX = startX; + *retOriginY = startY; + + uint32_t endX = startX + glyph.fWidth; + uint32_t endY = startY + glyph.fHeight; + + uint32_t cacheWidth = mCacheWidth; + + uint8_t* cacheBuffer = mTextTexture; + uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; + unsigned int stride = glyph.rowBytes(); + + uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; + for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { + for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { + uint8_t tempCol = bitmapBuffer[bY * stride + bX]; + cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; + } + } + + return true; +} + +void FontRenderer::initTextTexture() { + mTextTexture = new uint8_t[mCacheWidth * mCacheHeight]; + memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t)); + + mUploadTexture = false; + + glGenTextures(1, &mTextureId); + glBindTexture(GL_TEXTURE_2D, mTextureId); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // Initialize texture dimentions + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, 0); + + mLinearFiltering = false; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Split up our cache texture into lines of certain widths + int nextLine = 0; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); +} + +// Avoid having to reallocate memory and render quad by quad +void FontRenderer::initVertexArrayBuffers() { + uint32_t numIndicies = mMaxNumberOfQuads * 6; + uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); + uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); + + // Four verts, two triangles , six indices per quad + for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { + int i6 = i * 6; + int i4 = i * 4; + + indexBufferData[i6 + 0] = i4 + 0; + indexBufferData[i6 + 1] = i4 + 1; + indexBufferData[i6 + 2] = i4 + 2; + + indexBufferData[i6 + 3] = i4 + 0; + indexBufferData[i6 + 4] = i4 + 2; + indexBufferData[i6 + 5] = i4 + 3; + } + + glGenBuffers(1, &mIndexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + free(indexBufferData); + + uint32_t coordSize = 3; + uint32_t uvSize = 2; + uint32_t vertsPerQuad = 4; + uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; + mTextMeshPtr = new float[vertexBufferSize]; +} + +// We don't want to allocate anything unless we actually draw text +void FontRenderer::checkInit() { + if (mInitialized) { + return; + } + + initTextTexture(); + initVertexArrayBuffers(); + + // We store a string with letters in a rough frequency of occurrence + mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); + mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); + mLatinPrecache += String16(",.?!()-+@;:`'"); + mLatinPrecache += String16("0123456789"); + + mInitialized = true; +} + +void FontRenderer::checkTextureUpdate() { + if (!mUploadTexture) { + return; + } + + glBindTexture(GL_TEXTURE_2D, mTextureId); + + // Iterate over all the cache lines and see which ones need to be updated + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + CacheTextureLine* cl = mCacheLines[i]; + if(cl->mDirty) { + uint32_t xOffset = 0; + uint32_t yOffset = cl->mCurrentRow; + uint32_t width = mCacheWidth; + uint32_t height = cl->mMaxHeight; + void* textureData = mTextTexture + yOffset*width; + + glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, + GL_ALPHA, GL_UNSIGNED_BYTE, textureData); + + cl->mDirty = false; + } + } + + mUploadTexture = false; +} + +void FontRenderer::issueDrawCommand() { + checkTextureUpdate(); + + float* vtx = mTextMeshPtr; + float* tex = vtx + 3; + + // position is slot 0 + uint32_t slot = 0; + glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx); + + // texture0 is slot 1 + slot = 1; + glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); + glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); +} + +void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, + float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, + float x4, float y4, float z4, float u4, float v4) { + if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) { + return; + } + + const uint32_t vertsPerQuad = 4; + const uint32_t floatsPerVert = 5; + float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; + + (*currentPos++) = x1; + (*currentPos++) = y1; + (*currentPos++) = z1; + (*currentPos++) = u1; + (*currentPos++) = v1; + + (*currentPos++) = x2; + (*currentPos++) = y2; + (*currentPos++) = z2; + (*currentPos++) = u2; + (*currentPos++) = v2; + + (*currentPos++) = x3; + (*currentPos++) = y3; + (*currentPos++) = z3; + (*currentPos++) = u3; + (*currentPos++) = v3; + + (*currentPos++) = x4; + (*currentPos++) = y4; + (*currentPos++) = z4; + (*currentPos++) = u4; + (*currentPos++) = v4; + + mCurrentQuadIndex++; + + if (mCurrentQuadIndex == mMaxNumberOfQuads) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +uint32_t FontRenderer::getRemainingCacheCapacity() { + uint32_t remainingCapacity = 0; + float totalPixels = 0; + for(uint32_t i = 0; i < mCacheLines.size(); i ++) { + remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); + totalPixels += mCacheLines[i]->mMaxWidth; + } + remainingCapacity = (remainingCapacity * 100) / totalPixels; + return remainingCapacity; +} + +void FontRenderer::precacheLatin(SkPaint* paint) { + // Remaining capacity is measured in % + uint32_t remainingCapacity = getRemainingCacheCapacity(); + uint32_t precacheIdx = 0; + while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { + mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]); + remainingCapacity = getRemainingCacheCapacity(); + precacheIdx ++; + } +} + +void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { + uint32_t currentNumFonts = mActiveFonts.size(); + mCurrentFont = Font::create(this, fontId, fontSize); + + const float maxPrecacheFontSize = 40.0f; + bool isNewFont = currentNumFonts != mActiveFonts.size(); + + if (isNewFont && fontSize <= maxPrecacheFontSize) { + precacheLatin(paint); + } +} + +FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { + checkInit(); + + if (!mCurrentFont) { + DropShadow image; + image.width = 0; + image.height = 0; + image.image = NULL; + image.penX = 0; + image.penY = 0; + return image; + } + + Rect bounds; + mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds); + uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; + uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; + uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; + for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { + dataBuffer[i] = 0; + } + + int penX = radius - bounds.left; + int penY = radius - bounds.bottom; + + mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY, + dataBuffer, paddedWidth, paddedHeight); + blurImage(dataBuffer, paddedWidth, paddedHeight, radius); + + DropShadow image; + image.width = paddedWidth; + image.height = paddedHeight; + image.image = dataBuffer; + image.penX = penX; + image.penY = penY; + return image; +} + +void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) { + checkInit(); + + if (!mCurrentFont) { + LOGE("No font set"); + return; + } + + mClip = clip; + mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); + + if (mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { + // Compute gaussian weights for the blur + // e is the euler's number + float e = 2.718281828459045f; + float pi = 3.1415926535897932f; + // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) + // x is of the form [-radius .. 0 .. radius] + // and sigma varies with radius. + // Based on some experimental radius values and sigma's + // we approximately fit sigma = f(radius) as + // sigma = radius * 0.3 + 0.6 + // The larger the radius gets, the more our gaussian blur + // will resemble a box blur since with large sigma + // the gaussian curve begins to lose its shape + float sigma = 0.3f * (float)radius + 0.6f; + + // Now compute the coefficints + // We will store some redundant values to save some math during + // the blur calculations + // precompute some values + float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); + float coeff2 = - 1.0f / (2.0f * sigma * sigma); + + float normalizeFactor = 0.0f; + for(int32_t r = -radius; r <= radius; r ++) { + float floatR = (float) r; + weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); + normalizeFactor += weights[r + radius]; + } + + //Now we need to normalize the weights because all our coefficients need to add up to one + normalizeFactor = 1.0f / normalizeFactor; + for(int32_t r = -radius; r <= radius; r ++) { + weights[r + radius] *= normalizeFactor; + } +} + +void FontRenderer::horizontalBlur(float* weights, int32_t radius, + const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { + float blurredPixel = 0.0f; + float currentPixel = 0.0f; + + for(int32_t y = 0; y < height; y ++) { + + const uint8_t* input = source + y * width; + uint8_t* output = dest + y * width; + + for(int32_t x = 0; x < width; x ++) { + blurredPixel = 0.0f; + const float* gPtr = weights; + // Optimization for non-border pixels + if ((x > radius) && (x < (width - radius))) { + const uint8_t *i = input + (x - radius); + for(int r = -radius; r <= radius; r ++) { + currentPixel = (float) (*i); + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + i++; + } + } else { + for(int32_t r = -radius; r <= radius; r ++) { + // Stepping left and right away from the pixel + int validW = x + r; + if(validW < 0) { + validW = 0; + } + if(validW > width - 1) { + validW = width - 1; + } + + currentPixel = (float)(input[validW]); + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + } + } + *output = (uint8_t)blurredPixel; + output ++; + } + } +} + +void FontRenderer::verticalBlur(float* weights, int32_t radius, + const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { + float blurredPixel = 0.0f; + float currentPixel = 0.0f; + + for(int32_t y = 0; y < height; y ++) { + + uint8_t* output = dest + y * width; + + for(int32_t x = 0; x < width; x ++) { + blurredPixel = 0.0f; + const float* gPtr = weights; + const uint8_t* input = source + x; + // Optimization for non-border pixels + if ((y > radius) && (y < (height - radius))) { + const uint8_t *i = input + ((y - radius) * width); + for(int32_t r = -radius; r <= radius; r ++) { + currentPixel = (float)(*i); + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + i += width; + } + } else { + for(int32_t r = -radius; r <= radius; r ++) { + int validH = y + r; + // Clamp to zero and width + if(validH < 0) { + validH = 0; + } + if(validH > height - 1) { + validH = height - 1; + } + + const uint8_t *i = input + validH * width; + currentPixel = (float)(*i); + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + } + } + *output = (uint8_t)blurredPixel; + output ++; + } + } +} + + +void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { + float *gaussian = new float[2 * radius + 1]; + computeGaussianWeights(gaussian, radius); + uint8_t* scratch = new uint8_t[width * height]; + horizontalBlur(gaussian, radius, image, scratch, width, height); + verticalBlur(gaussian, radius, scratch, image, width, height); + delete[] gaussian; + delete[] scratch; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h new file mode 100644 index 0000000..f10efad --- /dev/null +++ b/libs/hwui/FontRenderer.h @@ -0,0 +1,276 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_FONT_RENDERER_H +#define ANDROID_UI_FONT_RENDERER_H + +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +#include <SkScalerContext.h> +#include <SkPaint.h> + +#include <GLES2/gl2.h> + +#include "Rect.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +class FontRenderer; + +/** + * Represents a font, defined by a Skia font id and a font size. A font is used + * to generate glyphs and cache them in the FontState. + */ +class Font { +public: + ~Font(); + + /** + * Renders the specified string of text. + * If bitmap is specified, it will be used as the render target + */ + void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, + uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0); + /** + * Creates a new font associated with the specified font state. + */ + static Font* create(FontRenderer* state, uint32_t fontId, float fontSize); + +protected: + friend class FontRenderer; + + enum RenderMode { + FRAMEBUFFER, + BITMAP, + MEASURE, + }; + + void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, RenderMode mode, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, + Rect *bounds); + + void measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, Rect *bounds); + + struct CachedGlyphInfo { + // Has the cache been invalidated? + bool mIsValid; + // Location of the cached glyph in the bitmap + // in case we need to resize the texture or + // render to bitmap + uint32_t mStartX; + uint32_t mStartY; + uint32_t mBitmapWidth; + uint32_t mBitmapHeight; + // Also cache texture coords for the quad + float mBitmapMinU; + float mBitmapMinV; + float mBitmapMaxU; + float mBitmapMaxV; + // Minimize how much we call freetype + uint32_t mGlyphIndex; + uint32_t mAdvanceX; + uint32_t mAdvanceY; + // Values below contain a glyph's origin in the bitmap + int32_t mBitmapLeft; + int32_t mBitmapTop; + // Auto-kerning + SkFixed mLsbDelta; + SkFixed mRsbDelta; + }; + + Font(FontRenderer* state, uint32_t fontId, float fontSize); + + DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs; + + void invalidateTextureCache(); + + CachedGlyphInfo* cacheGlyph(SkPaint* paint, int32_t glyph); + void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph); + void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds); + void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y); + void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH); + + CachedGlyphInfo* getCachedUTFChar(SkPaint* paint, int32_t utfChar); + + FontRenderer* mState; + uint32_t mFontId; + float mFontSize; +}; + +class FontRenderer { +public: + FontRenderer(); + ~FontRenderer(); + + void init(); + void deinit(); + + void setGammaTable(const uint8_t* gammaTable) { + mGammaTable = gammaTable; + } + + void setFont(SkPaint* paint, uint32_t fontId, float fontSize); + void renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, + uint32_t len, int numGlyphs, int x, int y); + + struct DropShadow { + DropShadow() { }; + + DropShadow(const DropShadow& dropShadow): + width(dropShadow.width), height(dropShadow.height), + image(dropShadow.image), penX(dropShadow.penX), + penY(dropShadow.penY) { + } + + uint32_t width; + uint32_t height; + uint8_t* image; + int32_t penX; + int32_t penY; + }; + + // After renderDropShadow returns, the called owns the memory in DropShadow.image + // and is responsible for releasing it when it's done with it + DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex, + uint32_t len, int numGlyphs, uint32_t radius); + + GLuint getTexture(bool linearFiltering = false) { + checkInit(); + if (linearFiltering != mLinearFiltering) { + mLinearFiltering = linearFiltering; + const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST; + + glBindTexture(GL_TEXTURE_2D, mTextureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); + } + return mTextureId; + } + +protected: + friend class Font; + + const uint8_t* mGammaTable; + + struct CacheTextureLine { + uint16_t mMaxHeight; + uint16_t mMaxWidth; + uint32_t mCurrentRow; + uint32_t mCurrentCol; + bool mDirty; + + CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow, + uint32_t currentCol): + mMaxHeight(maxHeight), + mMaxWidth(maxWidth), + mCurrentRow(currentRow), + mCurrentCol(currentCol), + mDirty(false) { + } + + bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { + if (glyph.fHeight + 2 > mMaxHeight) { + return false; + } + + if (mCurrentCol + glyph.fWidth + 2 < mMaxWidth) { + *retOriginX = mCurrentCol + 1; + *retOriginY = mCurrentRow + 1; + mCurrentCol += glyph.fWidth + 2; + mDirty = true; + return true; + } + + return false; + } + }; + + uint32_t getCacheWidth() const { + return mCacheWidth; + } + + uint32_t getCacheHeight() const { + return mCacheHeight; + } + + void initTextTexture(); + bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY); + + void flushAllAndInvalidate(); + void initVertexArrayBuffers(); + + void checkInit(); + + String16 mLatinPrecache; + void precacheLatin(SkPaint* paint); + + void issueDrawCommand(); + void appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, float y2, + float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, + float x4, float y4, float z4, float u4, float v4); + + uint32_t mCacheWidth; + uint32_t mCacheHeight; + + Vector<CacheTextureLine*> mCacheLines; + uint32_t getRemainingCacheCapacity(); + + Font* mCurrentFont; + Vector<Font*> mActiveFonts; + + // Texture to cache glyph bitmaps + uint8_t* mTextTexture; + const uint8_t* getTextTextureData() const { + return mTextTexture; + } + GLuint mTextureId; + void checkTextureUpdate(); + bool mUploadTexture; + + // Pointer to vertex data to speed up frame to frame work + float *mTextMeshPtr; + uint32_t mCurrentQuadIndex; + uint32_t mMaxNumberOfQuads; + + uint32_t mIndexBufferID; + + const Rect* mClip; + + bool mInitialized; + + bool mLinearFiltering; + + void computeGaussianWeights(float* weights, int32_t radius); + void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, + int32_t width, int32_t height); + void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, + int32_t width, int32_t height); + void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius); +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_FONT_RENDERER_H diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp new file mode 100644 index 0000000..6d087e3 --- /dev/null +++ b/libs/hwui/GammaFontRenderer.cpp @@ -0,0 +1,103 @@ +/* + * 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 "GammaFontRenderer.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +GammaFontRenderer::GammaFontRenderer() { + LOGD("Creating gamma font renderer"); + + // Get the renderer properties + char property[PROPERTY_VALUE_MAX]; + + // Get the gamma + float gamma = DEFAULT_TEXT_GAMMA; + if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) { + LOGD(" Setting text gamma to %s", property); + gamma = atof(property); + } else { + LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA); + } + + // Get the black gamma threshold + mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; + if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) { + LOGD(" Setting text black gamma threshold to %s", property); + mBlackThreshold = atoi(property); + } else { + LOGD(" Using default text black gamma threshold of %d", + DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD); + } + + // Get the white gamma threshold + mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; + if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) { + LOGD(" Setting text white gamma threshold to %s", property); + mWhiteThreshold = atoi(property); + } else { + LOGD(" Using default white black gamma threshold of %d", + DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD); + } + + // Compute the gamma tables + const float blackGamma = gamma; + const float whiteGamma = 1.0f / gamma; + + for (uint32_t i = 0; i <= 255; i++) { + mDefault[i] = i; + + const float v = i / 255.0f; + const float black = pow(v, blackGamma); + const float white = pow(v, whiteGamma); + + mBlackGamma[i] = uint8_t((float)::floor(black * 255.0f + 0.5f)); + mWhiteGamma[i] = uint8_t((float)::floor(white * 255.0f + 0.5f)); + } + + // Configure the font renderers + mDefaultRenderer.setGammaTable(&mDefault[0]); + mBlackGammaRenderer.setGammaTable(&mBlackGamma[0]); + mWhiteGammaRenderer.setGammaTable(&mWhiteGamma[0]); +} + +FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) { + if (paint->getShader() == NULL) { + uint32_t c = paint->getColor(); + const int r = (c >> 16) & 0xFF; + const int g = (c >> 8) & 0xFF; + const int b = (c ) & 0xFF; + const int luminance = (r * 2 + g * 5 + b) >> 3; + + if (luminance <= mBlackThreshold) { + return mBlackGammaRenderer; + } else if (luminance >= mWhiteThreshold) { + return mWhiteGammaRenderer; + } + } + return mDefaultRenderer; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h new file mode 100644 index 0000000..5fa45cf --- /dev/null +++ b/libs/hwui/GammaFontRenderer.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_GAMMA_FONT_RENDERER_H +#define ANDROID_UI_GAMMA_FONT_RENDERER_H + +#include <SkPaint.h> + +#include "FontRenderer.h" + +namespace android { +namespace uirenderer { + +struct GammaFontRenderer { + GammaFontRenderer(); + + FontRenderer& getFontRenderer(const SkPaint* paint); + +private: + FontRenderer mDefaultRenderer; + FontRenderer mBlackGammaRenderer; + FontRenderer mWhiteGammaRenderer; + + int mBlackThreshold; + int mWhiteThreshold; + + uint8_t mDefault[256]; + uint8_t mBlackGamma[256]; + uint8_t mWhiteGamma[256]; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_GAMMA_FONT_RENDERER_H diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp new file mode 100644 index 0000000..97f4cb4 --- /dev/null +++ b/libs/hwui/GradientCache.cpp @@ -0,0 +1,188 @@ +/* + * 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 <GLES2/gl2.h> + +#include <SkCanvas.h> +#include <SkGradientShader.h> + +#include <utils/threads.h> + +#include "GradientCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +GradientCache::GradientCache(): + mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting gradient cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); + } + + mCache.setOnEntryRemovedListener(this); +} + +GradientCache::GradientCache(uint32_t maxByteSize): + mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + mCache.setOnEntryRemovedListener(this); +} + +GradientCache::~GradientCache() { + Mutex::Autolock _l(mLock); + mCache.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t GradientCache::getSize() { + Mutex::Autolock _l(mLock); + return mSize; +} + +uint32_t GradientCache::getMaxSize() { + Mutex::Autolock _l(mLock); + return mMaxSize; +} + +void GradientCache::setMaxSize(uint32_t maxSize) { + Mutex::Autolock _l(mLock); + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void GradientCache::operator()(SkShader*& shader, Texture*& texture) { + // Already locked here + if (shader) { + const uint32_t size = texture->width * texture->height * 4; + mSize -= size; + } + + if (texture) { + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +Texture* GradientCache::get(SkShader* shader) { + Mutex::Autolock _l(mLock); + return mCache.get(shader); +} + +void GradientCache::remove(SkShader* shader) { + Mutex::Autolock _l(mLock); + mCache.remove(shader); +} + +void GradientCache::clear() { + Mutex::Autolock _l(mLock); + mCache.clear(); +} + +Texture* GradientCache::addLinearGradient(SkShader* shader, uint32_t* colors, + float* positions, int count, SkShader::TileMode tileMode) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1); + bitmap.allocPixels(); + bitmap.eraseColor(0); + + SkCanvas canvas(bitmap); + + SkPoint points[2]; + points[0].set(0.0f, 0.0f); + points[1].set(bitmap.width(), 0.0f); + + SkShader* localShader = SkGradientShader::CreateLinear(points, + reinterpret_cast<const SkColor*>(colors), positions, count, tileMode); + + SkPaint p; + p.setStyle(SkPaint::kStrokeAndFill_Style); + p.setShader(localShader)->unref(); + + canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 1.0f, p); + + mLock.lock(); + // Asume the cache is always big enough + const uint32_t size = bitmap.rowBytes() * bitmap.height(); + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + mLock.unlock(); + + Texture* texture = new Texture; + generateTexture(&bitmap, texture); + + mLock.lock(); + mSize += size; + mCache.put(shader, texture); + mLock.unlock(); + + return texture; +} + +void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) { + SkAutoLockPixels autoLock(*bitmap); + if (!bitmap->readyToDraw()) { + LOGE("Cannot generate texture from shader"); + return; + } + + texture->generation = bitmap->getGenerationID(); + texture->width = bitmap->width(); + texture->height = bitmap->height(); + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + + texture->blend = !bitmap->isOpaque(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h new file mode 100644 index 0000000..48877f6 --- /dev/null +++ b/libs/hwui/GradientCache.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_GRADIENT_CACHE_H +#define ANDROID_UI_GRADIENT_CACHE_H + +#include <SkShader.h> + +#include "Texture.h" +#include "utils/GenerationCache.h" + +namespace android { +namespace uirenderer { + +/** + * A simple LRU gradient 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 GradientCache: public OnEntryRemoved<SkShader*, Texture*> { +public: + GradientCache(); + GradientCache(uint32_t maxByteSize); + ~GradientCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(SkShader*& shader, Texture*& texture); + + /** + * Adds a new linear gradient to the cache. The generated texture is + * returned. + */ + Texture* addLinearGradient(SkShader* shader, uint32_t* colors, float* positions, + int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode); + /** + * Returns the texture associated with the specified shader. + */ + Texture* get(SkShader* shader); + /** + * Removes the texture associated with the specified shader. Returns NULL + * if the texture cannot be found. Upon remove the texture is freed. + */ + void remove(SkShader* shader); + /** + * 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(); + +private: + void generateTexture(SkBitmap* bitmap, Texture* texture); + + GenerationCache<SkShader*, Texture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + + /** + * Used to access mCache and mSize. All methods are accessed from a single + * thread except for remove(). + */ + mutable Mutex mLock; +}; // class GradientCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_GRADIENT_CACHE_H diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h new file mode 100644 index 0000000..2afe2fa --- /dev/null +++ b/libs/hwui/Layer.h @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_LAYER_H +#define ANDROID_UI_LAYER_H + +#include <sys/types.h> + +#include <GLES2/gl2.h> + +#include <SkXfermode.h> + +#include "Rect.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Layers +/////////////////////////////////////////////////////////////////////////////// + +/** + * A layer has dimensions and is backed by an OpenGL texture or FBO. + */ +struct Layer { + Layer(const uint32_t layerWidth, const uint32_t layerHeight): + width(layerWidth), height(layerHeight) { + } + + /** + * Bounds of the layer. + */ + Rect layer; + /** + * Texture coordinates of the layer. + */ + Rect texCoords; + + /** + * Name of the FBO used to render the layer. If the name is 0 + * this layer is not backed by an FBO, but a simple texture. + */ + GLuint fbo; + + /** + * Opacity of the layer. + */ + int alpha; + /** + * Blending mode of the layer. + */ + SkXfermode::Mode mode; + /** + * Indicates whether this layer should be blended. + */ + bool blend; + + /** + * Indicates whether this layer has been used already. + */ + bool empty; + + /** + * Name of the texture used to render the layer. + */ + GLuint texture; + /** + * Width of the layer texture. + */ + uint32_t width; + /** + * Height of the layer texture. + */ + uint32_t height; +}; // struct Layer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_LAYER_H diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp new file mode 100644 index 0000000..9ce0359 --- /dev/null +++ b/libs/hwui/LayerCache.cpp @@ -0,0 +1,158 @@ +/* + * 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 <GLES2/gl2.h> + +#include <utils/Log.h> + +#include "LayerCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +LayerCache::LayerCache(): mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting layer cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE); + } +} + +LayerCache::~LayerCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t LayerCache::getSize() { + return mSize; +} + +uint32_t LayerCache::getMaxSize() { + return mMaxSize; +} + +void LayerCache::setMaxSize(uint32_t maxSize) { + clear(); + mMaxSize = maxSize; +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void LayerCache::deleteLayer(Layer* layer) { + if (layer) { + mSize -= layer->width * layer->height * 4; + glDeleteTextures(1, &layer->texture); + delete layer; + } +} + +void LayerCache::clear() { + size_t count = mCache.size(); + for (size_t i = 0; i < count; i++) { + deleteLayer(mCache.itemAt(i).mLayer); + } + mCache.clear(); +} + +Layer* LayerCache::get(const uint32_t width, const uint32_t height) { + Layer* layer = NULL; + + LayerEntry entry(width, height); + ssize_t index = mCache.indexOf(entry); + + if (index >= 0) { + entry = mCache.itemAt(index); + mCache.removeAt(index); + + layer = entry.mLayer; + mSize -= layer->width * layer->height * 4; + + LAYER_LOGD("Reusing layer %dx%d", layer->width, layer->height); + } else { + LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); + + layer = new Layer(entry.mWidth, entry.mHeight); + layer->blend = true; + layer->empty = true; + layer->fbo = 0; + + glGenTextures(1, &layer->texture); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + +#if DEBUG_LAYERS + size_t size = mCache.size(); + for (size_t i = 0; i < size; i++) { + const LayerEntry& entry = mCache.itemAt(i); + LAYER_LOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight); + } +#endif + } + + return layer; +} + +bool LayerCache::put(Layer* layer) { + const uint32_t size = layer->width * layer->height * 4; + // Don't even try to cache a layer that's bigger than the cache + if (size < mMaxSize) { + // TODO: Use an LRU + while (mSize + size > mMaxSize) { + size_t position = 0; +#if LAYER_REMOVE_BIGGEST + position = mCache.size() - 1; +#endif + Layer* victim = mCache.itemAt(position).mLayer; + deleteLayer(victim); + mCache.removeAt(position); + + LAYER_LOGD(" Deleting layer %.2fx%.2f", victim->layer.getWidth(), + victim->layer.getHeight()); + } + + LayerEntry entry(layer); + + mCache.add(entry); + mSize += size; + + return true; + } + return false; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h new file mode 100644 index 0000000..ae792ab --- /dev/null +++ b/libs/hwui/LayerCache.h @@ -0,0 +1,141 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_LAYER_CACHE_H +#define ANDROID_UI_LAYER_CACHE_H + +#include "Layer.h" +#include "utils/SortedList.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#define DEBUG_LAYERS 0 + +// Indicates whether to remove the biggest layers first, or the smaller ones +#define LAYER_REMOVE_BIGGEST 0 +// Textures used by layers must have dimensions multiples of this number +#define LAYER_SIZE 64 + +// Debug +#if DEBUG_LAYERS + #define LAYER_LOGD(...) LOGD(__VA_ARGS__) +#else + #define LAYER_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////////////////////////// + +class LayerCache { +public: + LayerCache(); + ~LayerCache(); + + /** + * Returns a layer large enough for the specified dimensions. If no suitable + * layer can be found, a new one is created and returned. If creating a new + * layer fails, NULL is returned. + * + * When a layer is obtained from the cache, it is removed and the total + * size of the cache goes down. + * + * @param width The desired width of the layer + * @param width The desired height of the layer + */ + Layer* get(const uint32_t width, const uint32_t height); + + /** + * Adds the layer to the cache. The layer will not be added if there is + * not enough space available. Adding a layer can cause other layers to + * be removed from the cache. + * + * @param layer The layer to add to the cache + * + * @return True if the layer was added, false otherwise. + */ + bool put(Layer* layer); + /** + * Clears the cache. This causes all layers 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(); + +private: + void deleteLayer(Layer* layer); + + struct LayerEntry { + LayerEntry(): + mLayer(NULL), mWidth(0), mHeight(0) { + } + + LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) { + mWidth = uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE); + mHeight = uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE); + } + + LayerEntry(const LayerEntry& entry): + mLayer(entry.mLayer), mWidth(entry.mWidth), mHeight(entry.mHeight) { + } + + LayerEntry(Layer* layer): + mLayer(layer), mWidth(layer->width), mHeight(layer->height) { + } + + bool operator<(const LayerEntry& rhs) const { + if (mWidth == rhs.mWidth) { + return mHeight < rhs.mHeight; + } + return mWidth < rhs.mWidth; + } + + bool operator==(const LayerEntry& rhs) const { + return mWidth == rhs.mWidth && mHeight == rhs.mHeight; + } + + Layer* mLayer; + uint32_t mWidth; + uint32_t mHeight; + }; // struct LayerEntry + + SortedList<LayerEntry> mCache; + + uint32_t mSize; + uint32_t mMaxSize; +}; // class LayerCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_LAYER_CACHE_H diff --git a/libs/hwui/Line.h b/libs/hwui/Line.h new file mode 100644 index 0000000..64bdd6a --- /dev/null +++ b/libs/hwui/Line.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_LINE_H +#define ANDROID_UI_LINE_H + +#include <GLES2/gl2.h> + +#include <cmath> + +#include <sys/types.h> + +#include "Patch.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// + +// Alpha8 texture used to perform texture anti-aliasing +static const uint8_t gLineTexture[] = { + 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0 +}; +static const GLsizei gLineTextureWidth = 5; +static const GLsizei gLineTextureHeight = 5; +static const float gLineAABias = 1.0f; + +/////////////////////////////////////////////////////////////////////////////// +// Line +/////////////////////////////////////////////////////////////////////////////// + +class Line { +public: + Line(): mXDivsCount(2), mYDivsCount(2) { + mPatch = new Patch(mXDivsCount, mYDivsCount); + mXDivs = new int32_t[mXDivsCount]; + mYDivs = new int32_t[mYDivsCount]; + + mXDivs[0] = mYDivs[0] = 2; + mXDivs[1] = mYDivs[1] = 3; + + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_2D, mTexture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gLineTextureWidth, gLineTextureHeight, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, gLineTexture); + } + + ~Line() { + delete mPatch; + delete[] mXDivs; + delete[] mYDivs; + + glDeleteTextures(1, &mTexture); + } + + inline float getLength(float x1, float y1, float x2, float y2) { + return sqrtf((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + } + + void update(float x1, float y1, float x2, float y2, float lineWidth, float& tx, float& ty) { + const float length = getLength(x1, y1, x2, y2); + const float half = lineWidth * 0.5f; + + mPatch->updateVertices(gLineTextureWidth, gLineTextureHeight, + -gLineAABias, -half - gLineAABias, length + gLineAABias, half + gLineAABias, + mXDivs, mYDivs, mXDivsCount, mYDivsCount); + + tx = -gLineAABias; + ty = lineWidth <= 1.0f ? -gLineAABias : -half - gLineAABias; + } + + inline GLvoid* getVertices() const { + return &mPatch->vertices[0].position[0]; + } + + inline GLvoid* getTexCoords() const { + return &mPatch->vertices[0].texture[0]; + } + + inline GLsizei getElementsCount() const { + return mPatch->verticesCount; + } + + inline GLuint getTexture() const { + return mTexture; + } + +private: + uint32_t mXDivsCount; + uint32_t mYDivsCount; + + int32_t* mXDivs; + int32_t* mYDivs; + + Patch* mPatch; + + GLuint mTexture; +}; // class Line + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_LINE_H diff --git a/libs/hwui/MODULE_LICENSE_APACHE2 b/libs/hwui/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libs/hwui/MODULE_LICENSE_APACHE2 diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp new file mode 100644 index 0000000..5502e66 --- /dev/null +++ b/libs/hwui/Matrix.cpp @@ -0,0 +1,321 @@ +/* + * 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 <math.h> +#include <stdlib.h> +#include <string.h> + +#include <utils/Log.h> + +#include <SkMatrix.h> + +#include "utils/Compare.h" +#include "Matrix.h" + +namespace android { +namespace uirenderer { + +void Matrix4::loadIdentity() { + data[kScaleX] = 1.0f; + data[kSkewY] = 0.0f; + data[2] = 0.0f; + data[kPerspective0] = 0.0f; + + data[kSkewX] = 0.0f; + data[kScaleY] = 1.0f; + data[6] = 0.0f; + data[kPerspective1] = 0.0f; + + data[8] = 0.0f; + data[9] = 0.0f; + data[kScaleZ] = 1.0f; + data[11] = 0.0f; + + data[kTranslateX] = 0.0f; + data[kTranslateY] = 0.0f; + data[kTranslateZ] = 0.0f; + data[kPerspective2] = 1.0f; + + mSimpleMatrix = true; +} + +bool Matrix4::changesBounds() { + return !(almost(data[0], 1.0f) && almost(data[1], 0.0f) && almost(data[2], 0.0f) && + almost(data[4], 0.0f) && almost(data[5], 1.0f) && almost(data[6], 0.0f) && + almost(data[8], 0.0f) && almost(data[9], 0.0f) && almost(data[10], 1.0f)); +} + +void Matrix4::load(const float* v) { + memcpy(data, v, sizeof(data)); + mSimpleMatrix = false; +} + +void Matrix4::load(const Matrix4& v) { + memcpy(data, v.data, sizeof(data)); + mSimpleMatrix = v.mSimpleMatrix; +} + +void Matrix4::load(const SkMatrix& v) { + memset(data, 0, sizeof(data)); + + data[kScaleX] = v[SkMatrix::kMScaleX]; + data[kSkewX] = v[SkMatrix::kMSkewX]; + data[kTranslateX] = v[SkMatrix::kMTransX]; + + data[kSkewY] = v[SkMatrix::kMSkewY]; + data[kScaleY] = v[SkMatrix::kMScaleY]; + data[kTranslateY] = v[SkMatrix::kMTransY]; + + data[kPerspective0] = v[SkMatrix::kMPersp0]; + data[kPerspective1] = v[SkMatrix::kMPersp1]; + data[kPerspective2] = v[SkMatrix::kMPersp2]; + + data[kScaleZ] = 1.0f; + + mSimpleMatrix = (v.getType() <= SkMatrix::kScale_Mask); +} + +void Matrix4::copyTo(SkMatrix& v) const { + v.reset(); + + v.set(SkMatrix::kMScaleX, data[kScaleX]); + v.set(SkMatrix::kMSkewX, data[kSkewX]); + v.set(SkMatrix::kMTransX, data[kTranslateX]); + + v.set(SkMatrix::kMSkewY, data[kSkewY]); + v.set(SkMatrix::kMScaleY, data[kScaleY]); + v.set(SkMatrix::kMTransY, data[kTranslateY]); + + v.set(SkMatrix::kMPersp0, data[kPerspective0]); + v.set(SkMatrix::kMPersp1, data[kPerspective1]); + v.set(SkMatrix::kMPersp2, data[kPerspective2]); +} + +void Matrix4::loadInverse(const Matrix4& v) { + double scale = 1.0 / + (v.data[kScaleX] * ((double) v.data[kScaleY] * v.data[kPerspective2] - + (double) v.data[kTranslateY] * v.data[kPerspective1]) + + v.data[kSkewX] * ((double) v.data[kTranslateY] * v.data[kPerspective0] - + (double) v.data[kSkewY] * v.data[kPerspective2]) + + v.data[kTranslateX] * ((double) v.data[kSkewY] * v.data[kPerspective1] - + (double) v.data[kScaleY] * v.data[kPerspective0])); + + data[kScaleX] = (v.data[kScaleY] * v.data[kPerspective2] - + v.data[kTranslateY] * v.data[kPerspective1]) * scale; + data[kSkewX] = (v.data[kTranslateX] * v.data[kPerspective1] - + v.data[kSkewX] * v.data[kPerspective2]) * scale; + data[kTranslateX] = (v.data[kSkewX] * v.data[kTranslateY] - + v.data[kTranslateX] * v.data[kScaleY]) * scale; + + data[kSkewY] = (v.data[kTranslateY] * v.data[kPerspective0] - + v.data[kSkewY] * v.data[kPerspective2]) * scale; + data[kScaleY] = (v.data[kScaleX] * v.data[kPerspective2] - + v.data[kTranslateX] * v.data[kPerspective0]) * scale; + data[kTranslateY] = (v.data[kTranslateX] * v.data[kSkewY] - + v.data[kScaleX] * v.data[kTranslateY]) * scale; + + data[kPerspective0] = (v.data[kSkewY] * v.data[kPerspective1] - + v.data[kScaleY] * v.data[kPerspective0]) * scale; + data[kPerspective1] = (v.data[kSkewX] * v.data[kPerspective0] - + v.data[kScaleX] * v.data[kPerspective1]) * scale; + data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] - + v.data[kSkewX] * v.data[kSkewY]) * scale; + + mSimpleMatrix = v.mSimpleMatrix; +} + +void Matrix4::copyTo(float* v) const { + memcpy(v, data, sizeof(data)); +} + +float Matrix4::getTranslateX() { + return data[kTranslateX]; +} + +float Matrix4::getTranslateY() { + return data[kTranslateY]; +} + +void Matrix4::multiply(float v) { + for (int i = 0; i < 16; i++) { + data[i] *= v; + } +} + +void Matrix4::loadTranslate(float x, float y, float z) { + loadIdentity(); + data[kTranslateX] = x; + data[kTranslateY] = y; + data[kTranslateZ] = z; +} + +void Matrix4::loadScale(float sx, float sy, float sz) { + loadIdentity(); + data[kScaleX] = sx; + data[kScaleY] = sy; + data[kScaleZ] = sz; +} + +void Matrix4::loadRotate(float angle, float x, float y, float z) { + data[kPerspective0] = 0.0f; + data[kPerspective1] = 0.0f; + data[11] = 0.0f; + data[kTranslateX] = 0.0f; + data[kTranslateY] = 0.0f; + data[kTranslateZ] = 0.0f; + data[kPerspective2] = 1.0f; + + angle *= float(M_PI / 180.0f); + float c = cosf(angle); + float s = sinf(angle); + + const float length = sqrtf(x * x + y * y + z * z); + float recipLen = 1.0f / length; + x *= recipLen; + y *= recipLen; + z *= recipLen; + + const float nc = 1.0f - c; + const float xy = x * y; + const float yz = y * z; + const float zx = z * x; + const float xs = x * s; + const float ys = y * s; + const float zs = z * s; + + data[kScaleX] = x * x * nc + c; + data[kSkewX] = xy * nc - zs; + data[8] = zx * nc + ys; + data[kSkewY] = xy * nc + zs; + data[kScaleY] = y * y * nc + c; + data[9] = yz * nc - xs; + data[2] = zx * nc - ys; + data[6] = yz * nc + xs; + data[kScaleZ] = z * z * nc + c; + + mSimpleMatrix = false; +} + +void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { + for (int i = 0 ; i < 4 ; i++) { + float x = 0; + float y = 0; + float z = 0; + float w = 0; + + for (int j = 0 ; j < 4 ; j++) { + const float e = v.get(i, j); + x += u.get(j, 0) * e; + y += u.get(j, 1) * e; + z += u.get(j, 2) * e; + w += u.get(j, 3) * e; + } + + set(i, 0, x); + set(i, 1, y); + set(i, 2, z); + set(i, 3, w); + } + + mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix; +} + +void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) { + loadIdentity(); + data[kScaleX] = 2.0f / (right - left); + data[kScaleY] = 2.0f / (top - bottom); + data[kScaleZ] = -2.0f / (far - near); + data[kTranslateX] = -(right + left) / (right - left); + data[kTranslateY] = -(top + bottom) / (top - bottom); + data[kTranslateZ] = -(far + near) / (far - near); +} + +#define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c) + +void Matrix4::mapPoint(float& x, float& y) const { + if (mSimpleMatrix) { + MUL_ADD_STORE(x, data[kScaleX], data[kTranslateX]); + MUL_ADD_STORE(y, data[kScaleY], data[kTranslateY]); + return; + } + + float dx = x * data[kScaleX] + y * data[kSkewX] + data[kTranslateX]; + float dy = x * data[kSkewY] + y * data[kScaleY] + data[kTranslateY]; + float dz = x * data[kPerspective0] + y * data[kPerspective1] + data[kPerspective2]; + if (dz) dz = 1.0f / dz; + + x = dx * dz; + y = dy * dz; +} + +void Matrix4::mapRect(Rect& r) const { + if (mSimpleMatrix) { + MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]); + MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); + MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]); + MUL_ADD_STORE(r.bottom, data[kScaleY], data[kTranslateY]); + return; + } + + float vertices[] = { + r.left, r.top, + r.right, r.top, + r.right, r.bottom, + r.left, r.bottom + }; + + float x, y, z; + + for (int i = 0; i < 8; i+= 2) { + float px = vertices[i]; + float py = vertices[i + 1]; + + x = px * data[kScaleX] + py * data[kSkewX] + data[kTranslateX]; + y = px * data[kSkewY] + py * data[kScaleY] + data[kTranslateY]; + z = px * data[kPerspective0] + py * data[kPerspective1] + data[kPerspective2]; + if (z) z = 1.0f / z; + + vertices[i] = x * z; + vertices[i + 1] = y * z; + } + + r.left = r.right = vertices[0]; + r.top = r.bottom = vertices[1]; + + for (int i = 2; i < 8; i += 2) { + x = vertices[i]; + y = vertices[i + 1]; + + if (x < r.left) r.left = x; + else if (x > r.right) r.right = x; + if (y < r.top) r.top = y; + else if (y > r.bottom) r.bottom = y; + } +} + +void Matrix4::dump() const { + LOGD("Matrix4[simple=%d", mSimpleMatrix); + LOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]); + LOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]); + LOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]); + LOGD(" %f %f %f %f", data[kPerspective0], data[kPerspective1], data[11], data[kPerspective2]); + LOGD("]"); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h new file mode 100644 index 0000000..fe81159 --- /dev/null +++ b/libs/hwui/Matrix.h @@ -0,0 +1,140 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_MATRIX_H +#define ANDROID_UI_MATRIX_H + +#include <SkMatrix.h> + +#include "Rect.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Classes +/////////////////////////////////////////////////////////////////////////////// + +class Matrix4 { +public: + float data[16]; + + enum Entry { + kScaleX = 0, + kSkewY = 1, + kPerspective0 = 3, + kSkewX = 4, + kScaleY = 5, + kPerspective1 = 7, + kScaleZ = 10, + kTranslateX = 12, + kTranslateY = 13, + kTranslateZ = 14, + kPerspective2 = 15 + }; + + Matrix4() { + loadIdentity(); + } + + Matrix4(const float* v) { + load(v); + } + + Matrix4(const Matrix4& v) { + load(v); + } + + Matrix4(const SkMatrix& v) { + load(v); + } + + void loadIdentity(); + + void load(const float* v); + void load(const Matrix4& v); + void load(const SkMatrix& v); + + void loadInverse(const Matrix4& v); + + void loadTranslate(float x, float y, float z); + void loadScale(float sx, float sy, float sz); + void loadRotate(float angle, float x, float y, float z); + void loadMultiply(const Matrix4& u, const Matrix4& v); + + void loadOrtho(float left, float right, float bottom, float top, float near, float far); + + void multiply(const Matrix4& v) { + Matrix4 u; + u.loadMultiply(*this, v); + load(u); + } + + void multiply(float v); + + void translate(float x, float y, float z) { + Matrix4 u; + u.loadTranslate(x, y, z); + multiply(u); + } + + void scale(float sx, float sy, float sz) { + Matrix4 u; + u.loadScale(sx, sy, sz); + multiply(u); + } + + void rotate(float angle, float x, float y, float z) { + Matrix4 u; + u.loadRotate(angle, x, y, z); + multiply(u); + } + + bool changesBounds(); + + void copyTo(float* v) const; + void copyTo(SkMatrix& v) const; + + void mapRect(Rect& r) const; + void mapPoint(float& x, float& y) const; + + float getTranslateX(); + float getTranslateY(); + + void dump() const; + +private: + bool mSimpleMatrix; + + inline float get(int i, int j) const { + return data[i * 4 + j]; + } + + inline void set(int i, int j, float v) { + data[i * 4 + j] = v; + } +}; // class Matrix4 + +/////////////////////////////////////////////////////////////////////////////// +// Types +/////////////////////////////////////////////////////////////////////////////// + +typedef Matrix4 mat4; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_MATRIX_H diff --git a/libs/hwui/NOTICE b/libs/hwui/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/libs/hwui/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/hwui/OpenGLDebugRenderer.cpp b/libs/hwui/OpenGLDebugRenderer.cpp new file mode 100644 index 0000000..d492e23 --- /dev/null +++ b/libs/hwui/OpenGLDebugRenderer.cpp @@ -0,0 +1,116 @@ +/* + * 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/StopWatch.h> + +#include "OpenGLDebugRenderer.h" + +namespace android { +namespace uirenderer { + +void OpenGLDebugRenderer::prepare(bool opaque) { + mPrimitivesCount = 0; + LOGD("========= Frame start ========="); + OpenGLRenderer::prepare(opaque); +} + +void OpenGLDebugRenderer::finish() { + LOGD("========= Frame end ========="); + LOGD("Primitives draw count = %d", mPrimitivesCount); + OpenGLRenderer::finish(); +} + +void OpenGLDebugRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { + mPrimitivesCount++; + StopWatch w("composeLayer"); + return OpenGLRenderer::composeLayer(current, previous); +} + +int OpenGLDebugRenderer::saveLayer(float left, float top, float right, float bottom, + const SkPaint* p, int flags) { + mPrimitivesCount++; + StopWatch w("saveLayer"); + return OpenGLRenderer::saveLayer(left, top, right, bottom, p, flags); +} + +void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawBitmap"); + OpenGLRenderer::drawBitmap(bitmap, left, top, paint); +} + +void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawBitmapMatrix"); + OpenGLRenderer::drawBitmap(bitmap, matrix, paint); +} + +void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawBitmapRect"); + OpenGLRenderer::drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, + dstLeft, dstTop, dstRight, dstBottom, paint); +} + +void OpenGLDebugRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, const SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawPatch"); + OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors, + left, top, right, bottom, paint); +} + +void OpenGLDebugRenderer::drawColor(int color, SkXfermode::Mode mode) { + mPrimitivesCount++; + StopWatch w("drawColor"); + OpenGLRenderer::drawColor(color, mode); +} + +void OpenGLDebugRenderer::drawRect(float left, float top, float right, float bottom, + const SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawRect"); + OpenGLRenderer::drawRect(left, top, right, bottom, paint); +} + +void OpenGLDebugRenderer::drawPath(SkPath* path, SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawPath"); + OpenGLRenderer::drawPath(path, paint); +} + +void OpenGLDebugRenderer::drawLines(float* points, int count, const SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawLines"); + OpenGLRenderer::drawLines(points, count, paint); +} + +void OpenGLDebugRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, + SkPaint* paint) { + mPrimitivesCount++; + StopWatch w("drawText"); + OpenGLRenderer::drawText(text, bytesCount, count, x, y, paint); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h new file mode 100644 index 0000000..4997ef3 --- /dev/null +++ b/libs/hwui/OpenGLDebugRenderer.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_OPENGL_DEBUG_RENDERER_H +#define ANDROID_UI_OPENGL_DEBUG_RENDERER_H + +#include "OpenGLRenderer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + +class OpenGLDebugRenderer: public OpenGLRenderer { +public: + OpenGLDebugRenderer(): mPrimitivesCount(0) { + } + + ~OpenGLDebugRenderer() { + } + + void prepare(bool opaque); + void finish(); + + int saveLayer(float left, float top, float right, float bottom, + const SkPaint* p, int flags); + + void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint); + void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, const SkPaint* paint); + void drawColor(int color, SkXfermode::Mode mode); + void drawRect(float left, float top, float right, float bottom, const SkPaint* paint); + void drawPath(SkPath* path, SkPaint* paint); + void drawLines(float* points, int count, const SkPaint* paint); + void drawText(const char* text, int bytesCount, int count, float x, float y, + SkPaint* paint); + +protected: + void composeLayer(sp<Snapshot> current, sp<Snapshot> previous); + +private: + uint32_t mPrimitivesCount; + +}; // class OpenGLDebugRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_OPENGL_DEBUG_RENDERER_H diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp new file mode 100644 index 0000000..81fa1ea --- /dev/null +++ b/libs/hwui/OpenGLRenderer.cpp @@ -0,0 +1,1366 @@ +/* + * 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 <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <SkCanvas.h> +#include <SkTypeface.h> + +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include "OpenGLRenderer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define REQUIRED_TEXTURE_UNITS_COUNT 3 + +// Generates simple and textured vertices +#define FV(x, y, u, v) { { x, y }, { u, v } } + +#define RAD_TO_DEG (180.0f / 3.14159265f) +#define MIN_ANGLE 0.001f + +// TODO: This should be set in properties +#define ALPHA_THRESHOLD (0x7f / PANEL_BIT_DEPTH) + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// + +// This array is never used directly but used as a memcpy source in the +// OpenGLRenderer constructor +static const TextureVertex gMeshVertices[] = { + FV(0.0f, 0.0f, 0.0f, 0.0f), + FV(1.0f, 0.0f, 1.0f, 0.0f), + FV(0.0f, 1.0f, 0.0f, 1.0f), + FV(1.0f, 1.0f, 1.0f, 1.0f) +}; +static const GLsizei gMeshStride = sizeof(TextureVertex); +static const GLsizei gMeshCount = 4; + +/** + * Structure mapping Skia xfermodes to OpenGL blending factors. + */ +struct Blender { + SkXfermode::Mode mode; + GLenum src; + GLenum dst; +}; // struct Blender + +// In this array, the index of each Blender equals the value of the first +// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] +static const Blender gBlends[] = { + { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } +}; + +// This array contains the swapped version of each SkXfermode. For instance +// this array's SrcOver blending mode is actually DstOver. You can refer to +// createLayer() for more information on the purpose of this array. +static const Blender gBlendsSwap[] = { + { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } +}; + +static const GLenum gTextureUnits[] = { + GL_TEXTURE0, + GL_TEXTURE1, + GL_TEXTURE2 +}; + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) { + mShader = NULL; + mColorFilter = NULL; + mHasShadow = false; + + memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices)); + + mFirstSnapshot = new Snapshot; + + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) { + LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT); + } + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); +} + +OpenGLRenderer::~OpenGLRenderer() { + // The context has already been destroyed at this point, do not call + // GL APIs. All GL state should be kept in Caches.h +} + +/////////////////////////////////////////////////////////////////////////////// +// Setup +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::setViewport(int width, int height) { + glViewport(0, 0, width, height); + mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + + mWidth = width; + mHeight = height; + + mFirstSnapshot->height = height; + mFirstSnapshot->viewport.set(0, 0, width, height); +} + +void OpenGLRenderer::prepare(bool opaque) { + mSnapshot = new Snapshot(mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSaveCount = 1; + + glViewport(0, 0, mWidth, mHeight); + + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + + if (!opaque) { + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, mWidth, mHeight); + + mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight); +} + +void OpenGLRenderer::finish() { +#if DEBUG_OPENGL + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + LOGD("GL error from OpenGLRenderer: 0x%x", status); + } +#endif +} + +void OpenGLRenderer::acquireContext() { + if (mCaches.currentProgram) { + if (mCaches.currentProgram->isInUse()) { + mCaches.currentProgram->remove(); + mCaches.currentProgram = NULL; + } + } +} + +void OpenGLRenderer::releaseContext() { + glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight()); + + glEnable(GL_SCISSOR_TEST); + setScissorFromClip(); + + glDisable(GL_DITHER); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (mCaches.blend) { + glEnable(GL_BLEND); + glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode); + glBlendEquation(GL_FUNC_ADD); + } else { + glDisable(GL_BLEND); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// State management +/////////////////////////////////////////////////////////////////////////////// + +int OpenGLRenderer::getSaveCount() const { + return mSaveCount; +} + +int OpenGLRenderer::save(int flags) { + return saveSnapshot(flags); +} + +void OpenGLRenderer::restore() { + if (mSaveCount > 1) { + restoreSnapshot(); + } +} + +void OpenGLRenderer::restoreToCount(int saveCount) { + if (saveCount < 1) saveCount = 1; + + while (mSaveCount > saveCount) { + restoreSnapshot(); + } +} + +int OpenGLRenderer::saveSnapshot(int flags) { + mSnapshot = new Snapshot(mSnapshot, flags); + return mSaveCount++; +} + +bool OpenGLRenderer::restoreSnapshot() { + bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; + bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; + bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; + + sp<Snapshot> current = mSnapshot; + sp<Snapshot> previous = mSnapshot->previous; + + if (restoreOrtho) { + Rect& r = previous->viewport; + glViewport(r.left, r.top, r.right, r.bottom); + mOrthoMatrix.load(current->orthoMatrix); + } + + mSaveCount--; + mSnapshot = previous; + + if (restoreLayer) { + composeLayer(current, previous); + } + + if (restoreClip) { + setScissorFromClip(); + } + + return restoreClip; +} + +/////////////////////////////////////////////////////////////////////////////// +// Layers +/////////////////////////////////////////////////////////////////////////////// + +int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, + const SkPaint* p, int flags) { + const GLuint previousFbo = mSnapshot->fbo; + const int count = saveSnapshot(flags); + + int alpha = 255; + SkXfermode::Mode mode; + + if (p) { + alpha = p->getAlpha(); + if (!mExtensions.hasFramebufferFetch()) { + const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode); + if (!isMode) { + // Assume SRC_OVER + mode = SkXfermode::kSrcOver_Mode; + } + } else { + mode = getXfermode(p->getXfermode()); + } + } else { + mode = SkXfermode::kSrcOver_Mode; + } + + if (!mSnapshot->previous->invisible) { + createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags, previousFbo); + } + + return count; +} + +int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags) { + if (alpha == 0xff) { + return saveLayer(left, top, right, bottom, NULL, flags); + } else { + SkPaint paint; + paint.setAlpha(alpha); + return saveLayer(left, top, right, bottom, &paint, flags); + } +} + +/** + * Layers are viewed by Skia are slightly different than layers in image editing + * programs (for instance.) When a layer is created, previously created layers + * and the frame buffer still receive every drawing command. For instance, if a + * layer is created and a shape intersecting the bounds of the layers and the + * framebuffer is draw, the shape will be drawn on both (unless the layer was + * created with the SkCanvas::kClipToLayer_SaveFlag flag.) + * + * A way to implement layers is to create an FBO for each layer, backed by an RGBA + * texture. Unfortunately, this is inefficient as it requires every primitive to + * be drawn n + 1 times, where n is the number of active layers. In practice this + * means, for every primitive: + * - Switch active frame buffer + * - Change viewport, clip and projection matrix + * - Issue the drawing + * + * Switching rendering target n + 1 times per drawn primitive is extremely costly. + * To avoid this, layers are implemented in a different way here, at least in the + * general case. FBOs are used, as an optimization, when the "clip to layer" flag + * is set. When this flag is set we can redirect all drawing operations into a + * single FBO. + * + * This implementation relies on the frame buffer being at least RGBA 8888. When + * a layer is created, only a texture is created, not an FBO. The content of the + * frame buffer contained within the layer's bounds is copied into this texture + * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame + * buffer and drawing continues as normal. This technique therefore treats the + * frame buffer as a scratch buffer for the layers. + * + * To compose the layers back onto the frame buffer, each layer texture + * (containing the original frame buffer data) is drawn as a simple quad over + * the frame buffer. The trick is that the quad is set as the composition + * destination in the blending equation, and the frame buffer becomes the source + * of the composition. + * + * Drawing layers with an alpha value requires an extra step before composition. + * An empty quad is drawn over the layer's region in the frame buffer. This quad + * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the + * quad is used to multiply the colors in the frame buffer. This is achieved by + * changing the GL blend functions for the GL_FUNC_ADD blend equation to + * GL_ZERO, GL_SRC_ALPHA. + * + * Because glCopyTexImage2D() can be slow, an alternative implementation might + * be use to draw a single clipped layer. The implementation described above + * is correct in every case. + * + * (1) The frame buffer is actually not cleared right away. To allow the GPU + * to potentially optimize series of calls to glCopyTexImage2D, the frame + * buffer is left untouched until the first drawing operation. Only when + * something actually gets drawn are the layers regions cleared. + */ +bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, + float right, float bottom, int alpha, SkXfermode::Mode mode, + int flags, GLuint previousFbo) { + LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); + LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); + + const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag; + + // Window coordinates of the layer + Rect bounds(left, top, right, bottom); + if (!fboLayer) { + mSnapshot->transform->mapRect(bounds); + // Layers only make sense if they are in the framebuffer's bounds + bounds.intersect(*mSnapshot->clipRect); + bounds.snapToPixelBoundaries(); + } + + if (bounds.isEmpty() || bounds.getWidth() > mMaxTextureSize || + bounds.getHeight() > mMaxTextureSize) { + snapshot->invisible = true; + } else { + // TODO: Should take the mode into account + snapshot->invisible = snapshot->previous->invisible || + (alpha <= ALPHA_THRESHOLD && fboLayer); + } + + // Bail out if we won't draw in this snapshot + if (snapshot->invisible) { + return false; + } + + glActiveTexture(GL_TEXTURE0); + + Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight()); + if (!layer) { + return false; + } + + layer->mode = mode; + layer->alpha = alpha; + layer->layer.set(bounds); + layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->height), + bounds.getWidth() / float(layer->width), 0.0f); + + // Save the layer in the snapshot + snapshot->flags |= Snapshot::kFlagIsLayer; + snapshot->layer = layer; + + if (fboLayer) { + layer->fbo = mCaches.fboCache.get(); + + snapshot->flags |= Snapshot::kFlagIsFboLayer; + snapshot->fbo = layer->fbo; + snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); + snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + snapshot->height = bounds.getHeight(); + snapshot->flags |= Snapshot::kFlagDirtyOrtho; + snapshot->orthoMatrix.load(mOrthoMatrix); + + // Bind texture to FBO + glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + // Initialize the texture if needed + if (layer->empty) { + layer->empty = false; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + layer->texture, 0); + +#if DEBUG_LAYERS + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LOGE("Framebuffer incomplete (GL error code 0x%x)", status); + + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + glDeleteTextures(1, &layer->texture); + mCaches.fboCache.put(layer->fbo); + + delete layer; + + return false; + } +#endif + + // Clear the FBO + glScissor(0.0f, 0.0f, bounds.getWidth() + 1.0f, bounds.getHeight() + 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + setScissorFromClip(); + + // Change the ortho projection + glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); + mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + } else { + // Copy the framebuffer into the layer + glBindTexture(GL_TEXTURE_2D, layer->texture); + + if (layer->empty) { + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom, + layer->width, layer->height, 0); + layer->empty = false; + } else { + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom, + bounds.getWidth(), bounds.getHeight()); + } + + // Enqueue the buffer coordinates to clear the corresponding region later + mLayers.push(new Rect(bounds)); + } + + return true; +} + +/** + * Read the documentation of createLayer() before doing anything in this method. + */ +void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { + if (!current->layer) { + LOGE("Attempting to compose a layer that does not exist"); + return; + } + + const bool fboLayer = current->flags & SkCanvas::kClipToLayer_SaveFlag; + + if (fboLayer) { + // Unbind current FBO and restore previous one + glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + } + + // Restore the clip from the previous snapshot + const Rect& clip = *previous->clipRect; + glScissor(clip.left, previous->height - clip.bottom, clip.getWidth(), clip.getHeight()); + + Layer* layer = current->layer; + const Rect& rect = layer->layer; + + if (!fboLayer && layer->alpha < 255) { + drawColorRect(rect.left, rect.top, rect.right, rect.bottom, + layer->alpha << 24, SkXfermode::kDstIn_Mode, true); + } + + const Rect& texCoords = layer->texCoords; + resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); + + if (fboLayer) { + drawTextureRect(rect.left, rect.top, rect.right, rect.bottom, + layer->texture, layer->alpha / 255.0f, layer->mode, layer->blend); + } else { + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + 1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, true, true); + } + + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + + if (fboLayer) { + // Detach the texture from the FBO + glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + + // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed + mCaches.fboCache.put(current->fbo); + } + + // Failing to add the layer to the cache should happen only if the layer is too large + if (!mCaches.layerCache.put(layer)) { + LAYER_LOGD("Deleting layer"); + glDeleteTextures(1, &layer->texture); + delete layer; + } +} + +void OpenGLRenderer::clearLayerRegions() { + if (mLayers.size() == 0 || mSnapshot->invisible) return; + + for (uint32_t i = 0; i < mLayers.size(); i++) { + Rect* bounds = mLayers.itemAt(i); + + // Clear the framebuffer where the layer will draw + glScissor(bounds->left, mSnapshot->height - bounds->bottom, + bounds->getWidth(), bounds->getHeight()); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + delete bounds; + } + mLayers.clear(); + + // Restore the clip + setScissorFromClip(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Transforms +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::translate(float dx, float dy) { + mSnapshot->transform->translate(dx, dy, 0.0f); +} + +void OpenGLRenderer::rotate(float degrees) { + mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); +} + +void OpenGLRenderer::scale(float sx, float sy) { + mSnapshot->transform->scale(sx, sy, 1.0f); +} + +void OpenGLRenderer::setMatrix(SkMatrix* matrix) { + mSnapshot->transform->load(*matrix); +} + +const float* OpenGLRenderer::getMatrix() const { + if (mSnapshot->fbo != 0) { + return &mSnapshot->transform->data[0]; + } + return &mIdentity.data[0]; +} + +void OpenGLRenderer::getMatrix(SkMatrix* matrix) { + mSnapshot->transform->copyTo(*matrix); +} + +void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { + mat4 m(*matrix); + mSnapshot->transform->multiply(m); +} + +/////////////////////////////////////////////////////////////////////////////// +// Clipping +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::setScissorFromClip() { + const Rect& clip = *mSnapshot->clipRect; + glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight()); +} + +const Rect& OpenGLRenderer::getClipBounds() { + return mSnapshot->getLocalClip(); +} + +bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) { + if (mSnapshot->invisible) { + return true; + } + + Rect r(left, top, right, bottom); + mSnapshot->transform->mapRect(r); + r.snapToPixelBoundaries(); + + Rect clipRect(*mSnapshot->clipRect); + clipRect.snapToPixelBoundaries(); + + return !clipRect.intersects(r); +} + +bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { + bool clipped = mSnapshot->clip(left, top, right, bottom, op); + if (clipped) { + setScissorFromClip(); + } + return !mSnapshot->clipRect->isEmpty(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Drawing +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) { + const float right = left + bitmap->width(); + const float bottom = top + bitmap->height(); + + if (quickReject(left, top, right, bottom)) { + return; + } + + glActiveTexture(GL_TEXTURE0); + const Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + drawTextureRect(left, top, right, bottom, texture, paint); +} + +void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) { + Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); + const mat4 transform(*matrix); + transform.mapRect(r); + + if (quickReject(r.left, r.top, r.right, r.bottom)) { + return; + } + + glActiveTexture(GL_TEXTURE0); + const Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint); +} + +void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, + float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) { + if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) { + return; + } + + glActiveTexture(GL_TEXTURE0); + const Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + const float width = texture->width; + const float height = texture->height; + + const float u1 = srcLeft / width; + const float v1 = srcTop / height; + const float u2 = srcRight / width; + const float v2 = srcBottom / height; + + resetDrawTextureTexCoords(u1, v1, u2, v2); + + drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture, paint); + + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); +} + +void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, const SkPaint* paint) { + if (quickReject(left, top, right, bottom)) { + return; + } + + glActiveTexture(GL_TEXTURE0); + const Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(), + right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors); + + // Specify right and bottom as +1.0f from left/top to prevent scaling since the + // patch mesh already defines the final size + drawTextureMesh(left, top, left + 1.0f, top + 1.0f, texture->id, alpha / 255.0f, + mode, texture->blend, &mesh->vertices[0].position[0], + &mesh->vertices[0].texture[0], GL_TRIANGLES, mesh->verticesCount); +} + +void OpenGLRenderer::drawLines(float* points, int count, const SkPaint* paint) { + if (mSnapshot->invisible) return; + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + uint32_t color = paint->getColor(); + const GLfloat a = alpha / 255.0f; + const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f; + const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f; + const GLfloat b = a * ((color ) & 0xFF) / 255.0f; + + const bool isAA = paint->isAntiAlias(); + if (isAA) { + GLuint textureUnit = 0; + setupTextureAlpha8(mCaches.line.getTexture(), 0, 0, textureUnit, 0.0f, 0.0f, r, g, b, a, + mode, false, true, mCaches.line.getVertices(), mCaches.line.getTexCoords()); + } else { + setupColorRect(0.0f, 0.0f, 1.0f, 1.0f, r, g, b, a, mode, false); + } + + const float strokeWidth = paint->getStrokeWidth(); + const GLsizei elementsCount = isAA ? mCaches.line.getElementsCount() : gMeshCount; + const GLenum drawMode = isAA ? GL_TRIANGLES : GL_TRIANGLE_STRIP; + + for (int i = 0; i < count; i += 4) { + float tx = 0.0f; + float ty = 0.0f; + + if (isAA) { + mCaches.line.update(points[i], points[i + 1], points[i + 2], points[i + 3], + strokeWidth, tx, ty); + } else { + ty = strokeWidth <= 1.0f ? 0.0f : -strokeWidth * 0.5f; + } + + const float dx = points[i + 2] - points[i]; + const float dy = points[i + 3] - points[i + 1]; + const float mag = sqrtf(dx * dx + dy * dy); + const float angle = acos(dx / mag); + + mModelView.loadTranslate(points[i], points[i + 1], 0.0f); + if (angle > MIN_ANGLE || angle < -MIN_ANGLE) { + mModelView.rotate(angle * RAD_TO_DEG, 0.0f, 0.0f, 1.0f); + } + mModelView.translate(tx, ty, 0.0f); + if (!isAA) { + float length = mCaches.line.getLength(points[i], points[i + 1], + points[i + 2], points[i + 3]); + mModelView.scale(length, strokeWidth, 1.0f); + } + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + + if (mShader) { + mShader->updateTransforms(mCaches.currentProgram, mModelView, *mSnapshot); + } + + glDrawArrays(drawMode, 0, elementsCount); + } + + if (isAA) { + glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); + } +} + +void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { + const Rect& clip = *mSnapshot->clipRect; + drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true); +} + +void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) { + if (quickReject(left, top, right, bottom)) { + return; + } + + SkXfermode::Mode mode; + if (!mExtensions.hasFramebufferFetch()) { + const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode); + if (!isMode) { + // Assume SRC_OVER + mode = SkXfermode::kSrcOver_Mode; + } + } else { + mode = getXfermode(p->getXfermode()); + } + + // Skia draws using the color's alpha channel if < 255 + // Otherwise, it uses the paint's alpha + int color = p->getColor(); + if (((color >> 24) & 0xff) == 255) { + color |= p->getAlpha() << 24; + } + + drawColorRect(left, top, right, bottom, color, mode); +} + +void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, + float x, float y, SkPaint* paint) { + if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { + return; + } + if (mSnapshot->invisible) return; + + paint->setAntiAlias(true); + + float length = -1.0f; + switch (paint->getTextAlign()) { + case SkPaint::kCenter_Align: + length = paint->measureText(text, bytesCount); + x -= length / 2.0f; + break; + case SkPaint::kRight_Align: + length = paint->measureText(text, bytesCount); + x -= length; + break; + default: + break; + } + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + uint32_t color = paint->getColor(); + const GLfloat a = alpha / 255.0f; + const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f; + const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f; + const GLfloat b = a * ((color ) & 0xFF) / 255.0f; + + FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint); + fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), + paint->getTextSize()); + + if (mHasShadow) { + glActiveTexture(gTextureUnits[0]); + mCaches.dropShadowCache.setFontRenderer(fontRenderer); + const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount, + count, mShadowRadius); + const AutoTexture autoCleanup(shadow); + + setupShadow(shadow, x, y, mode, a); + + // Draw the mesh + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); + } + + GLuint textureUnit = 0; + glActiveTexture(gTextureUnits[textureUnit]); + + // Assume that the modelView matrix does not force scales, rotates, etc. + const bool linearFilter = mSnapshot->transform->changesBounds(); + setupTextureAlpha8(fontRenderer.getTexture(linearFilter), 0, 0, textureUnit, + x, y, r, g, b, a, mode, false, true); + + const Rect& clip = mSnapshot->getLocalClip(); + clearLayerRegions(); + fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); + + drawTextDecorations(text, bytesCount, length, x, y, paint); +} + +void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { + if (mSnapshot->invisible) return; + + GLuint textureUnit = 0; + glActiveTexture(gTextureUnits[textureUnit]); + + const PathTexture* texture = mCaches.pathCache.get(path, paint); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + const float x = texture->left - texture->offset; + const float y = texture->top - texture->offset; + + if (quickReject(x, y, x + texture->width, y + texture->height)) { + return; + } + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + uint32_t color = paint->getColor(); + const GLfloat a = alpha / 255.0f; + const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f; + const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f; + const GLfloat b = a * ((color ) & 0xFF) / 255.0f; + + setupTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true, true); + + clearLayerRegions(); + + // Draw the mesh + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); +} + +/////////////////////////////////////////////////////////////////////////////// +// Shaders +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetShader() { + mShader = NULL; +} + +void OpenGLRenderer::setupShader(SkiaShader* shader) { + mShader = shader; + if (mShader) { + mShader->set(&mCaches.textureCache, &mCaches.gradientCache); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Color filters +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetColorFilter() { + mColorFilter = NULL; +} + +void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) { + mColorFilter = filter; +} + +/////////////////////////////////////////////////////////////////////////////// +// Drop shadow +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetShadow() { + mHasShadow = false; +} + +void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) { + mHasShadow = true; + mShadowRadius = radius; + mShadowDx = dx; + mShadowDy = dy; + mShadowColor = color; +} + +/////////////////////////////////////////////////////////////////////////////// +// Drawing implementation +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::setupShadow(const ShadowTexture* texture, float x, float y, + SkXfermode::Mode mode, float alpha) { + const float sx = x - texture->left + mShadowDx; + const float sy = y - texture->top + mShadowDy; + + const int shadowAlpha = ((mShadowColor >> 24) & 0xFF); + const GLfloat a = shadowAlpha < 255 ? shadowAlpha / 255.0f : alpha; + const GLfloat r = a * ((mShadowColor >> 16) & 0xFF) / 255.0f; + const GLfloat g = a * ((mShadowColor >> 8) & 0xFF) / 255.0f; + const GLfloat b = a * ((mShadowColor ) & 0xFF) / 255.0f; + + GLuint textureUnit = 0; + setupTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, true, false); +} + +void OpenGLRenderer::setupTextureAlpha8(const Texture* texture, GLuint& textureUnit, + float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode, + bool transforms, bool applyFilters) { + setupTextureAlpha8(texture->id, texture->width, texture->height, textureUnit, + x, y, r, g, b, a, mode, transforms, applyFilters, + &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]); +} + +void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height, + GLuint& textureUnit, float x, float y, float r, float g, float b, float a, + SkXfermode::Mode mode, bool transforms, bool applyFilters) { + setupTextureAlpha8(texture, width, height, textureUnit, + x, y, r, g, b, a, mode, transforms, applyFilters, + &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]); +} + +void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height, + GLuint& textureUnit, float x, float y, float r, float g, float b, float a, + SkXfermode::Mode mode, bool transforms, bool applyFilters, + GLvoid* vertices, GLvoid* texCoords) { + // Describe the required shaders + ProgramDescription description; + description.hasTexture = true; + description.hasAlpha8Texture = true; + const bool setColor = description.setAlpha8Color(r, g, b, a); + + if (applyFilters) { + if (mShader) { + mShader->describe(description, mExtensions); + } + if (mColorFilter) { + mColorFilter->describe(description, mExtensions); + } + } + + // Setup the blending mode + chooseBlending(true, mode, description); + + // Build and use the appropriate shader + useProgram(mCaches.programCache.get(description)); + + bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit); + + int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(texCoordsSlot); + + // Setup attributes + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gMeshStride, vertices); + glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, + gMeshStride, texCoords); + + // Setup uniforms + if (transforms) { + mModelView.loadTranslate(x, y, 0.0f); + mModelView.scale(width, height, 1.0f); + } else { + mModelView.loadIdentity(); + } + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + if (setColor) { + mCaches.currentProgram->setColor(r, g, b, a); + } + + textureUnit++; + if (applyFilters) { + // Setup attributes and uniforms required by the shaders + if (mShader) { + mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit); + } + if (mColorFilter) { + mColorFilter->setupProgram(mCaches.currentProgram); + } + } +} + +// Same values used by Skia +#define kStdStrikeThru_Offset (-6.0f / 21.0f) +#define kStdUnderline_Offset (1.0f / 9.0f) +#define kStdUnderline_Thickness (1.0f / 18.0f) + +void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length, + float x, float y, SkPaint* paint) { + // Handle underline and strike-through + uint32_t flags = paint->getFlags(); + if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { + float underlineWidth = length; + // If length is > 0.0f, we already measured the text for the text alignment + if (length <= 0.0f) { + underlineWidth = paint->measureText(text, bytesCount); + } + + float offsetX = 0; + switch (paint->getTextAlign()) { + case SkPaint::kCenter_Align: + offsetX = underlineWidth * 0.5f; + break; + case SkPaint::kRight_Align: + offsetX = underlineWidth; + break; + default: + break; + } + + if (underlineWidth > 0.0f) { + const float textSize = paint->getTextSize(); + const float strokeWidth = textSize * kStdUnderline_Thickness; + + const float left = x - offsetX; + float top = 0.0f; + + const int pointsCount = 4 * (flags & SkPaint::kStrikeThruText_Flag ? 2 : 1); + float points[pointsCount]; + int currentPoint = 0; + + if (flags & SkPaint::kUnderlineText_Flag) { + top = y + textSize * kStdUnderline_Offset; + points[currentPoint++] = left; + points[currentPoint++] = top; + points[currentPoint++] = left + underlineWidth; + points[currentPoint++] = top; + } + + if (flags & SkPaint::kStrikeThruText_Flag) { + top = y + textSize * kStdStrikeThru_Offset; + points[currentPoint++] = left; + points[currentPoint++] = top; + points[currentPoint++] = left + underlineWidth; + points[currentPoint++] = top; + } + + SkPaint linesPaint(*paint); + linesPaint.setStrokeWidth(strokeWidth); + + drawLines(&points[0], pointsCount, &linesPaint); + } + } +} + +void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, + int color, SkXfermode::Mode mode, bool ignoreTransform) { + clearLayerRegions(); + + // If a shader is set, preserve only the alpha + if (mShader) { + color |= 0x00ffffff; + } + + // Render using pre-multiplied alpha + const int alpha = (color >> 24) & 0xFF; + const GLfloat a = alpha / 255.0f; + const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f; + const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f; + const GLfloat b = a * ((color ) & 0xFF) / 255.0f; + + setupColorRect(left, top, right, bottom, r, g, b, a, mode, ignoreTransform); + + // Draw the mesh + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); +} + +void OpenGLRenderer::setupColorRect(float left, float top, float right, float bottom, + float r, float g, float b, float a, SkXfermode::Mode mode, bool ignoreTransform) { + GLuint textureUnit = 0; + + // Describe the required shaders + ProgramDescription description; + const bool setColor = description.setColor(r, g, b, a); + + if (mShader) { + mShader->describe(description, mExtensions); + } + if (mColorFilter) { + mColorFilter->describe(description, mExtensions); + } + + // Setup the blending mode + chooseBlending(a < 1.0f || (mShader && mShader->blend()), mode, description); + + // Build and use the appropriate shader + useProgram(mCaches.programCache.get(description)); + + // Setup attributes + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gMeshStride, &mMeshVertices[0].position[0]); + + // Setup uniforms + mModelView.loadTranslate(left, top, 0.0f); + mModelView.scale(right - left, bottom - top, 1.0f); + if (!ignoreTransform) { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + } else { + mat4 identity; + mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity); + } + mCaches.currentProgram->setColor(r, g, b, a); + + // Setup attributes and uniforms required by the shaders + if (mShader) { + mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit); + } + if (mColorFilter) { + mColorFilter->setupProgram(mCaches.currentProgram); + } +} + +void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, + const Texture* texture, const SkPaint* paint) { + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, + texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + GL_TRIANGLE_STRIP, gMeshCount); +} + +void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, + GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) { + drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend, + &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + GL_TRIANGLE_STRIP, gMeshCount); +} + +void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom, + GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, + GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, + bool swapSrcDst, bool ignoreTransform) { + clearLayerRegions(); + + ProgramDescription description; + description.hasTexture = true; + const bool setColor = description.setColor(alpha, alpha, alpha, alpha); + if (mColorFilter) { + mColorFilter->describe(description, mExtensions); + } + + mModelView.loadTranslate(left, top, 0.0f); + mModelView.scale(right - left, bottom - top, 1.0f); + + chooseBlending(blend || alpha < 1.0f, mode, description, swapSrcDst); + + useProgram(mCaches.programCache.get(description)); + if (!ignoreTransform) { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + } else { + mat4 m; + mCaches.currentProgram->set(mOrthoMatrix, mModelView, m); + } + + // Texture + bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0); + + // Always premultiplied + if (setColor) { + mCaches.currentProgram->setColor(alpha, alpha, alpha, alpha); + } + + // Mesh + int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(texCoordsSlot); + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gMeshStride, vertices); + glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords); + + // Color filter + if (mColorFilter) { + mColorFilter->setupProgram(mCaches.currentProgram); + } + + glDrawArrays(drawMode, 0, elementsCount); + glDisableVertexAttribArray(texCoordsSlot); +} + +void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, + ProgramDescription& description, bool swapSrcDst) { + blend = blend || mode != SkXfermode::kSrcOver_Mode; + if (blend) { + if (mode < SkXfermode::kPlus_Mode) { + if (!mCaches.blend) { + glEnable(GL_BLEND); + } + + GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src; + GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst; + + if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) { + glBlendFunc(sourceMode, destMode); + mCaches.lastSrcMode = sourceMode; + mCaches.lastDstMode = destMode; + } + } else { + // These blend modes are not supported by OpenGL directly and have + // to be implemented using shaders. Since the shader will perform + // the blending, turn blending off here + if (mExtensions.hasFramebufferFetch()) { + description.framebufferMode = mode; + description.swapSrcDst = swapSrcDst; + } + + if (mCaches.blend) { + glDisable(GL_BLEND); + } + blend = false; + } + } else if (mCaches.blend) { + glDisable(GL_BLEND); + } + mCaches.blend = blend; +} + +bool OpenGLRenderer::useProgram(Program* program) { + if (!program->isInUse()) { + if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove(); + program->use(); + mCaches.currentProgram = program; + return false; + } + return true; +} + +void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) { + TextureVertex* v = &mMeshVertices[0]; + TextureVertex::setUV(v++, u1, v1); + TextureVertex::setUV(v++, u2, v1); + TextureVertex::setUV(v++, u1, v2); + TextureVertex::setUV(v++, u2, v2); +} + +void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { + if (paint) { + if (!mExtensions.hasFramebufferFetch()) { + const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode); + if (!isMode) { + // Assume SRC_OVER + *mode = SkXfermode::kSrcOver_Mode; + } + } else { + *mode = getXfermode(paint->getXfermode()); + } + + // Skia draws using the color's alpha channel if < 255 + // Otherwise, it uses the paint's alpha + int color = paint->getColor(); + *alpha = (color >> 24) & 0xFF; + if (*alpha == 255) { + *alpha = paint->getAlpha(); + } + } else { + *mode = SkXfermode::kSrcOver_Mode; + *alpha = 255; + } +} + +SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) { + if (mode == NULL) { + return SkXfermode::kSrcOver_Mode; + } + return mode->fMode; +} + +void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) { + glActiveTexture(gTextureUnits[textureUnit]); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h new file mode 100644 index 0000000..423614b --- /dev/null +++ b/libs/hwui/OpenGLRenderer.h @@ -0,0 +1,445 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_OPENGL_RENDERER_H +#define ANDROID_UI_OPENGL_RENDERER_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <SkBitmap.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkRegion.h> +#include <SkShader.h> +#include <SkXfermode.h> + +#include <utils/RefBase.h> +#include <utils/Vector.h> + +#include "Extensions.h" +#include "Matrix.h" +#include "Program.h" +#include "Rect.h" +#include "Snapshot.h" +#include "Vertex.h" +#include "SkiaShader.h" +#include "SkiaColorFilter.h" +#include "Caches.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#define DEBUG_OPENGL 1 + +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + +class DisplayListRenderer; + +/** + * OpenGL renderer used to draw accelerated 2D graphics. The API is a + * simplified version of Skia's Canvas API. + */ +class OpenGLRenderer { +public: + OpenGLRenderer(); + virtual ~OpenGLRenderer(); + + virtual void setViewport(int width, int height); + + virtual void prepare(bool opaque); + virtual void finish(); + + virtual void acquireContext(); + virtual void releaseContext(); + + int getSaveCount() const; + virtual int save(int flags); + virtual void restore(); + virtual void restoreToCount(int saveCount); + + virtual int saveLayer(float left, float top, float right, float bottom, + const SkPaint* p, int flags); + virtual int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags); + + virtual void translate(float dx, float dy); + virtual void rotate(float degrees); + virtual void scale(float sx, float sy); + + const float* getMatrix() const; + void getMatrix(SkMatrix* matrix); + virtual void setMatrix(SkMatrix* matrix); + virtual void concatMatrix(SkMatrix* matrix); + + const Rect& getClipBounds(); + bool quickReject(float left, float top, float right, float bottom); + virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + + virtual void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint); + virtual void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint); + virtual void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint); + virtual void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, const SkPaint* paint); + virtual void drawColor(int color, SkXfermode::Mode mode); + virtual void drawRect(float left, float top, float right, float bottom, const SkPaint* paint); + virtual void drawPath(SkPath* path, SkPaint* paint); + virtual void drawLines(float* points, int count, const SkPaint* paint); + virtual void drawText(const char* text, int bytesCount, int count, float x, float y, + SkPaint* paint); + + virtual void resetShader(); + virtual void setupShader(SkiaShader* shader); + + virtual void resetColorFilter(); + virtual void setupColorFilter(SkiaColorFilter* filter); + + virtual void resetShadow(); + virtual void setupShadow(float radius, float dx, float dy, int color); + +protected: + /** + * Compose the layer defined in the current snapshot with the layer + * defined by the previous snapshot. + * + * The current snapshot *must* be a layer (flag kFlagIsLayer set.) + * + * @param curent The current snapshot containing the layer to compose + * @param previous The previous snapshot to compose the current layer with + */ + virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous); + +private: + /** + * Saves the current state of the renderer as a new snapshot. + * The new snapshot is saved in mSnapshot and the previous snapshot + * is linked from mSnapshot->previous. + * + * @param flags The save flags; see SkCanvas for more information + * + * @return The new save count. This value can be passed to #restoreToCount() + */ + int saveSnapshot(int flags); + + /** + * Restores the current snapshot; mSnapshot becomes mSnapshot->previous. + * + * @return True if the clip was modified. + */ + bool restoreSnapshot(); + + /** + * Sets the clipping rectangle using glScissor. The clip is defined by + * the current snapshot's clipRect member. + */ + void setScissorFromClip(); + + /** + * Creates a new layer stored in the specified snapshot. + * + * @param snapshot The snapshot associated with the new layer + * @param left The left coordinate of the layer + * @param top The top coordinate of the layer + * @param right The right coordinate of the layer + * @param bottom The bottom coordinate of the layer + * @param alpha The translucency of the layer + * @param mode The blending mode of the layer + * @param flags The layer save flags + * @param previousFbo The name of the current framebuffer + * + * @return True if the layer was successfully created, false otherwise + */ + bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, + int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo); + + /** + * Clears all the regions corresponding to the current list of layers. + * This method MUST be invoked before any drawing operation. + */ + void clearLayerRegions(); + + /** + * Draws a colored rectangle with the specified color. The specified coordinates + * are transformed by the current snapshot's transform matrix. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param color The rectangle's ARGB color, defined as a packed 32 bits word + * @param mode The Skia xfermode to use + * @param ignoreTransform True if the current transform should be ignored + * @paran ignoreBlending True if the blending is set by the caller + */ + void drawColorRect(float left, float top, float right, float bottom, + int color, SkXfermode::Mode mode, bool ignoreTransform = false); + + /** + * Setups shaders to draw a colored rect. + */ + void setupColorRect(float left, float top, float right, float bottom, + float r, float g, float b, float a, SkXfermode::Mode mode, bool ignoreTransform); + + /** + * Draws a textured rectangle with the specified texture. The specified coordinates + * are transformed by the current snapshot's transform matrix. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param texture The texture name to map onto the rectangle + * @param alpha An additional translucency parameter, between 0.0f and 1.0f + * @param mode The blending mode + * @param blend True if the texture contains an alpha channel + */ + void drawTextureRect(float left, float top, float right, float bottom, GLuint texture, + float alpha, SkXfermode::Mode mode, bool blend); + + /** + * Draws a textured rectangle with the specified texture. The specified coordinates + * are transformed by the current snapshot's transform matrix. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param texture The texture to use + * @param paint The paint containing the alpha, blending mode, etc. + */ + void drawTextureRect(float left, float top, float right, float bottom, + const Texture* texture, const SkPaint* paint); + + /** + * Draws a textured mesh with the specified texture. If the indices are omitted, the + * mesh is drawn as a simple quad. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param texture The texture name to map onto the rectangle + * @param alpha An additional translucency parameter, between 0.0f and 1.0f + * @param mode The blending mode + * @param blend True if the texture contains an alpha channel + * @param vertices The vertices that define the mesh + * @param texCoords The texture coordinates of each vertex + * @param indices The indices of the vertices, can be NULL + * @param elementsCount The number of elements in the mesh, required by indices + * @param swapSrcDst Whether or not the src and dst blending operations should be swapped + * @param ignoreTransform True if the current transform should be ignored + */ + void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, + float alpha, SkXfermode::Mode mode, bool blend, + GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, + bool swapSrcDst = false, bool ignoreTransform = false); + + /** + * Prepares the renderer to draw the specified shadow. + * + * @param texture The shadow texture + * @param x The x coordinate of the shadow + * @param y The y coordinate of the shadow + * @param mode The blending mode + * @param alpha The alpha value + */ + void setupShadow(const ShadowTexture* texture, float x, float y, SkXfermode::Mode mode, + float alpha); + + /** + * Prepares the renderer to draw the specified Alpha8 texture as a rectangle. + * + * @param texture The texture to render with + * @param textureUnit The texture unit to use, may be modified + * @param x The x coordinate of the rectangle to draw + * @param y The y coordinate of the rectangle to draw + * @param r The red component of the color + * @param g The green component of the color + * @param b The blue component of the color + * @param a The alpha component of the color + * @param mode The blending mode + * @param transforms True if the matrix passed to the shader should be multiplied + * by the model-view matrix + * @param applyFilters Whether or not to take color filters and + * shaders into account + */ + void setupTextureAlpha8(const Texture* texture, GLuint& textureUnit, float x, float y, + float r, float g, float b, float a, SkXfermode::Mode mode, bool transforms, + bool applyFilters); + + /** + * Prepares the renderer to draw the specified Alpha8 texture as a rectangle. + * + * @param texture The texture to render with + * @param width The width of the texture + * @param height The height of the texture + * @param textureUnit The texture unit to use, may be modified + * @param x The x coordinate of the rectangle to draw + * @param y The y coordinate of the rectangle to draw + * @param r The red component of the color + * @param g The green component of the color + * @param b The blue component of the color + * @param a The alpha component of the color + * @param mode The blending mode + * @param transforms True if the matrix passed to the shader should be multiplied + * by the model-view matrix + * @param applyFilters Whether or not to take color filters and + * shaders into account + */ + void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height, + GLuint& textureUnit, float x, float y, float r, float g, float b, float a, + SkXfermode::Mode mode, bool transforms, bool applyFilters); + + /** + * Same as above setupTextureAlpha8() but specifies the mesh's vertices + * and texCoords pointers. + */ + void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height, + GLuint& textureUnit, float x, float y, float r, float g, float b, float a, + SkXfermode::Mode mode, bool transforms, bool applyFilters, + GLvoid* vertices, GLvoid* texCoords); + + /** + * Draws text underline and strike-through if needed. + * + * @param text The text to decor + * @param bytesCount The number of bytes in the text + * @param length The length in pixels of the text, can be <= 0.0f to force a measurement + * @param x The x coordinate where the text will be drawn + * @param y The y coordinate where the text will be drawn + * @param paint The paint to draw the text with + */ + void drawTextDecorations(const char* text, int bytesCount, float length, + float x, float y, SkPaint* paint); + + /** + * Resets the texture coordinates stored in mMeshVertices. Setting the values + * back to default is achieved by calling: + * + * resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + * + * @param u1 The left coordinate of the texture + * @param v1 The bottom coordinate of the texture + * @param u2 The right coordinate of the texture + * @param v2 The top coordinate of the texture + */ + void resetDrawTextureTexCoords(float u1, float v1, float u2, float v2); + + /** + * Gets the alpha and xfermode out of a paint object. If the paint is null + * alpha will be 255 and the xfermode will be SRC_OVER. + * + * @param paint The paint to extract values from + * @param alpha Where to store the resulting alpha + * @param mode Where to store the resulting xfermode + */ + inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode); + + /** + * Binds the specified texture with the specified wrap modes. + */ + inline void bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit = 0); + + /** + * Enable or disable blending as necessary. This function sets the appropriate + * blend function based on the specified xfermode. + */ + inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, + bool swapSrcDst = false); + + /** + * Safely retrieves the mode from the specified xfermode. If the specified + * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. + */ + inline SkXfermode::Mode getXfermode(SkXfermode* mode); + + /** + * Use the specified program with the current GL context. If the program is already + * in use, it will not be bound again. If it is not in use, the current program is + * marked unused and the specified program becomes used and becomes the new + * current program. + * + * @param program The program to use + * + * @return true If the specified program was already in use, false otherwise. + */ + inline bool useProgram(Program* program); + + // Dimensions of the drawing surface + int mWidth, mHeight; + + // Matrix used for ortho projection in shaders + mat4 mOrthoMatrix; + + // Model-view matrix used to position/size objects + mat4 mModelView; + + // Number of saved states + int mSaveCount; + // Base state + sp<Snapshot> mFirstSnapshot; + // Current state + sp<Snapshot> mSnapshot; + + // Shaders + SkiaShader* mShader; + + // Color filters + SkiaColorFilter* mColorFilter; + + // Used to draw textured quads + TextureVertex mMeshVertices[4]; + + // GL extensions + Extensions mExtensions; + + // Drop shadow + bool mHasShadow; + float mShadowRadius; + float mShadowDx; + float mShadowDy; + int mShadowColor; + + // Various caches + Caches& mCaches; + + // List of rectangles to clear due to calls to saveLayer() + Vector<Rect*> mLayers; + + // Misc + GLint mMaxTextureSize; + + // Indentity matrix + const mat4 mIdentity; + + friend class DisplayListRenderer; + +}; // class OpenGLRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_OPENGL_RENDERER_H diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp new file mode 100644 index 0000000..560decf --- /dev/null +++ b/libs/hwui/Patch.cpp @@ -0,0 +1,163 @@ +/* + * 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 <cmath> + +#include <utils/Log.h> + +#include "Patch.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads) { + // 2 triangles per patch, 3 vertices per triangle + verticesCount = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3; + vertices = new TextureVertex[verticesCount]; +} + +Patch::~Patch() { + delete[] vertices; +} + +/////////////////////////////////////////////////////////////////////////////// +// Vertices management +/////////////////////////////////////////////////////////////////////////////// + +void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom, + const int32_t* xDivs, const int32_t* yDivs, + const uint32_t width, const uint32_t height, const uint32_t colorKey) { + const uint32_t xStretchCount = (width + 1) >> 1; + const uint32_t yStretchCount = (height + 1) >> 1; + + float stretchX = 0.0f; + float stretchY = 0.0; + + const float meshWidth = right - left; + + if (xStretchCount > 0) { + uint32_t stretchSize = 0; + for (uint32_t i = 1; i < width; i += 2) { + stretchSize += xDivs[i] - xDivs[i - 1]; + } + const float xStretchTex = stretchSize; + const float fixed = bitmapWidth - stretchSize; + const float xStretch = right - left - fixed; + stretchX = xStretch / xStretchTex; + } + + if (yStretchCount > 0) { + uint32_t stretchSize = 0; + for (uint32_t i = 1; i < height; i += 2) { + stretchSize += yDivs[i] - yDivs[i - 1]; + } + const float yStretchTex = stretchSize; + const float fixed = bitmapHeight - stretchSize; + const float yStretch = bottom - top - fixed; + stretchY = yStretch / yStretchTex; + } + + TextureVertex* vertex = vertices; + uint32_t quadCount = 0; + + float previousStepY = 0.0f; + + float y1 = 0.0f; + float v1 = 0.0f; + + for (uint32_t i = 0; i < height; i++) { + float stepY = yDivs[i]; + + float y2 = 0.0f; + if (i & 1) { + const float segment = stepY - previousStepY; + y2 = y1 + segment * stretchY; + } else { + y2 = y1 + stepY - previousStepY; + } + float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight; + + generateRow(vertex, y1, y2, v1, v2, xDivs, width, stretchX, + right - left, bitmapWidth, quadCount, colorKey); + + y1 = y2; + v1 = (stepY + 0.5f) / bitmapHeight; + + previousStepY = stepY; + } + + generateRow(vertex, y1, bottom - top, v1, 1.0f, xDivs, width, stretchX, + right - left, bitmapWidth, quadCount, colorKey); +} + +inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2, + const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth, + uint32_t& quadCount, const uint32_t colorKey) { + float previousStepX = 0.0f; + + float x1 = 0.0f; + float u1 = 0.0f; + + // Generate the row quad by quad + for (uint32_t i = 0; i < xCount; i++) { + float stepX = xDivs[i]; + + float x2 = 0.0f; + if (i & 1) { + const float segment = stepX - previousStepX; + x2 = x1 + segment * stretchX; + } else { + x2 = x1 + stepX - previousStepX; + } + float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth; + + generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount, colorKey); + + x1 = x2; + u1 = (stepX + 0.5f) / bitmapWidth; + + previousStepX = stepX; + } + + generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount, colorKey); +} + +inline void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, + float u1, float v1, float u2, float v2, uint32_t& quadCount, const uint32_t colorKey) { + if (((colorKey >> quadCount++) & 0x1) == 1) { + return; + } + + // Left triangle + TextureVertex::set(vertex++, x1, y1, u1, v1); + TextureVertex::set(vertex++, x2, y1, u2, v1); + TextureVertex::set(vertex++, x1, y2, u1, v2); + + // Right triangle + TextureVertex::set(vertex++, x1, y2, u1, v2); + TextureVertex::set(vertex++, x2, y1, u2, v1); + TextureVertex::set(vertex++, x2, y2, u2, v2); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h new file mode 100644 index 0000000..54c9d6c --- /dev/null +++ b/libs/hwui/Patch.h @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_PATCH_H +#define ANDROID_UI_PATCH_H + +#include <sys/types.h> + +#include "Vertex.h" +#include "utils/Compare.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// 9-patch structures +/////////////////////////////////////////////////////////////////////////////// + +/** + * Description of a patch. + */ +struct PatchDescription { + PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0), + xCount(0), yCount(0), emptyCount(0), colorKey(0) { } + PatchDescription(const float bitmapWidth, const float bitmapHeight, + const float pixelWidth, const float pixelHeight, + const uint32_t xCount, const uint32_t yCount, + const int8_t emptyCount, const uint32_t colorKey): + bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight), + pixelWidth(pixelWidth), pixelHeight(pixelHeight), + xCount(xCount), yCount(yCount), + emptyCount(emptyCount), colorKey(colorKey) { } + PatchDescription(const PatchDescription& description): + bitmapWidth(description.bitmapWidth), bitmapHeight(description.bitmapHeight), + pixelWidth(description.pixelWidth), pixelHeight(description.pixelHeight), + xCount(description.xCount), yCount(description.yCount), + emptyCount(description.emptyCount), colorKey(description.colorKey) { } + + float bitmapWidth; + float bitmapHeight; + float pixelWidth; + float pixelHeight; + uint32_t xCount; + uint32_t yCount; + int8_t emptyCount; + uint32_t colorKey; + + bool operator<(const PatchDescription& rhs) const { + compare(bitmapWidth) { + compare(bitmapHeight) { + compare(pixelWidth) { + compare(pixelHeight) { + compareI(xCount) { + compareI(yCount) { + compareI(emptyCount) { + compareI(colorKey) return false; + } + } + } + } + } + } + } + return false; + } +}; // struct PatchDescription + +/** + * An OpenGL patch. This contains an array of vertices and an array of + * indices to render the vertices. + */ +struct Patch { + Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads = 0); + ~Patch(); + + void updateVertices(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom, + const int32_t* xDivs, const int32_t* yDivs, + const uint32_t width, const uint32_t height, + const uint32_t colorKey = 0); + + TextureVertex* vertices; + uint32_t verticesCount; + +private: + static inline void generateRow(TextureVertex*& vertex, float y1, float y2, + float v1, float v2, const int32_t xDivs[], uint32_t xCount, + float stretchX, float width, float bitmapWidth, + uint32_t& quadCount, const uint32_t colorKey); + static inline void generateQuad(TextureVertex*& vertex, + float x1, float y1, float x2, float y2, + float u1, float v1, float u2, float v2, + uint32_t& quadCount, const uint32_t colorKey); +}; // struct Patch + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_PATCH_H diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp new file mode 100644 index 0000000..4762d21 --- /dev/null +++ b/libs/hwui/PatchCache.cpp @@ -0,0 +1,100 @@ +/* + * 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/Log.h> + +#include "PatchCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) { +} + +PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) { +} + +PatchCache::~PatchCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void PatchCache::clear() { + size_t count = mCache.size(); + for (size_t i = 0; i < count; i++) { + delete mCache.valueAt(i); + } + mCache.clear(); +} + +Patch* PatchCache::get(const float bitmapWidth, const float bitmapHeight, + const float pixelWidth, const float pixelHeight, + const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, + const uint32_t width, const uint32_t height, const int8_t numColors) { + + int8_t transparentQuads = 0; + uint32_t colorKey = 0; + + if (uint8_t(numColors) < sizeof(uint32_t) * 4) { + for (int8_t i = 0; i < numColors; i++) { + if (colors[i] == 0x0) { + transparentQuads++; + colorKey |= 0x1 << i; + } + } + } + + const PatchDescription description(bitmapWidth, bitmapHeight, + pixelWidth, pixelHeight, width, height, transparentQuads, colorKey); + + ssize_t index = mCache.indexOfKey(description); + Patch* mesh = NULL; + if (index >= 0) { + mesh = mCache.valueAt(index); + } + + if (!mesh) { + PATCH_LOGD("New patch mesh " + "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f", + width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight); + + mesh = new Patch(width, height, transparentQuads); + mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, + pixelWidth, pixelHeight, xDivs, yDivs, width, height, colorKey); + + if (mCache.size() >= mMaxEntries) { + delete mCache.valueAt(mCache.size() - 1); + mCache.removeItemsAt(mCache.size() - 1, 1); + } + + mCache.add(description, mesh); + } + + return mesh; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h new file mode 100644 index 0000000..f4eeb09 --- /dev/null +++ b/libs/hwui/PatchCache.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_PATCH_CACHE_H +#define ANDROID_UI_PATCH_CACHE_H + +#include <utils/KeyedVector.h> + +#include "Patch.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#define DEBUG_PATCHES 0 + +// Debug +#if DEBUG_PATCHES + #define PATCH_LOGD(...) LOGD(__VA_ARGS__) +#else + #define PATCH_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////////////////////////// + +class PatchCache { +public: + PatchCache(); + PatchCache(uint32_t maxCapacity); + ~PatchCache(); + + Patch* get(const float bitmapWidth, const float bitmapHeight, + const float pixelWidth, const float pixelHeight, + const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, + const uint32_t width, const uint32_t height, const int8_t numColors); + void clear(); + +private: + uint32_t mMaxEntries; + KeyedVector<PatchDescription, Patch*> mCache; +}; // class PatchCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_PATCH_CACHE_H diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp new file mode 100644 index 0000000..377727b --- /dev/null +++ b/libs/hwui/PathCache.cpp @@ -0,0 +1,224 @@ +/* + * 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 <GLES2/gl2.h> + +#include <SkCanvas.h> +#include <SkRect.h> + +#include <utils/threads.h> + +#include "PathCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +PathCache::PathCache(): + mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting path cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE); + } + init(); +} + +PathCache::PathCache(uint32_t maxByteSize): + mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + init(); +} + +PathCache::~PathCache() { + Mutex::Autolock _l(mLock); + mCache.clear(); +} + +void PathCache::init() { + mCache.setOnEntryRemovedListener(this); + + GLint maxTextureSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + mMaxTextureSize = maxTextureSize; +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t PathCache::getSize() { + Mutex::Autolock _l(mLock); + return mSize; +} + +uint32_t PathCache::getMaxSize() { + Mutex::Autolock _l(mLock); + return mMaxSize; +} + +void PathCache::setMaxSize(uint32_t maxSize) { + Mutex::Autolock _l(mLock); + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void PathCache::operator()(PathCacheEntry& path, PathTexture*& texture) { + const uint32_t size = texture->width * texture->height; + mSize -= size; + + if (texture) { + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void PathCache::remove(SkPath* path) { + Mutex::Autolock _l(mLock); + // TODO: Linear search... + for (uint32_t i = 0; i < mCache.size(); i++) { + if (mCache.getKeyAt(i).path == path) { + mCache.removeAt(i); + } + } +} + +PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { + PathCacheEntry entry(path, paint); + + mLock.lock(); + PathTexture* texture = mCache.get(entry); + mLock.unlock(); + + if (!texture) { + texture = addTexture(entry, path, paint); + } else if (path->getGenerationID() != texture->generation) { + mLock.lock(); + mCache.remove(entry); + mLock.unlock(); + texture = addTexture(entry, path, paint); + } + + return texture; +} + +PathTexture* PathCache::addTexture(const PathCacheEntry& entry, + const SkPath *path, const SkPaint* paint) { + const SkRect& bounds = path->getBounds(); + + const float pathWidth = fmax(bounds.width(), 1.0f); + const float pathHeight = fmax(bounds.height(), 1.0f); + + if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) { + LOGW("Path too large to be rendered into a texture"); + return NULL; + } + + const float offset = entry.strokeWidth * 1.5f; + const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5); + const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5); + + const uint32_t size = width * height; + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + mLock.lock(); + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + mLock.unlock(); + } + + PathTexture* texture = new PathTexture; + texture->left = bounds.fLeft; + texture->top = bounds.fTop; + texture->offset = offset; + texture->width = width; + texture->height = height; + texture->generation = path->getGenerationID(); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kA8_Config, width, height); + bitmap.allocPixels(); + bitmap.eraseColor(0); + + SkCanvas canvas(bitmap); + canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset); + canvas.drawPath(*path, *paint); + + generateTexture(bitmap, texture); + + if (size < mMaxSize) { + mLock.lock(); + mSize += size; + mCache.put(entry, texture); + mLock.unlock(); + } else { + texture->cleanup = true; + } + + return texture; +} + +void PathCache::clear() { + Mutex::Autolock _l(mLock); + mCache.clear(); +} + +void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { + SkAutoLockPixels alp(bitmap); + if (!bitmap.readyToDraw()) { + LOGE("Cannot generate texture from bitmap"); + return; + } + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + // Textures are Alpha8 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + texture->blend = true; + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h new file mode 100644 index 0000000..0c74261 --- /dev/null +++ b/libs/hwui/PathCache.h @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_PATH_CACHE_H +#define ANDROID_UI_PATH_CACHE_H + +#include <SkBitmap.h> +#include <SkPaint.h> +#include <SkPath.h> + +#include "Texture.h" +#include "utils/Compare.h" +#include "utils/GenerationCache.h" + +namespace android { +namespace uirenderer { + +/** + * Describe a path in the path cache. + */ +struct PathCacheEntry { + PathCacheEntry() { + path = NULL; + join = SkPaint::kDefault_Join; + cap = SkPaint::kDefault_Cap; + style = SkPaint::kFill_Style; + miter = 4.0f; + strokeWidth = 1.0f; + } + + PathCacheEntry(const PathCacheEntry& entry): + path(entry.path), join(entry.join), cap(entry.cap), + style(entry.style), miter(entry.miter), + strokeWidth(entry.strokeWidth) { + } + + PathCacheEntry(SkPath* path, SkPaint* paint) { + this->path = path; + join = paint->getStrokeJoin(); + cap = paint->getStrokeCap(); + miter = paint->getStrokeMiter(); + strokeWidth = paint->getStrokeWidth(); + style = paint->getStyle(); + } + + SkPath* path; + SkPaint::Join join; + SkPaint::Cap cap; + SkPaint::Style style; + float miter; + float strokeWidth; + + bool operator<(const PathCacheEntry& rhs) const { + compareI(path) { + compareI(join) { + compareI(cap) { + compareI(style) { + compare(miter) { + compare(strokeWidth) return false; + } + } + } + } + } + return false; + } +}; // struct PathCacheEntry + +/** + * Alpha texture used to represent a path. + */ +struct PathTexture: public Texture { + PathTexture(): Texture() { + } + + /** + * 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; +}; // struct PathTexture + +/** + * A simple LRU path 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<PathCacheEntry, PathTexture*> { +public: + PathCache(); + PathCache(uint32_t maxByteSize); + ~PathCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(PathCacheEntry& path, PathTexture*& texture); + + /** + * Returns the texture associated with the specified path. If the texture + * cannot be found in the cache, a new texture is generated. + */ + PathTexture* get(SkPath* path, SkPaint* paint); + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + /** + * Removes an entry. + */ + void remove(SkPath* path); + + /** + * 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(); + +private: + /** + * Generates the texture from a bitmap into the specified texture structure. + */ + void generateTexture(SkBitmap& bitmap, Texture* texture); + + PathTexture* addTexture(const PathCacheEntry& entry, const SkPath *path, const SkPaint* paint); + + void init(); + + GenerationCache<PathCacheEntry, PathTexture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + GLuint mMaxTextureSize; + + /** + * Used to access mCache and mSize. All methods are accessed from a single + * thread except for remove(). + */ + mutable Mutex mLock; +}; // class PathCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_PATH_CACHE_H diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp new file mode 100644 index 0000000..25674c6 --- /dev/null +++ b/libs/hwui/Program.cpp @@ -0,0 +1,144 @@ +/* + * 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 "Program.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Shaders +/////////////////////////////////////////////////////////////////////////////// + +#define SHADER_SOURCE(name, source) const char* name = #source + +/////////////////////////////////////////////////////////////////////////////// +// Base program +/////////////////////////////////////////////////////////////////////////////// + +Program::Program(const char* vertex, const char* fragment) { + vertexShader = buildShader(vertex, GL_VERTEX_SHADER); + fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); + + id = glCreateProgram(); + glAttachShader(id, vertexShader); + glAttachShader(id, fragmentShader); + glLinkProgram(id); + + GLint status; + glGetProgramiv(id, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + GLint infoLen = 0; + glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) { + GLchar log[infoLen]; + glGetProgramInfoLog(id, infoLen, 0, &log[0]); + LOGE("Error while linking shaders: %s", log); + } + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + glDeleteProgram(id); + } + + mUse = false; + + position = addAttrib("position"); + transform = addUniform("transform"); +} + +Program::~Program() { + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + glDeleteProgram(id); +} + +int Program::addAttrib(const char* name) { + int slot = glGetAttribLocation(id, name); + attributes.add(name, slot); + return slot; +} + +int Program::getAttrib(const char* name) { + ssize_t index = attributes.indexOfKey(name); + if (index >= 0) { + return attributes.valueAt(index); + } + return addAttrib(name); +} + +int Program::addUniform(const char* name) { + int slot = glGetUniformLocation(id, name); + uniforms.add(name, slot); + return slot; +} + +int Program::getUniform(const char* name) { + ssize_t index = uniforms.indexOfKey(name); + if (index >= 0) { + return uniforms.valueAt(index); + } + return addUniform(name); +} + +GLuint Program::buildShader(const char* source, GLenum type) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + // Some drivers return wrong values for GL_INFO_LOG_LENGTH + // use a fixed size instead + GLchar log[512]; + glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]); + LOGE("Error while compiling shader: %s", log); + glDeleteShader(shader); + } + + return shader; +} + +void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, + const mat4& transformMatrix) { + mat4 t(projectionMatrix); + t.multiply(transformMatrix); + t.multiply(modelViewMatrix); + + glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]); +} + +void Program::setColor(const float r, const float g, const float b, const float a) { + glUniform4f(getUniform("color"), r, g, b, a); +} + +void Program::use() { + glUseProgram(id); + mUse = true; + + glEnableVertexAttribArray(position); +} + +void Program::remove() { + mUse = false; + + glDisableVertexAttribArray(position); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h new file mode 100644 index 0000000..026097c --- /dev/null +++ b/libs/hwui/Program.h @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_PROGRAM_H +#define ANDROID_UI_PROGRAM_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/KeyedVector.h> + +#include "Matrix.h" + +namespace android { +namespace uirenderer { + +/** + * A program holds a vertex and a fragment shader. It offers several utility + * methods to query attributes and uniforms. + */ +class Program { +public: + /** + * Creates a new program with the specified vertex and fragment + * shaders sources. + */ + Program(const char* vertex, const char* fragment); + virtual ~Program(); + + /** + * Binds this program to the GL context. + */ + virtual void use(); + + /** + * Marks this program as unused. This will not unbind + * the program from the GL context. + */ + virtual void remove(); + + /** + * Returns the OpenGL name of the specified attribute. + */ + int getAttrib(const char* name); + + /** + * Returns the OpenGL name of the specified uniform. + */ + int getUniform(const char* name); + + /** + * Indicates whether this program is currently in use with + * the GL context. + */ + inline bool isInUse() const { + return mUse; + } + + /** + * Binds the program with the specified projection, modelView and + * transform matrices. + */ + void set(const mat4& projectionMatrix, const mat4& modelViewMatrix, + const mat4& transformMatrix); + + /** + * Sets the color associated with this shader. + */ + void setColor(const float r, const float g, const float b, const float a); + + /** + * Name of the position attribute. + */ + int position; + + /** + * Name of the transform uniform. + */ + int transform; + +protected: + /** + * Adds an attribute with the specified name. + * + * @return The OpenGL name of the attribute. + */ + int addAttrib(const char* name); + + /** + * Adds a uniform with the specified name. + * + * @return The OpenGL name of the uniform. + */ + int addUniform(const char* name); + +private: + /** + * Compiles the specified shader of the specified type. + * + * @return The name of the compiled shader. + */ + GLuint buildShader(const char* source, GLenum type); + + // Name of the OpenGL program + GLuint id; + + // Name of the shaders + GLuint vertexShader; + GLuint fragmentShader; + + // Keeps track of attributes and uniforms slots + KeyedVector<const char*, int> attributes; + KeyedVector<const char*, int> uniforms; + + bool mUse; +}; // class Program + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_PROGRAM_H diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp new file mode 100644 index 0000000..3cd85c8 --- /dev/null +++ b/libs/hwui/ProgramCache.cpp @@ -0,0 +1,615 @@ +/* + * 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/String8.h> + +#include "ProgramCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define MODULATE_OP_NO_MODULATE 0 +#define MODULATE_OP_MODULATE 1 +#define MODULATE_OP_MODULATE_A8 2 + +/////////////////////////////////////////////////////////////////////////////// +// Vertex shaders snippets +/////////////////////////////////////////////////////////////////////////////// + +const char* gVS_Header_Attributes = + "attribute vec4 position;\n"; +const char* gVS_Header_Attributes_TexCoords = + "attribute vec2 texCoords;\n"; +const char* gVS_Header_Uniforms = + "uniform mat4 transform;\n"; +const char* gVS_Header_Uniforms_HasGradient[3] = { + // Linear + "uniform mat4 screenSpace;\n", + // Circular + "uniform mat4 screenSpace;\n", + // Sweep + "uniform mat4 screenSpace;\n" +}; +const char* gVS_Header_Uniforms_HasBitmap = + "uniform mat4 textureTransform;\n" + "uniform vec2 textureDimension;\n"; +const char* gVS_Header_Varyings_HasTexture = + "varying vec2 outTexCoords;\n"; +const char* gVS_Header_Varyings_HasBitmap = + "varying vec2 outBitmapTexCoords;\n"; +const char* gVS_Header_Varyings_HasGradient[3] = { + // Linear + "varying vec2 linear;\n", + // Circular + "varying vec2 circular;\n", + // Sweep + "varying vec2 sweep;\n" +}; +const char* gVS_Main = + "\nvoid main(void) {\n"; +const char* gVS_Main_OutTexCoords = + " outTexCoords = texCoords;\n"; +const char* gVS_Main_OutGradient[3] = { + // Linear + " linear = vec2((screenSpace * position).x, 0.5);\n", + // Circular + " circular = (screenSpace * position).xy;\n", + // Sweep + " sweep = (screenSpace * position).xy;\n" +}; +const char* gVS_Main_OutBitmapTexCoords = + " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; +const char* gVS_Main_Position = + " gl_Position = transform * position;\n"; +const char* gVS_Footer = + "}\n\n"; + +/////////////////////////////////////////////////////////////////////////////// +// Fragment shaders snippets +/////////////////////////////////////////////////////////////////////////////// + +const char* gFS_Header_Extension_FramebufferFetch = + "#extension GL_NV_shader_framebuffer_fetch : enable\n\n"; +const char* gFS_Header = + "precision mediump float;\n\n"; +const char* gFS_Uniforms_Color = + "uniform vec4 color;\n"; +const char* gFS_Uniforms_TextureSampler = + "uniform sampler2D sampler;\n"; +const char* gFS_Uniforms_GradientSampler[3] = { + // Linear + "uniform sampler2D gradientSampler;\n", + // Circular + "uniform sampler2D gradientSampler;\n", + // Sweep + "uniform sampler2D gradientSampler;\n" +}; +const char* gFS_Uniforms_BitmapSampler = + "uniform sampler2D bitmapSampler;\n"; +const char* gFS_Uniforms_ColorOp[4] = { + // None + "", + // Matrix + "uniform mat4 colorMatrix;\n" + "uniform vec4 colorMatrixVector;\n", + // Lighting + "uniform vec4 lightingMul;\n" + "uniform vec4 lightingAdd;\n", + // PorterDuff + "uniform vec4 colorBlend;\n" +}; +const char* gFS_Main = + "\nvoid main(void) {\n" + " lowp vec4 fragColor;\n"; + +// Fast cases +const char* gFS_Fast_SingleColor = + "\nvoid main(void) {\n" + " gl_FragColor = color;\n" + "}\n\n"; +const char* gFS_Fast_SingleTexture = + "\nvoid main(void) {\n" + " gl_FragColor = texture2D(sampler, outTexCoords);\n" + "}\n\n"; +const char* gFS_Fast_SingleModulateTexture = + "\nvoid main(void) {\n" + " gl_FragColor = color.a * texture2D(sampler, outTexCoords);\n" + "}\n\n"; +const char* gFS_Fast_SingleA8Texture = + "\nvoid main(void) {\n" + " gl_FragColor = texture2D(sampler, outTexCoords);\n" + "}\n\n"; +const char* gFS_Fast_SingleModulateA8Texture = + "\nvoid main(void) {\n" + " gl_FragColor = color * texture2D(sampler, outTexCoords).a;\n" + "}\n\n"; +const char* gFS_Fast_SingleGradient = + "\nvoid main(void) {\n" + " gl_FragColor = texture2D(gradientSampler, linear);\n" + "}\n\n"; +const char* gFS_Fast_SingleModulateGradient = + "\nvoid main(void) {\n" + " gl_FragColor = color.a * texture2D(gradientSampler, linear);\n" + "}\n\n"; + +// General case +const char* gFS_Main_FetchColor = + " fragColor = color;\n"; +const char* gFS_Main_FetchTexture[2] = { + // Don't modulate + " fragColor = texture2D(sampler, outTexCoords);\n", + // Modulate + " fragColor = color * texture2D(sampler, outTexCoords);\n" +}; +const char* gFS_Main_FetchA8Texture[2] = { + // Don't modulate + " fragColor = texture2D(sampler, outTexCoords);\n", + // Modulate + " fragColor = color * texture2D(sampler, outTexCoords).a;\n" +}; +const char* gFS_Main_FetchGradient[3] = { + // Linear + " vec4 gradientColor = texture2D(gradientSampler, linear);\n", + // Circular + " float index = length(circular);\n" + " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n", + // Sweep + " float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" + " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n" +}; +const char* gFS_Main_FetchBitmap = + " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; +const char* gFS_Main_FetchBitmapNpot = + " vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n"; +const char* gFS_Main_BlendShadersBG = + " fragColor = blendShaders(gradientColor, bitmapColor)"; +const char* gFS_Main_BlendShadersGB = + " fragColor = blendShaders(bitmapColor, gradientColor)"; +const char* gFS_Main_BlendShaders_Modulate[3] = { + // Don't modulate + ";\n", + // Modulate + " * fragColor.a;\n", + // Modulate with alpha 8 texture + " * texture2D(sampler, outTexCoords).a;\n" +}; +const char* gFS_Main_GradientShader_Modulate[3] = { + // Don't modulate + " fragColor = gradientColor;\n", + // Modulate + " fragColor = gradientColor * fragColor.a;\n", + // Modulate with alpha 8 texture + " fragColor = gradientColor * texture2D(sampler, outTexCoords).a;\n" + }; +const char* gFS_Main_BitmapShader_Modulate[3] = { + // Don't modulate + " fragColor = bitmapColor;\n", + // Modulate + " fragColor = bitmapColor * fragColor.a;\n", + // Modulate with alpha 8 texture + " fragColor = bitmapColor * texture2D(sampler, outTexCoords).a;\n" + }; +const char* gFS_Main_FragColor = + " gl_FragColor = fragColor;\n"; +const char* gFS_Main_FragColor_Blend = + " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n"; +const char* gFS_Main_FragColor_Blend_Swap = + " gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n"; +const char* gFS_Main_ApplyColorOp[4] = { + // None + "", + // Matrix + // TODO: Fix premultiplied alpha computations for color matrix + " fragColor *= colorMatrix;\n" + " fragColor += colorMatrixVector;\n" + " fragColor.rgb *= fragColor.a;\n", + // Lighting + " float lightingAlpha = fragColor.a;\n" + " fragColor = min(fragColor * lightingMul + (lightingAdd * lightingAlpha), lightingAlpha);\n" + " fragColor.a = lightingAlpha;\n", + // PorterDuff + " fragColor = blendColors(colorBlend, fragColor);\n" +}; +const char* gFS_Footer = + "}\n\n"; + +/////////////////////////////////////////////////////////////////////////////// +// PorterDuff snippets +/////////////////////////////////////////////////////////////////////////////// + +const char* gBlendOps[18] = { + // Clear + "return vec4(0.0, 0.0, 0.0, 0.0);\n", + // Src + "return src;\n", + // Dst + "return dst;\n", + // SrcOver + "return src + dst * (1.0 - src.a);\n", + // DstOver + "return dst + src * (1.0 - dst.a);\n", + // SrcIn + "return src * dst.a;\n", + // DstIn + "return dst * src.a;\n", + // SrcOut + "return src * (1.0 - dst.a);\n", + // DstOut + "return dst * (1.0 - src.a);\n", + // SrcAtop + "return vec4(src.rgb * dst.a + (1.0 - src.a) * dst.rgb, dst.a);\n", + // DstAtop + "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n", + // Xor + "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, " + "src.a + dst.a - 2.0 * src.a * dst.a);\n", + // Add + "return min(src + dst, 1.0);\n", + // Multiply + "return src * dst;\n", + // Screen + "return src + dst - src * dst;\n", + // Overlay + "return clamp(vec4(mix(" + "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), " + "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), " + "step(dst.a, 2.0 * dst.rgb)), " + "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n", + // Darken + "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + " + "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", + // Lighten + "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + " + "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", +}; + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructors +/////////////////////////////////////////////////////////////////////////////// + +ProgramCache::ProgramCache() { +} + +ProgramCache::~ProgramCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Cache management +/////////////////////////////////////////////////////////////////////////////// + +void ProgramCache::clear() { + size_t count = mCache.size(); + for (size_t i = 0; i < count; i++) { + delete mCache.valueAt(i); + } + mCache.clear(); +} + +Program* ProgramCache::get(const ProgramDescription& description) { + programid key = description.key(); + ssize_t index = mCache.indexOfKey(key); + Program* program = NULL; + if (index < 0) { + description.log("Could not find program"); + program = generateProgram(description, key); + mCache.add(key, program); + } else { + program = mCache.valueAt(index); + } + return program; +} + +/////////////////////////////////////////////////////////////////////////////// +// Program generation +/////////////////////////////////////////////////////////////////////////////// + +Program* ProgramCache::generateProgram(const ProgramDescription& description, programid key) { + String8 vertexShader = generateVertexShader(description); + String8 fragmentShader = generateFragmentShader(description); + + Program* program = new Program(vertexShader.string(), fragmentShader.string()); + return program; +} + +String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { + // Add attributes + String8 shader(gVS_Header_Attributes); + if (description.hasTexture) { + shader.append(gVS_Header_Attributes_TexCoords); + } + // Uniforms + shader.append(gVS_Header_Uniforms); + if (description.hasGradient) { + shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Header_Uniforms_HasBitmap); + } + // Varyings + if (description.hasTexture) { + shader.append(gVS_Header_Varyings_HasTexture); + } + if (description.hasGradient) { + shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Header_Varyings_HasBitmap); + } + + // Begin the shader + shader.append(gVS_Main); { + if (description.hasTexture) { + shader.append(gVS_Main_OutTexCoords); + } + if (description.hasGradient) { + shader.append(gVS_Main_OutGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Main_OutBitmapTexCoords); + } + // Output transformed position + shader.append(gVS_Main_Position); + } + // End the shader + shader.append(gVS_Footer); + + PROGRAM_LOGD("*** Generated vertex shader:\n\n%s", shader.string()); + + return shader; +} + +String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) { + // Set the default precision + String8 shader; + + const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode; + if (blendFramebuffer) { + shader.append(gFS_Header_Extension_FramebufferFetch); + } + + shader.append(gFS_Header); + + // Varyings + if (description.hasTexture) { + shader.append(gVS_Header_Varyings_HasTexture); + } + if (description.hasGradient) { + shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Header_Varyings_HasBitmap); + } + + // Uniforms + int modulateOp = MODULATE_OP_NO_MODULATE; + const bool singleColor = !description.hasTexture && + !description.hasGradient && !description.hasBitmap; + + if (description.modulate || singleColor) { + shader.append(gFS_Uniforms_Color); + if (!singleColor) modulateOp = MODULATE_OP_MODULATE; + } + if (description.hasTexture) { + shader.append(gFS_Uniforms_TextureSampler); + } + if (description.hasGradient) { + shader.append(gFS_Uniforms_GradientSampler[description.gradientType]); + } + + // Optimization for common cases + if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone) { + bool fast = false; + + const bool noShader = !description.hasGradient && !description.hasBitmap; + const bool singleTexture = description.hasTexture && + !description.hasAlpha8Texture && noShader; + const bool singleA8Texture = description.hasTexture && + description.hasAlpha8Texture && noShader; + const bool singleGradient = !description.hasTexture && + description.hasGradient && !description.hasBitmap && + description.gradientType == ProgramDescription::kGradientLinear; + + if (singleColor) { + shader.append(gFS_Fast_SingleColor); + fast = true; + } else if (singleTexture) { + if (!description.modulate) { + shader.append(gFS_Fast_SingleTexture); + } else { + shader.append(gFS_Fast_SingleModulateTexture); + } + fast = true; + } else if (singleA8Texture) { + if (!description.modulate) { + shader.append(gFS_Fast_SingleA8Texture); + } else { + shader.append(gFS_Fast_SingleModulateA8Texture); + } + fast = true; + } else if (singleGradient) { + if (!description.modulate) { + shader.append(gFS_Fast_SingleGradient); + } else { + shader.append(gFS_Fast_SingleModulateGradient); + } + fast = true; + } + + if (fast) { + if (DEBUG_PROGRAM_CACHE) { + PROGRAM_LOGD("*** Fast case:\n"); + PROGRAM_LOGD("*** Generated fragment shader:\n\n"); + printLongString(shader); + } + + return shader; + } + } + + if (description.hasBitmap) { + shader.append(gFS_Uniforms_BitmapSampler); + } + shader.append(gFS_Uniforms_ColorOp[description.colorOp]); + + // Generate required functions + if (description.hasGradient && description.hasBitmap) { + generateBlend(shader, "blendShaders", description.shadersMode); + } + if (description.colorOp == ProgramDescription::kColorBlend) { + generateBlend(shader, "blendColors", description.colorMode); + } + if (blendFramebuffer) { + generateBlend(shader, "blendFramebuffer", description.framebufferMode); + } + if (description.isBitmapNpot) { + generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT); + } + + // Begin the shader + shader.append(gFS_Main); { + // Stores the result in fragColor directly + if (description.hasTexture) { + if (description.hasAlpha8Texture) { + if (!description.hasGradient && !description.hasBitmap) { + shader.append(gFS_Main_FetchA8Texture[modulateOp]); + } + } else { + shader.append(gFS_Main_FetchTexture[modulateOp]); + } + } else { + if ((!description.hasGradient && !description.hasBitmap) || description.modulate) { + shader.append(gFS_Main_FetchColor); + } + } + if (description.hasGradient) { + shader.append(gFS_Main_FetchGradient[description.gradientType]); + } + if (description.hasBitmap) { + if (!description.isBitmapNpot) { + shader.append(gFS_Main_FetchBitmap); + } else { + shader.append(gFS_Main_FetchBitmapNpot); + } + } + // Case when we have two shaders set + if (description.hasGradient && description.hasBitmap) { + int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + if (description.isBitmapFirst) { + shader.append(gFS_Main_BlendShadersBG); + } else { + shader.append(gFS_Main_BlendShadersGB); + } + shader.append(gFS_Main_BlendShaders_Modulate[op]); + } else { + if (description.hasGradient) { + int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + shader.append(gFS_Main_GradientShader_Modulate[op]); + } else if (description.hasBitmap) { + int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + shader.append(gFS_Main_BitmapShader_Modulate[op]); + } + } + // Apply the color op if needed + shader.append(gFS_Main_ApplyColorOp[description.colorOp]); + // Output the fragment + if (!blendFramebuffer) { + shader.append(gFS_Main_FragColor); + } else { + shader.append(!description.swapSrcDst ? + gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap); + } + } + // End the shader + shader.append(gFS_Footer); + + if (DEBUG_PROGRAM_CACHE) { + PROGRAM_LOGD("*** Generated fragment shader:\n\n"); + printLongString(shader); + } + + return shader; +} + +void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) { + shader.append("\nvec4 "); + shader.append(name); + shader.append("(vec4 src, vec4 dst) {\n"); + shader.append(" "); + shader.append(gBlendOps[mode]); + shader.append("}\n"); +} + +void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT) { + shader.append("\nvec2 wrap(vec2 texCoords) {\n"); + if (wrapS == GL_MIRRORED_REPEAT) { + shader.append(" float xMod2 = mod(texCoords.x, 2.0);\n"); + shader.append(" if (xMod2 > 1.0) xMod2 = 2.0 - xMod2;\n"); + } + if (wrapT == GL_MIRRORED_REPEAT) { + shader.append(" float yMod2 = mod(texCoords.y, 2.0);\n"); + shader.append(" if (yMod2 > 1.0) yMod2 = 2.0 - yMod2;\n"); + } + shader.append(" return vec2("); + switch (wrapS) { + case GL_CLAMP_TO_EDGE: + shader.append("texCoords.x"); + break; + case GL_REPEAT: + shader.append("mod(texCoords.x, 1.0)"); + break; + case GL_MIRRORED_REPEAT: + shader.append("xMod2"); + break; + } + shader.append(", "); + switch (wrapT) { + case GL_CLAMP_TO_EDGE: + shader.append("texCoords.y"); + break; + case GL_REPEAT: + shader.append("mod(texCoords.y, 1.0)"); + break; + case GL_MIRRORED_REPEAT: + shader.append("yMod2"); + break; + } + shader.append(");\n"); + shader.append("}\n"); +} + +void ProgramCache::printLongString(const String8& shader) const { + ssize_t index = 0; + ssize_t lastIndex = 0; + const char* str = shader.string(); + while ((index = shader.find("\n", index)) > -1) { + String8 line(str, index - lastIndex); + if (line.length() == 0) line.append("\n"); + PROGRAM_LOGD("%s", line.string()); + index++; + str += (index - lastIndex); + lastIndex = index; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h new file mode 100644 index 0000000..9cb13b3 --- /dev/null +++ b/libs/hwui/ProgramCache.h @@ -0,0 +1,261 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_PROGRAM_CACHE_H +#define ANDROID_UI_PROGRAM_CACHE_H + +#include <utils/KeyedVector.h> +#include <utils/Log.h> +#include <utils/String8.h> + +#include <GLES2/gl2.h> + +#include <SkXfermode.h> + +#include "Program.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#define DEBUG_PROGRAM_CACHE 0 + +// Debug +#if DEBUG_PROGRAM_CACHE + #define PROGRAM_LOGD(...) LOGD(__VA_ARGS__) +#else + #define PROGRAM_LOGD(...) +#endif + +// TODO: This should be set in properties +#define PANEL_BIT_DEPTH 20 +#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH)) +#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH) + +#define PROGRAM_KEY_TEXTURE 0x1 +#define PROGRAM_KEY_A8_TEXTURE 0x2 +#define PROGRAM_KEY_BITMAP 0x4 +#define PROGRAM_KEY_GRADIENT 0x8 +#define PROGRAM_KEY_BITMAP_FIRST 0x10 +#define PROGRAM_KEY_COLOR_MATRIX 0x20 +#define PROGRAM_KEY_COLOR_LIGHTING 0x40 +#define PROGRAM_KEY_COLOR_BLEND 0x80 +#define PROGRAM_KEY_BITMAP_NPOT 0x100 +#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 + +#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 +#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 + +// Encode the xfermodes on 6 bits +#define PROGRAM_MAX_XFERMODE 0x1f +#define PROGRAM_XFERMODE_SHADER_SHIFT 26 +#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20 +#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14 + +#define PROGRAM_BITMAP_WRAPS_SHIFT 9 +#define PROGRAM_BITMAP_WRAPT_SHIFT 11 + +#define PROGRAM_GRADIENT_TYPE_SHIFT 33 +#define PROGRAM_MODULATE 35 + +/////////////////////////////////////////////////////////////////////////////// +// Types +/////////////////////////////////////////////////////////////////////////////// + +typedef uint64_t programid; + +/////////////////////////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////////////////////////// + +/** + * Describe the features required for a given program. The features + * determine the generation of both the vertex and fragment shaders. + * A ProgramDescription must be used in conjunction with a ProgramCache. + */ +struct ProgramDescription { + enum ColorModifier { + kColorNone, + kColorMatrix, + kColorLighting, + kColorBlend + }; + + enum Gradient { + kGradientLinear, + kGradientCircular, + kGradientSweep + }; + + ProgramDescription(): + hasTexture(false), hasAlpha8Texture(false), modulate(false), + hasBitmap(false), isBitmapNpot(false), hasGradient(false), + gradientType(kGradientLinear), + shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false), + bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE), + colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode), + framebufferMode(SkXfermode::kClear_Mode), swapSrcDst(false) { + } + + // Texturing + bool hasTexture; + bool hasAlpha8Texture; + + // Modulate, this should only be set when setColor() return true + bool modulate; + + // Shaders + bool hasBitmap; + bool isBitmapNpot; + + bool hasGradient; + Gradient gradientType; + + SkXfermode::Mode shadersMode; + + bool isBitmapFirst; + GLenum bitmapWrapS; + GLenum bitmapWrapT; + + // Color operations + ColorModifier colorOp; + SkXfermode::Mode colorMode; + + // Framebuffer blending (requires Extensions.hasFramebufferFetch()) + // Ignored for all values < SkXfermode::kPlus_Mode + SkXfermode::Mode framebufferMode; + bool swapSrcDst; + + /** + * Indicates, for a given color, whether color modulation is required in + * the fragment shader. When this method returns true, the program should + * be provided with a modulation color. + */ + bool setColor(const float r, const float g, const float b, const float a) { + modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD || + g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD; + return modulate; + } + + /** + * Indicates, for a given color, whether color modulation is required in + * the fragment shader. When this method returns true, the program should + * be provided with a modulation color. + */ + bool setAlpha8Color(const float r, const float g, const float b, const float a) { + modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD || + g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD; + return modulate; + } + + /** + * Computes the unique key identifying this program. + */ + programid key() const { + programid key = 0; + if (hasTexture) key |= PROGRAM_KEY_TEXTURE; + if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE; + if (hasBitmap) { + key |= PROGRAM_KEY_BITMAP; + if (isBitmapNpot) { + key |= PROGRAM_KEY_BITMAP_NPOT; + key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT; + key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT; + } + } + if (hasGradient) key |= PROGRAM_KEY_GRADIENT; + key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT; + if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST; + if (hasBitmap && hasGradient) { + key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT; + } + switch (colorOp) { + case kColorMatrix: + key |= PROGRAM_KEY_COLOR_MATRIX; + break; + case kColorLighting: + key |= PROGRAM_KEY_COLOR_LIGHTING; + break; + case kColorBlend: + key |= PROGRAM_KEY_COLOR_BLEND; + key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT; + break; + case kColorNone: + break; + } + key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; + if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; + if (modulate) key |= programid(0x1) << PROGRAM_MODULATE; + return key; + } + + /** + * Logs the specified message followed by the key identifying this program. + */ + void log(const char* message) const { + programid k = key(); + PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), + uint32_t(k & 0xffffffff)); + } + +private: + inline uint32_t getEnumForWrap(GLenum wrap) const { + switch (wrap) { + case GL_CLAMP_TO_EDGE: + return 0; + case GL_REPEAT: + return 1; + case GL_MIRRORED_REPEAT: + return 2; + } + return 0; + } + +}; // struct ProgramDescription + +/** + * Generates and caches program. Programs are generated based on + * ProgramDescriptions. + */ +class ProgramCache { +public: + ProgramCache(); + ~ProgramCache(); + + Program* get(const ProgramDescription& description); + + void clear(); + +private: + Program* generateProgram(const ProgramDescription& description, programid key); + String8 generateVertexShader(const ProgramDescription& description); + String8 generateFragmentShader(const ProgramDescription& description); + void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode); + void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT); + + void printLongString(const String8& shader) const; + + KeyedVector<programid, Program*> mCache; +}; // class ProgramCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_PROGRAM_CACHE_H diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h new file mode 100644 index 0000000..db3cb4d --- /dev/null +++ b/libs/hwui/Properties.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_PROPERTIES_H +#define ANDROID_UI_PROPERTIES_H + +#include <cutils/properties.h> + +/** + * This file contains the list of system properties used to configure + * the OpenGLRenderer. + */ + +// These properties are defined in mega-bytes +#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size" +#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size" +#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size" +#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size" +#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size" +#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size" + +// These properties are defined in pixels +#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width" +#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height" + +// Gamma (>= 1.0, <= 10.0) +#define PROPERTY_TEXT_GAMMA "ro.text_gamma" +#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold" +#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold" + +// Converts a number of mega-bytes into bytes +#define MB(s) s * 1024 * 1024 + +#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f +#define DEFAULT_LAYER_CACHE_SIZE 6.0f +#define DEFAULT_PATH_CACHE_SIZE 4.0f +#define DEFAULT_PATCH_CACHE_SIZE 512 +#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f +#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f +#define DEFAULT_FBO_CACHE_SIZE 25 + +#define DEFAULT_TEXT_GAMMA 1.4f +#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64 +#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192 + +#endif // ANDROID_UI_PROPERTIES_H diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h new file mode 100644 index 0000000..c571ea1 --- /dev/null +++ b/libs/hwui/Rect.h @@ -0,0 +1,167 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_RECT_H +#define ANDROID_UI_RECT_H + +#include <utils/Log.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Structs +/////////////////////////////////////////////////////////////////////////////// + +struct Rect { + float left; + float top; + float right; + float bottom; + + Rect(): + left(0), + top(0), + right(0), + bottom(0) { + } + + Rect(float left, float top, float right, float bottom): + left(left), + top(top), + right(right), + bottom(bottom) { + } + + Rect(const Rect& r) { + set(r); + } + + Rect(Rect& r) { + set(r); + } + + Rect& operator=(const Rect& r) { + set(r); + return *this; + } + + Rect& operator=(Rect& r) { + set(r); + return *this; + } + + friend int operator==(const Rect& a, const Rect& b) { + return !memcmp(&a, &b, sizeof(a)); + } + + friend int operator!=(const Rect& a, const Rect& b) { + return memcmp(&a, &b, sizeof(a)); + } + + bool isEmpty() const { + return left >= right || top >= bottom; + } + + void setEmpty() { + memset(this, 0, sizeof(*this)); + } + + void set(float left, float top, float right, float bottom) { + this->left = left; + this->right = right; + this->top = top; + this->bottom = bottom; + } + + void set(const Rect& r) { + set(r.left, r.top, r.right, r.bottom); + } + + inline float getWidth() const { + return right - left; + } + + inline float getHeight() const { + return bottom - top; + } + + bool intersects(float left, float top, float right, float bottom) const { + return left < right && top < bottom && + this->left < this->right && this->top < this->bottom && + this->left < right && left < this->right && + this->top < bottom && top < this->bottom; + } + + bool intersects(const Rect& r) const { + return intersects(r.left, r.top, r.right, r.bottom); + } + + bool intersect(float left, float top, float right, float bottom) { + if (left < right && top < bottom && !this->isEmpty() && + this->left < right && left < this->right && + this->top < bottom && top < this->bottom) { + + if (this->left < left) this->left = left; + if (this->top < top) this->top = top; + if (this->right > right) this->right = right; + if (this->bottom > bottom) this->bottom = bottom; + + return true; + } + return false; + } + + bool intersect(const Rect& r) { + return intersect(r.left, r.top, r.right, r.bottom); + } + + bool unionWith(const Rect& r) { + if (r.left < r.right && r.top < r.bottom) { + if (left < right && top < bottom) { + if (left > r.left) left = r.left; + if (top > r.top) top = r.top; + if (right < r.right) right = r.right; + if (bottom < r.bottom) bottom = r.bottom; + return true; + } else { + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; + return true; + } + } + return false; + } + + void snapToPixelBoundaries() { + left = floorf(left); + top = floorf(top); + right = ceilf(right); + bottom = ceilf(bottom); + } + + void dump() const { + LOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom); + } + +}; // struct Rect + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_RECT_H diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp new file mode 100644 index 0000000..fe57ae7 --- /dev/null +++ b/libs/hwui/SkiaColorFilter.cpp @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#include "SkiaColorFilter.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Base color filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaColorFilter::SkiaColorFilter(Type type, bool blend): mType(type), mBlend(blend) { +} + +SkiaColorFilter::~SkiaColorFilter() { +} + +/////////////////////////////////////////////////////////////////////////////// +// Color matrix filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaColorMatrixFilter::SkiaColorMatrixFilter(float* matrix, float* vector): + SkiaColorFilter(kColorMatrix, true), mMatrix(matrix), mVector(vector) { +} + +SkiaColorMatrixFilter::~SkiaColorMatrixFilter() { + delete[] mMatrix; + delete[] mVector; +} + +void SkiaColorMatrixFilter::describe(ProgramDescription& description, + const Extensions& extensions) { + description.colorOp = ProgramDescription::kColorMatrix; +} + +void SkiaColorMatrixFilter::setupProgram(Program* program) { + glUniformMatrix4fv(program->getUniform("colorMatrix"), 1, GL_FALSE, &mMatrix[0]); + glUniform4fv(program->getUniform("colorMatrixVector"), 1, mVector); +} + +/////////////////////////////////////////////////////////////////////////////// +// Lighting color filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaLightingFilter::SkiaLightingFilter(int multiply, int add): + SkiaColorFilter(kLighting, true) { + mMulR = ((multiply >> 16) & 0xFF) / 255.0f; + mMulG = ((multiply >> 8) & 0xFF) / 255.0f; + mMulB = ((multiply ) & 0xFF) / 255.0f; + + mAddR = ((add >> 16) & 0xFF) / 255.0f; + mAddG = ((add >> 8) & 0xFF) / 255.0f; + mAddB = ((add ) & 0xFF) / 255.0f; +} + +void SkiaLightingFilter::describe(ProgramDescription& description, const Extensions& extensions) { + description.colorOp = ProgramDescription::kColorLighting; +} + +void SkiaLightingFilter::setupProgram(Program* program) { + glUniform4f(program->getUniform("lightingMul"), mMulR, mMulG, mMulB, 1.0f); + glUniform4f(program->getUniform("lightingAdd"), mAddR, mAddG, mAddB, 0.0f); +} + +/////////////////////////////////////////////////////////////////////////////// +// Blend color filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaBlendFilter::SkiaBlendFilter(int color, SkXfermode::Mode mode): + SkiaColorFilter(kBlend, true), mMode(mode) { + const int alpha = (color >> 24) & 0xFF; + mA = alpha / 255.0f; + mR = mA * ((color >> 16) & 0xFF) / 255.0f; + mG = mA * ((color >> 8) & 0xFF) / 255.0f; + mB = mA * ((color ) & 0xFF) / 255.0f; +} + +void SkiaBlendFilter::describe(ProgramDescription& description, const Extensions& extensions) { + description.colorOp = ProgramDescription::kColorBlend; + description.colorMode = mMode; +} + +void SkiaBlendFilter::setupProgram(Program* program) { + glUniform4f(program->getUniform("colorBlend"), mR, mG, mB, mA); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h new file mode 100644 index 0000000..865b6f0 --- /dev/null +++ b/libs/hwui/SkiaColorFilter.h @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_SKIA_COLOR_FILTER_H +#define ANDROID_UI_SKIA_COLOR_FILTER_H + +#include <GLES2/gl2.h> + +#include "ProgramCache.h" +#include "Extensions.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Base color filter +/////////////////////////////////////////////////////////////////////////////// + +/** + * Represents a Skia color filter. A color filter modifies a ProgramDescription + * and sets uniforms on the resulting shaders. + */ +struct SkiaColorFilter { + /** + * Type of Skia color filter in use. + */ + enum Type { + kNone, + kColorMatrix, + kLighting, + kBlend, + }; + + SkiaColorFilter(Type type, bool blend); + virtual ~SkiaColorFilter(); + + virtual void describe(ProgramDescription& description, const Extensions& extensions) = 0; + virtual void setupProgram(Program* program) = 0; + + inline bool blend() const { + return mBlend; + } + + Type type() const { + return mType; + } + +protected: + Type mType; + bool mBlend; +}; // struct SkiaColorFilter + +/////////////////////////////////////////////////////////////////////////////// +// Implementations +/////////////////////////////////////////////////////////////////////////////// + +/** + * A color filter that multiplies the source color with a matrix and adds a vector. + */ +struct SkiaColorMatrixFilter: public SkiaColorFilter { + SkiaColorMatrixFilter(float* matrix, float* vector); + ~SkiaColorMatrixFilter(); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program); + +private: + float* mMatrix; + float* mVector; +}; // struct SkiaColorMatrixFilter + +/** + * A color filters that multiplies the source color with a fixed value and adds + * another fixed value. Ignores the alpha channel of both arguments. + */ +struct SkiaLightingFilter: public SkiaColorFilter { + SkiaLightingFilter(int multiply, int add); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program); + +private: + GLfloat mMulR, mMulG, mMulB; + GLfloat mAddR, mAddG, mAddB; +}; // struct SkiaLightingFilter + +/** + * A color filters that blends the source color with a specified destination color + * and PorterDuff blending mode. + */ +struct SkiaBlendFilter: public SkiaColorFilter { + SkiaBlendFilter(int color, SkXfermode::Mode mode); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program); + +private: + SkXfermode::Mode mMode; + GLfloat mR, mG, mB, mA; +}; // struct SkiaBlendFilter + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_SKIA_COLOR_FILTER_H diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp new file mode 100644 index 0000000..fa85d20 --- /dev/null +++ b/libs/hwui/SkiaShader.cpp @@ -0,0 +1,338 @@ +/* + * 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/Log.h> + +#include <SkMatrix.h> + +#include "SkiaShader.h" +#include "Texture.h" +#include "Matrix.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Support +/////////////////////////////////////////////////////////////////////////////// + +static const GLenum gTextureUnitsMap[] = { + GL_TEXTURE0, + GL_TEXTURE1, + GL_TEXTURE2 +}; + +static const GLint gTileModes[] = { + GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode + GL_REPEAT, // == SkShader::kRepeat_Mode + GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode +}; + +/////////////////////////////////////////////////////////////////////////////// +// Base shader +/////////////////////////////////////////////////////////////////////////////// + +SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool blend): + mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) { + setMatrix(matrix); +} + +SkiaShader::~SkiaShader() { +} + +void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) { +} + +void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit) { +} + +void SkiaShader::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) { + glActiveTexture(gTextureUnitsMap[textureUnit]); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); +} + +void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) { + screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix); + screenSpace.multiply(modelView); +} + +/////////////////////////////////////////////////////////////////////////////// +// Bitmap shader +/////////////////////////////////////////////////////////////////////////////// + +SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool blend): + SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) { + updateLocalMatrix(matrix); +} + +void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) { + const Texture* texture = mTextureCache->get(mBitmap); + if (!texture) return; + mTexture = texture; + + const float width = texture->width; + const float height = texture->height; + + description.hasBitmap = true; + // The driver does not support non-power of two mirrored/repeated + // textures, so do it ourselves + if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) && + (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) { + description.isBitmapNpot = true; + description.bitmapWrapS = gTileModes[mTileX]; + description.bitmapWrapT = gTileModes[mTileY]; + mWrapS = GL_CLAMP_TO_EDGE; + mWrapT = GL_CLAMP_TO_EDGE; + } else { + mWrapS = gTileModes[mTileX]; + mWrapT = gTileModes[mTileY]; + } +} + +void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + glActiveTexture(gTextureUnitsMap[textureSlot]); + + const Texture* texture = mTexture; + mTexture = NULL; + if (!texture) return; + const AutoTexture autoCleanup(texture); + + const float width = texture->width; + const float height = texture->height; + + mat4 textureTransform; + computeScreenSpaceMatrix(textureTransform, modelView); + + // Uniforms + bindTexture(texture->id, mWrapS, mWrapT, textureSlot); + glUniform1i(program->getUniform("bitmapSampler"), textureSlot); + glUniformMatrix4fv(program->getUniform("textureTransform"), 1, + GL_FALSE, &textureTransform.data[0]); + glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height); +} + +void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + mat4 textureTransform; + computeScreenSpaceMatrix(textureTransform, modelView); + glUniformMatrix4fv(program->getUniform("textureTransform"), 1, + GL_FALSE, &textureTransform.data[0]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Linear gradient shader +/////////////////////////////////////////////////////////////////////////////// + +static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { + SkVector vec = pts[1] - pts[0]; + const float mag = vec.length(); + const float inv = mag ? 1.0f / mag : 0; + + vec.scale(inv); + matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); + matrix->postTranslate(-pts[0].fX, -pts[0].fY); + matrix->postScale(inv, inv); +} + +SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors, + float* positions, int count, SkShader* key, SkShader::TileMode tileMode, + SkMatrix* matrix, bool blend): + SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend), + mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) { + SkPoint points[2]; + points[0].set(bounds[0], bounds[1]); + points[1].set(bounds[2], bounds[3]); + + SkMatrix unitMatrix; + toUnitMatrix(points, &unitMatrix); + mUnitMatrix.load(unitMatrix); + + updateLocalMatrix(matrix); +} + +SkiaLinearGradientShader::~SkiaLinearGradientShader() { + delete[] mBounds; + delete[] mColors; + delete[] mPositions; +} + +void SkiaLinearGradientShader::describe(ProgramDescription& description, + const Extensions& extensions) { + description.hasGradient = true; + description.gradientType = ProgramDescription::kGradientLinear; +} + +void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + glActiveTexture(gTextureUnitsMap[textureSlot]); + + Texture* texture = mGradientCache->get(mKey); + if (!texture) { + texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount, mTileX); + } + + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + + // Uniforms + bindTexture(texture->id, gTileModes[mTileX], gTileModes[mTileY], textureSlot); + glUniform1i(program->getUniform("gradientSampler"), textureSlot); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Circular gradient shader +/////////////////////////////////////////////////////////////////////////////// + +static void toCircularUnitMatrix(const float x, const float y, const float radius, + SkMatrix* matrix) { + const float inv = 1.0f / radius; + matrix->setTranslate(-x, -y); + matrix->postScale(inv, inv); +} + +SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius, + uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, + SkMatrix* matrix, bool blend): + SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key, + tileMode, matrix, blend) { + SkMatrix unitMatrix; + toCircularUnitMatrix(x, y, radius, &unitMatrix); + mUnitMatrix.load(unitMatrix); + + updateLocalMatrix(matrix); +} + +void SkiaCircularGradientShader::describe(ProgramDescription& description, + const Extensions& extensions) { + description.hasGradient = true; + description.gradientType = ProgramDescription::kGradientCircular; +} + +/////////////////////////////////////////////////////////////////////////////// +// Sweep gradient shader +/////////////////////////////////////////////////////////////////////////////// + +static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) { + matrix->setTranslate(-x, -y); +} + +SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors, + float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend): + SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, matrix, blend), + mColors(colors), mPositions(positions), mCount(count) { + SkMatrix unitMatrix; + toSweepUnitMatrix(x, y, &unitMatrix); + mUnitMatrix.load(unitMatrix); + + updateLocalMatrix(matrix); +} + +SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, + float* positions, int count, SkShader* key, SkShader::TileMode tileMode, + SkMatrix* matrix, bool blend): + SkiaShader(type, key, tileMode, tileMode, matrix, blend), + mColors(colors), mPositions(positions), mCount(count) { +} + +SkiaSweepGradientShader::~SkiaSweepGradientShader() { + delete[] mColors; + delete[] mPositions; +} + +void SkiaSweepGradientShader::describe(ProgramDescription& description, + const Extensions& extensions) { + description.hasGradient = true; + description.gradientType = ProgramDescription::kGradientSweep; +} + +void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + glActiveTexture(gTextureUnitsMap[textureSlot]); + + Texture* texture = mGradientCache->get(mKey); + if (!texture) { + texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount); + } + + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + + // Uniforms + bindTexture(texture->id, gTileModes[mTileX], gTileModes[mTileY], textureSlot); + glUniform1i(program->getUniform("gradientSampler"), textureSlot); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Compose shader +/////////////////////////////////////////////////////////////////////////////// + +SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second, + SkXfermode::Mode mode, SkShader* key): + SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, + NULL, first->blend() || second->blend()), mFirst(first), mSecond(second), mMode(mode) { +} + +void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) { + SkiaShader::set(textureCache, gradientCache); + mFirst->set(textureCache, gradientCache); + mSecond->set(textureCache, gradientCache); +} + +void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) { + mFirst->describe(description, extensions); + mSecond->describe(description, extensions); + if (mFirst->type() == kBitmap) { + description.isBitmapFirst = true; + } + description.shadersMode = mMode; +} + +void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + mFirst->setupProgram(program, modelView, snapshot, textureUnit); + mSecond->setupProgram(program, modelView, snapshot, textureUnit); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h new file mode 100644 index 0000000..2565e65 --- /dev/null +++ b/libs/hwui/SkiaShader.h @@ -0,0 +1,215 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_SKIA_SHADER_H +#define ANDROID_UI_SKIA_SHADER_H + +#include <SkShader.h> +#include <SkXfermode.h> + +#include <GLES2/gl2.h> + +#include "Extensions.h" +#include "ProgramCache.h" +#include "TextureCache.h" +#include "GradientCache.h" +#include "Snapshot.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Base shader +/////////////////////////////////////////////////////////////////////////////// + +/** + * Represents a Skia shader. A shader will modify the GL context and active + * program to recreate the original effect. + */ +struct SkiaShader { + /** + * Type of Skia shader in use. + */ + enum Type { + kNone, + kBitmap, + kLinearGradient, + kCircularGradient, + kSweepGradient, + kCompose + }; + + SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, + SkMatrix* matrix, bool blend); + virtual ~SkiaShader(); + + virtual void describe(ProgramDescription& description, const Extensions& extensions); + virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + + inline bool blend() const { + return mBlend; + } + + Type type() const { + return mType; + } + + virtual void set(TextureCache* textureCache, GradientCache* gradientCache) { + mTextureCache = textureCache; + mGradientCache = gradientCache; + } + + virtual void updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + } + + void setMatrix(SkMatrix* matrix) { + updateLocalMatrix(matrix); + } + + void updateLocalMatrix(const SkMatrix* matrix) { + if (matrix) { + mat4 localMatrix(*matrix); + mShaderMatrix.loadInverse(localMatrix); + } else { + mShaderMatrix.loadIdentity(); + } + } + + void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView); + +protected: + inline void bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit); + + Type mType; + SkShader* mKey; + SkShader::TileMode mTileX; + SkShader::TileMode mTileY; + bool mBlend; + + TextureCache* mTextureCache; + GradientCache* mGradientCache; + + mat4 mUnitMatrix; + mat4 mShaderMatrix; +}; // struct SkiaShader + + +/////////////////////////////////////////////////////////////////////////////// +// Implementations +/////////////////////////////////////////////////////////////////////////////// + +/** + * A shader that draws a bitmap. + */ +struct SkiaBitmapShader: public SkiaShader { + SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool blend); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); + +private: + /** + * This method does not work for n == 0. + */ + inline bool isPowerOfTwo(unsigned int n) { + return !(n & (n - 1)); + } + + SkBitmap* mBitmap; + const Texture* mTexture; + GLenum mWrapS; + GLenum mWrapT; +}; // struct SkiaBitmapShader + +/** + * A shader that draws a linear gradient. + */ +struct SkiaLinearGradientShader: public SkiaShader { + SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions, int count, + SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + ~SkiaLinearGradientShader(); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); + +private: + float* mBounds; + uint32_t* mColors; + float* mPositions; + int mCount; +}; // struct SkiaLinearGradientShader + +/** + * A shader that draws a sweep gradient. + */ +struct SkiaSweepGradientShader: public SkiaShader { + SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions, int count, + SkShader* key, SkMatrix* matrix, bool blend); + ~SkiaSweepGradientShader(); + + virtual void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); + +protected: + SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions, + int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + + uint32_t* mColors; + float* mPositions; + int mCount; +}; // struct SkiaSweepGradientShader + +/** + * A shader that draws a circular gradient. + */ +struct SkiaCircularGradientShader: public SkiaSweepGradientShader { + SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions, + int count, SkShader* key,SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + + void describe(ProgramDescription& description, const Extensions& extensions); +}; // struct SkiaCircularGradientShader + +/** + * A shader that draws two shaders, composited with an xfermode. + */ +struct SkiaComposeShader: public SkiaShader { + SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, SkShader* key); + + void set(TextureCache* textureCache, GradientCache* gradientCache); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + +private: + SkiaShader* mFirst; + SkiaShader* mSecond; + SkXfermode::Mode mMode; +}; // struct SkiaComposeShader + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_SKIA_SHADER_H diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h new file mode 100644 index 0000000..2da1950 --- /dev/null +++ b/libs/hwui/Snapshot.h @@ -0,0 +1,256 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_SNAPSHOT_H +#define ANDROID_UI_SNAPSHOT_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/RefBase.h> + +#include <SkCanvas.h> +#include <SkRegion.h> + +#include "Layer.h" +#include "Matrix.h" +#include "Rect.h" + +namespace android { +namespace uirenderer { + +/** + * A snapshot holds information about the current state of the rendering + * surface. A snapshot is usually created whenever the user calls save() + * and discarded when the user calls restore(). Once a snapshot is created, + * it can hold information for deferred rendering. + * + * Each snapshot has a link to a previous snapshot, indicating the previous + * state of the renderer. + */ +class Snapshot: public LightRefBase<Snapshot> { +public: + Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), invisible(false) { + transform = &mTransformRoot; + clipRect = &mClipRectRoot; + } + + /** + * Copies the specified snapshot/ The specified snapshot is stored as + * the previous snapshot. + */ + Snapshot(const sp<Snapshot>& s, int saveFlags): + flags(0), previous(s), layer(NULL), fbo(s->fbo), + invisible(s->invisible), viewport(s->viewport), height(s->height) { + if (saveFlags & SkCanvas::kMatrix_SaveFlag) { + mTransformRoot.load(*s->transform); + transform = &mTransformRoot; + } else { + transform = s->transform; + } + + if (saveFlags & SkCanvas::kClip_SaveFlag) { + mClipRectRoot.set(*s->clipRect); + clipRect = &mClipRectRoot; + } else { + clipRect = s->clipRect; + } + + if ((s->flags & Snapshot::kFlagClipSet) && + !(s->flags & Snapshot::kFlagDirtyLocalClip)) { + mLocalClip.set(s->mLocalClip); + } else { + flags |= Snapshot::kFlagDirtyLocalClip; + } + } + + /** + * Various flags set on #flags. + */ + enum Flags { + /** + * Indicates that the clip region was modified. When this + * snapshot is restored so must the clip. + */ + kFlagClipSet = 0x1, + /** + * Indicates that this snapshot was created when saving + * a new layer. + */ + kFlagIsLayer = 0x2, + /** + * Indicates that this snapshot is a special type of layer + * backed by an FBO. This flag only makes sense when the + * flag kFlagIsLayer is also set. + */ + kFlagIsFboLayer = 0x4, + /** + * Indicates that the local clip should be recomputed. + */ + kFlagDirtyLocalClip = 0x8, + /** + * Indicates that this snapshot has changed the ortho matrix. + */ + kFlagDirtyOrtho = 0x10, + }; + + /** + * Modifies the current clip with the new clip rectangle and + * the specified operation. The specified rectangle is transformed + * by this snapshot's trasnformation. + */ + bool clip(float left, float top, float right, float bottom, + SkRegion::Op op = SkRegion::kIntersect_Op) { + Rect r(left, top, right, bottom); + transform->mapRect(r); + return clipTransformed(r, op); + } + + /** + * Modifies the current clip with the new clip rectangle and + * the specified operation. The specified rectangle is considered + * already transformed. + */ + bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) { + bool clipped = false; + + // NOTE: The unimplemented operations require support for regions + // Supporting regions would require using a stencil buffer instead + // of the scissor. The stencil buffer itself is not too expensive + // (memory cost excluded) but on fillrate limited devices, managing + // the stencil might have a negative impact on the framerate. + switch (op) { + case SkRegion::kDifference_Op: + break; + case SkRegion::kIntersect_Op: + clipped = clipRect->intersect(r); + break; + case SkRegion::kUnion_Op: + clipped = clipRect->unionWith(r); + break; + case SkRegion::kXOR_Op: + break; + case SkRegion::kReverseDifference_Op: + break; + case SkRegion::kReplace_Op: + clipRect->set(r); + clipped = true; + break; + } + + if (clipped) { + flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; + } + + return clipped; + } + + /** + * Sets the current clip. + */ + void setClip(float left, float top, float right, float bottom) { + clipRect->set(left, top, right, bottom); + flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; + } + + const Rect& getLocalClip() { + if (flags & Snapshot::kFlagDirtyLocalClip) { + mat4 inverse; + inverse.loadInverse(*transform); + + mLocalClip.set(*clipRect); + inverse.mapRect(mLocalClip); + + flags &= ~Snapshot::kFlagDirtyLocalClip; + } + return mLocalClip; + } + + void resetTransform(float x, float y, float z) { + transform = &mTransformRoot; + transform->loadTranslate(x, y, z); + } + + void resetClip(float left, float top, float right, float bottom) { + clipRect = &mClipRectRoot; + clipRect->set(left, top, right, bottom); + flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; + } + + /** + * Dirty flags. + */ + int flags; + + /** + * Previous snapshot. + */ + sp<Snapshot> previous; + + /** + * Only set when the flag kFlagIsLayer is set. + */ + Layer* layer; + + /** + * Only set when the flag kFlagIsFboLayer is set. + */ + GLuint fbo; + + /** + * Indicates that this snapshot is invisible and nothing should be drawn + * inside it. + */ + bool invisible; + + /** + * Current viewport. + */ + Rect viewport; + + /** + * Height of the framebuffer the snapshot is rendering into. + */ + int height; + + /** + * Contains the previous ortho matrix. + */ + mat4 orthoMatrix; + + /** + * Local transformation. Holds the current translation, scale and + * rotation values. + */ + mat4* transform; + + /** + * Current clip region. The clip is stored in canvas-space coordinates, + * (screen-space coordinates in the regular case.) + */ + Rect* clipRect; + +private: + mat4 mTransformRoot; + Rect mClipRectRoot; + Rect mLocalClip; + +}; // class Snapshot + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_SNAPSHOT_H diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp new file mode 100644 index 0000000..9d54277 --- /dev/null +++ b/libs/hwui/TextDropShadowCache.cpp @@ -0,0 +1,149 @@ +/* + * 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 "TextDropShadowCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +TextDropShadowCache::TextDropShadowCache(): + mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting drop shadow cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE); + } + + mCache.setOnEntryRemovedListener(this); +} + +TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize): + mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + mCache.setOnEntryRemovedListener(this); +} + +TextDropShadowCache::~TextDropShadowCache() { + mCache.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t TextDropShadowCache::getSize() { + return mSize; +} + +uint32_t TextDropShadowCache::getMaxSize() { + return mMaxSize; +} + +void TextDropShadowCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) { + const uint32_t size = texture->width * texture->height; + mSize -= size; + + if (texture) { + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void TextDropShadowCache::clear() { + mCache.clear(); +} + +ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len, + int numGlyphs, uint32_t radius) { + ShadowText entry(paint, radius, len, text); + ShadowTexture* texture = mCache.get(entry); + + if (!texture) { + FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0, + len, numGlyphs, radius); + + texture = new ShadowTexture; + texture->left = shadow.penX; + texture->top = shadow.penY; + texture->width = shadow.width; + texture->height = shadow.height; + texture->generation = 0; + texture->blend = true; + + const uint32_t size = shadow.width * shadow.height; + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + // Textures are Alpha8 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (size < mMaxSize) { + mSize += size; + mCache.put(entry, texture); + } else { + texture->cleanup = true; + } + + // Cleanup shadow + delete[] shadow.image; + } + + return texture; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h new file mode 100644 index 0000000..16e2814 --- /dev/null +++ b/libs/hwui/TextDropShadowCache.h @@ -0,0 +1,151 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H +#define ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H + +#include <GLES2/gl2.h> + +#include <SkPaint.h> + +#include "utils/GenerationCache.h" +#include "FontRenderer.h" +#include "Texture.h" + +namespace android { +namespace uirenderer { + +struct ShadowText { + ShadowText() { + text = NULL; + } + + ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText): + radius(radius), len(len) { + text = new char[len]; + memcpy(text, srcText, len); + + textSize = paint->getTextSize(); + typeface = paint->getTypeface(); + + hash = 0; + uint32_t multiplier = 1; + for (uint32_t i = 0; i < len; i++) { + hash += text[i] * multiplier; + uint32_t shifted = multiplier << 5; + multiplier = shifted - multiplier; + } + } + + ShadowText(const ShadowText& shadow): + radius(shadow.radius), len(shadow.len), hash(shadow.hash), + textSize(shadow.textSize), typeface(shadow.typeface) { + text = new char[shadow.len]; + memcpy(text, shadow.text, shadow.len); + } + + ~ShadowText() { + delete[] text; + } + + uint32_t radius; + uint32_t len; + uint32_t hash; + float textSize; + SkTypeface* typeface; + char *text; + + bool operator<(const ShadowText& rhs) const { + if (hash < rhs.hash) return true; + else if (hash == rhs.hash) { + if (len < rhs.len) return true; + else if (len == rhs.len) { + if (radius < rhs.radius) return true; + else if (radius == rhs.radius) { + if (textSize < rhs.textSize) return true; + else if (textSize == rhs.textSize) { + if (typeface < rhs.typeface) return true; + else if (typeface == rhs.typeface) { + return strncmp(text, rhs.text, len) < 0; + } + } + } + } + } + return false; + } +}; // struct ShadowText + +/** + * Alpha texture used to represent a shadow. + */ +struct ShadowTexture: public Texture { + ShadowTexture(): Texture() { + } + + float left; + float top; +}; // struct ShadowTexture + +class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> { +public: + TextDropShadowCache(); + TextDropShadowCache(uint32_t maxByteSize); + ~TextDropShadowCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(ShadowText& text, ShadowTexture*& texture); + + ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len, + int numGlyphs, uint32_t radius); + + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + + void setFontRenderer(FontRenderer& fontRenderer) { + mRenderer = &fontRenderer; + } + + /** + * 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(); + +private: + GenerationCache<ShadowText, ShadowTexture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + FontRenderer* mRenderer; +}; // class TextDropShadowCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h new file mode 100644 index 0000000..817f143 --- /dev/null +++ b/libs/hwui/Texture.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_TEXTURE_H +#define ANDROID_UI_TEXTURE_H + +#include <GLES2/gl2.h> + +namespace android { +namespace uirenderer { + +/** + * Represents an OpenGL texture. + */ +struct Texture { + Texture() { + cleanup = false; + bitmapSize = 0; + } + + /** + * Name of the texture. + */ + GLuint id; + /** + * Generation of the backing bitmap, + */ + uint32_t generation; + /** + * Indicates whether the texture requires blending. + */ + bool blend; + /** + * Width of the backing bitmap. + */ + uint32_t width; + /** + * Height of the backing bitmap. + */ + uint32_t height; + /** + * Indicates whether this texture should be cleaned up after use. + */ + bool cleanup; + /** + * Optional, size of the original bitmap. + */ + uint32_t bitmapSize; +}; // struct Texture + +class AutoTexture { +public: + AutoTexture(const Texture* texture): mTexture(texture) { } + ~AutoTexture() { + if (mTexture && mTexture->cleanup) { + glDeleteTextures(1, &mTexture->id); + delete mTexture; + } + } + +private: + const Texture* mTexture; +}; // class AutoTexture + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_TEXTURE_H diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp new file mode 100644 index 0000000..701df83 --- /dev/null +++ b/libs/hwui/TextureCache.cpp @@ -0,0 +1,237 @@ +/* + * 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 <GLES2/gl2.h> + +#include <SkCanvas.h> + +#include <utils/threads.h> + +#include "TextureCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +TextureCache::TextureCache(): + mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting texture cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); + } + + init(); +} + +TextureCache::TextureCache(uint32_t maxByteSize): + mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + init(); +} + +TextureCache::~TextureCache() { + Mutex::Autolock _l(mLock); + mCache.clear(); +} + +void TextureCache::init() { + mCache.setOnEntryRemovedListener(this); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t TextureCache::getSize() { + Mutex::Autolock _l(mLock); + return mSize; +} + +uint32_t TextureCache::getMaxSize() { + Mutex::Autolock _l(mLock); + return mMaxSize; +} + +void TextureCache::setMaxSize(uint32_t maxSize) { + Mutex::Autolock _l(mLock); + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) { + // This will be called already locked + if (texture) { + mSize -= texture->bitmapSize; + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +Texture* TextureCache::get(SkBitmap* bitmap) { + mLock.lock(); + Texture* texture = mCache.get(bitmap); + mLock.unlock(); + + if (!texture) { + if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { + LOGW("Bitmap too large to be uploaded into a texture"); + return NULL; + } + + const uint32_t size = bitmap->rowBytes() * bitmap->height(); + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + mLock.lock(); + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + mLock.unlock(); + } + + texture = new Texture; + texture->bitmapSize = size; + generateTexture(bitmap, texture, false); + + if (size < mMaxSize) { + mLock.lock(); + mSize += size; + mCache.put(bitmap, texture); + mLock.unlock(); + } else { + texture->cleanup = true; + } + } else if (bitmap->getGenerationID() != texture->generation) { + generateTexture(bitmap, texture, true); + } + + return texture; +} + +void TextureCache::remove(SkBitmap* bitmap) { + Mutex::Autolock _l(mLock); + mCache.remove(bitmap); +} + +void TextureCache::clear() { + Mutex::Autolock _l(mLock); + mCache.clear(); +} + +void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) { + SkAutoLockPixels alp(*bitmap); + + if (!bitmap->readyToDraw()) { + LOGE("Cannot generate texture from bitmap"); + return; + } + + const bool resize = !regenerate || bitmap->width() != int(texture->width) || + bitmap->height() != int(texture->height); + + if (!regenerate) { + glGenTextures(1, &texture->id); + } + + texture->generation = bitmap->getGenerationID(); + texture->width = bitmap->width(); + texture->height = bitmap->height(); + + glBindTexture(GL_TEXTURE_2D, texture->id); + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + + switch (bitmap->getConfig()) { + case SkBitmap::kA8_Config: + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, + GL_UNSIGNED_BYTE, bitmap->getPixels()); + texture->blend = true; + break; + case SkBitmap::kRGB_565_Config: + uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height, + GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); + texture->blend = false; + break; + case SkBitmap::kARGB_8888_Config: + uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, + GL_UNSIGNED_BYTE, bitmap->getPixels()); + // Do this after calling getPixels() to make sure Skia's deferred + // decoding happened + texture->blend = !bitmap->isOpaque(); + break; + case SkBitmap::kIndex8_Config: + uploadPalettedTexture(resize, bitmap, texture->width, texture->height); + texture->blend = false; + break; + default: + LOGW("Unsupported bitmap config: %d", bitmap->getConfig()); + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void TextureCache::uploadPalettedTexture(bool resize, SkBitmap* bitmap, + uint32_t width, uint32_t height) { + SkBitmap rgbaBitmap; + rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + rgbaBitmap.allocPixels(); + rgbaBitmap.eraseColor(0); + + SkCanvas canvas(rgbaBitmap); + canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); + + uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height, + GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); +} + +void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, + GLenum type, const GLvoid * data) { + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h new file mode 100644 index 0000000..467e851 --- /dev/null +++ b/libs/hwui/TextureCache.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_TEXTURE_CACHE_H +#define ANDROID_UI_TEXTURE_CACHE_H + +#include <SkBitmap.h> + +#include "Texture.h" +#include "utils/GenerationCache.h" + +namespace android { +namespace uirenderer { + +/** + * A simple LRU texture 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 TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> { +public: + TextureCache(); + TextureCache(uint32_t maxByteSize); + ~TextureCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(SkBitmap*& bitmap, Texture*& texture); + + /** + * Returns the texture associated with the specified bitmap. If the texture + * cannot be found in the cache, a new texture is generated. + */ + Texture* get(SkBitmap* bitmap); + /** + * Removes the texture associated with the specified bitmap. Returns NULL + * if the texture cannot be found. Upon remove the texture is freed. + */ + void remove(SkBitmap* bitmap); + /** + * 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(); + +private: + /** + * Generates the texture from a bitmap into the specified texture structure. + * + * @param regenerate If true, the bitmap data is reuploaded into the texture, but + * no new texture is generated. + */ + void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); + + void uploadPalettedTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height); + void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, + GLenum type, const GLvoid * data); + + void init(); + + GenerationCache<SkBitmap*, Texture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + GLint mMaxTextureSize; + + /** + * Used to access mCache and mSize. All methods are accessed from a single + * thread except for remove(). + */ + mutable Mutex mLock; +}; // class TextureCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_TEXTURE_CACHE_H diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h new file mode 100644 index 0000000..1f54086 --- /dev/null +++ b/libs/hwui/Vertex.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_VERTEX_H +#define ANDROID_UI_VERTEX_H + +namespace android { +namespace uirenderer { + +/** + * Simple structure to describe a vertex with a position and a texture. + */ +struct TextureVertex { + float position[2]; + float texture[2]; + + static inline void set(TextureVertex* vertex, float x, float y, float u, float v) { + vertex[0].position[0] = x; + vertex[0].position[1] = y; + vertex[0].texture[0] = u; + vertex[0].texture[1] = v; + } + + static inline void setUV(TextureVertex* vertex, float u, float v) { + vertex[0].texture[0] = u; + vertex[0].texture[1] = v; + } +}; // struct TextureVertex + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_VERTEX_H diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h new file mode 100644 index 0000000..754b470 --- /dev/null +++ b/libs/hwui/utils/Compare.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_COMPARE_H +#define ANDROID_UI_COMPARE_H + +#include <cmath> + +#define EPSILON 0.00001f + +#define almost(u, v) (fabs((u) - (v)) < EPSILON) + +/** + * Compare floats. + */ +#define compare(a) \ + if (a < rhs.a) return true; \ + if (almost(a, rhs.a)) + +/** + * Compare integers. + */ +#define compareI(a) \ + if (a < rhs.a) return true; \ + if (a == rhs.a) + +#endif // ANDROID_UI_COMPARE_H diff --git a/libs/hwui/utils/GenerationCache.h b/libs/hwui/utils/GenerationCache.h new file mode 100644 index 0000000..5cea30f --- /dev/null +++ b/libs/hwui/utils/GenerationCache.h @@ -0,0 +1,245 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_GENERATION_CACHE_H +#define ANDROID_UI_GENERATION_CACHE_H + +#include <utils/KeyedVector.h> +#include <utils/RefBase.h> + +namespace android { +namespace uirenderer { + +template<typename EntryKey, typename EntryValue> +class OnEntryRemoved { +public: + virtual ~OnEntryRemoved() { }; + virtual void operator()(EntryKey& key, EntryValue& value) = 0; +}; // class OnEntryRemoved + +template<typename EntryKey, typename EntryValue> +struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > { + Entry() { } + Entry(const Entry<EntryKey, EntryValue>& e): + key(e.key), value(e.value), parent(e.parent), child(e.child) { } + Entry(sp<Entry<EntryKey, EntryValue> > e): + key(e->key), value(e->value), parent(e->parent), child(e->child) { } + + EntryKey key; + EntryValue value; + + sp<Entry<EntryKey, EntryValue> > parent; + sp<Entry<EntryKey, EntryValue> > child; +}; // struct Entry + +template<typename K, typename V> +class GenerationCache { +public: + GenerationCache(uint32_t maxCapacity); + virtual ~GenerationCache(); + + enum Capacity { + kUnlimitedCapacity, + }; + + void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener); + + void clear(); + + bool contains(K key) const; + V get(K key); + K getKeyAt(uint32_t index) const; + bool put(K key, V value); + V remove(K key); + V removeOldest(); + V getValueAt(uint32_t index) const; + + uint32_t size() const; + + void addToCache(sp<Entry<K, V> > entry, K key, V value); + void attachToCache(sp<Entry<K, V> > entry); + void detachFromCache(sp<Entry<K, V> > entry); + + V removeAt(ssize_t index); + + KeyedVector<K, sp<Entry<K, V> > > mCache; + uint32_t mMaxCapacity; + + OnEntryRemoved<K, V>* mListener; + + sp<Entry<K, V> > mOldest; + sp<Entry<K, V> > mYoungest; +}; // class GenerationCache + +template<typename K, typename V> +GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { +}; + +template<typename K, typename V> +GenerationCache<K, V>::~GenerationCache() { + clear(); +}; + +template<typename K, typename V> +uint32_t GenerationCache<K, V>::size() const { + return mCache.size(); +} + +template<typename K, typename V> +void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) { + mListener = listener; +} + +template<typename K, typename V> +void GenerationCache<K, V>::clear() { + if (mListener) { + for (uint32_t i = 0; i < mCache.size(); i++) { + sp<Entry<K, V> > entry = mCache.valueAt(i); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + } + } + mCache.clear(); + mYoungest.clear(); + mOldest.clear(); +} + +template<typename K, typename V> +bool GenerationCache<K, V>::contains(K key) const { + return mCache.indexOfKey(key) >= 0; +} + +template<typename K, typename V> +K GenerationCache<K, V>::getKeyAt(uint32_t index) const { + return mCache.keyAt(index); +} + +template<typename K, typename V> +V GenerationCache<K, V>::getValueAt(uint32_t index) const { + return mCache.valueAt(index); +} + +template<typename K, typename V> +V GenerationCache<K, V>::get(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + sp<Entry<K, V> > entry = mCache.valueAt(index); + if (entry.get()) { + detachFromCache(entry); + attachToCache(entry); + return entry->value; + } + } + + return NULL; +} + +template<typename K, typename V> +bool GenerationCache<K, V>::put(K key, V value) { + if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { + removeOldest(); + } + + ssize_t index = mCache.indexOfKey(key); + if (index < 0) { + sp<Entry<K, V> > entry = new Entry<K, V>; + addToCache(entry, key, value); + return true; + } + + return false; +} + +template<typename K, typename V> +void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) { + entry->key = key; + entry->value = value; + mCache.add(key, entry); + attachToCache(entry); +} + +template<typename K, typename V> +V GenerationCache<K, V>::remove(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + return removeAt(index); + } + + return NULL; +} + +template<typename K, typename V> +V GenerationCache<K, V>::removeAt(ssize_t index) { + sp<Entry<K, V> > entry = mCache.valueAt(index); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + mCache.removeItemsAt(index, 1); + detachFromCache(entry); + + return entry->value; +} + +template<typename K, typename V> +V GenerationCache<K, V>::removeOldest() { + if (mOldest.get()) { + ssize_t index = mCache.indexOfKey(mOldest->key); + if (index >= 0) { + return removeAt(index); + } + } + + return NULL; +} + +template<typename K, typename V> +void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) { + if (!mYoungest.get()) { + mYoungest = mOldest = entry; + } else { + entry->parent = mYoungest; + mYoungest->child = entry; + mYoungest = entry; + } +} + +template<typename K, typename V> +void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) { + if (entry->parent.get()) { + entry->parent->child = entry->child; + } + + if (entry->child.get()) { + entry->child->parent = entry->parent; + } + + if (mOldest == entry) { + mOldest = entry->child; + } + + if (mYoungest == entry) { + mYoungest = entry->parent; + } + + entry->parent.clear(); + entry->child.clear(); +} + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_GENERATION_CACHE_H diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h new file mode 100644 index 0000000..68f5e9d --- /dev/null +++ b/libs/hwui/utils/SortedList.h @@ -0,0 +1,242 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_SORTED_LIST_H +#define ANDROID_UI_SORTED_LIST_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Vector.h> +#include <utils/TypeHelpers.h> + +#include "SortedListImpl.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Sorted list +/////////////////////////////////////////////////////////////////////////////// + +template<class TYPE> +class SortedList: private SortedListImpl { +public: + typedef TYPE value_type; + + SortedList(); + SortedList(const SortedList<TYPE>& rhs); + virtual ~SortedList(); + + const SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs) const; + SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs); + + inline void clear() { + VectorImpl::clear(); + } + + inline size_t size() const { + return VectorImpl::size(); + } + + inline bool isEmpty() const { + return VectorImpl::isEmpty(); + } + + inline size_t capacity() const { + return VectorImpl::capacity(); + } + + inline ssize_t setCapacity(size_t size) { + return VectorImpl::setCapacity(size); + } + + inline const TYPE* array() const; + + TYPE* editArray(); + + ssize_t indexOf(const TYPE& item) const; + size_t orderOf(const TYPE& item) const; + + inline const TYPE& operator [](size_t index) const; + inline const TYPE& itemAt(size_t index) const; + const TYPE& top() const; + const TYPE& mirrorItemAt(ssize_t index) const; + + ssize_t add(const TYPE& item); + + TYPE& editItemAt(size_t index) { + return *(static_cast<TYPE *> (VectorImpl::editItemLocation(index))); + } + + ssize_t merge(const Vector<TYPE>& vector); + ssize_t merge(const SortedList<TYPE>& vector); + + ssize_t remove(const TYPE&); + + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + inline ssize_t removeAt(size_t index) { + return removeItemsAt(index); + } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; // class SortedList + +/////////////////////////////////////////////////////////////////////////////// +// Implementation +/////////////////////////////////////////////////////////////////////////////// + +template<class TYPE> +inline SortedList<TYPE>::SortedList(): + SortedListImpl(sizeof(TYPE), ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + | (traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + | (traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))) { +} + +template<class TYPE> +inline SortedList<TYPE>::SortedList(const SortedList<TYPE>& rhs): SortedListImpl(rhs) { +} + +template<class TYPE> inline SortedList<TYPE>::~SortedList() { + finish_vector(); +} + +template<class TYPE> +inline SortedList<TYPE>& SortedList<TYPE>::operator =(const SortedList<TYPE>& rhs) { + SortedListImpl::operator =(rhs); + return *this; +} + +template<class TYPE> +inline const SortedList<TYPE>& SortedList<TYPE>::operator =( + const SortedList<TYPE>& rhs) const { + SortedListImpl::operator =(rhs); + return *this; +} + +template<class TYPE> +inline const TYPE* SortedList<TYPE>::array() const { + return static_cast<const TYPE *> (arrayImpl()); +} + +template<class TYPE> +inline TYPE* SortedList<TYPE>::editArray() { + return static_cast<TYPE *> (editArrayImpl()); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::operator[](size_t index) const { + assert( index<size() ); + return *(array() + index); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::itemAt(size_t index) const { + return operator[](index); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::mirrorItemAt(ssize_t index) const { + assert( (index>0 ? index : -index)<size() ); + return *(array() + ((index < 0) ? (size() - index) : index)); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::top() const { + return *(array() + size() - 1); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::add(const TYPE& item) { + return SortedListImpl::add(&item); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::indexOf(const TYPE& item) const { + return SortedListImpl::indexOf(&item); +} + +template<class TYPE> +inline size_t SortedList<TYPE>::orderOf(const TYPE& item) const { + return SortedListImpl::orderOf(&item); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::merge(const Vector<TYPE>& vector) { + return SortedListImpl::merge(reinterpret_cast<const VectorImpl&> (vector)); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::merge(const SortedList<TYPE>& vector) { + return SortedListImpl::merge(reinterpret_cast<const SortedListImpl&> (vector)); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::remove(const TYPE& item) { + return SortedListImpl::remove(&item); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +template<class TYPE> +void SortedList<TYPE>::do_construct(void* storage, size_t num) const { + construct_type(reinterpret_cast<TYPE*> (storage), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_destroy(void* storage, size_t num) const { + destroy_type(reinterpret_cast<TYPE*> (storage), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_copy(void* dest, const void* from, size_t num) const { + copy_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_splat(void* dest, const void* item, size_t num) const { + splat_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (item), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num); +} + +template<class TYPE> +int SortedList<TYPE>::do_compare(const void* lhs, const void* rhs) const { + return compare_type(*reinterpret_cast<const TYPE*> (lhs), *reinterpret_cast<const TYPE*> (rhs)); +} + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_SORTED_LIST_H diff --git a/libs/hwui/utils/SortedListImpl.cpp b/libs/hwui/utils/SortedListImpl.cpp new file mode 100644 index 0000000..35171d5 --- /dev/null +++ b/libs/hwui/utils/SortedListImpl.cpp @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#include "SortedListImpl.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Sorted list implementation, not for direct use +/////////////////////////////////////////////////////////////////////////////// + +SortedListImpl::SortedListImpl(size_t itemSize, uint32_t flags): VectorImpl(itemSize, flags) { +} + +SortedListImpl::SortedListImpl(const VectorImpl& rhs): VectorImpl(rhs) { +} + +SortedListImpl::~SortedListImpl() { +} + +SortedListImpl& SortedListImpl::operator =(const SortedListImpl& rhs) { + return static_cast<SortedListImpl&> + (VectorImpl::operator =(static_cast<const VectorImpl&> (rhs))); +} + +ssize_t SortedListImpl::indexOf(const void* item) const { + return _indexOrderOf(item); +} + +size_t SortedListImpl::orderOf(const void* item) const { + size_t o; + _indexOrderOf(item, &o); + return o; +} + +ssize_t SortedListImpl::_indexOrderOf(const void* item, size_t* order) const { + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = size() - 1; + ssize_t mid; + const void* a = arrayImpl(); + const size_t s = itemSize(); + while (l <= h) { + mid = l + (h - l) / 2; + const void* const curr = reinterpret_cast<const char *> (a) + (mid * s); + const int c = do_compare(curr, item); + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) { + *order = l; + } + return err; +} + +ssize_t SortedListImpl::add(const void* item) { + size_t order; + ssize_t index = _indexOrderOf(item, &order); + index = VectorImpl::insertAt(item, order, 1); + return index; +} + +ssize_t SortedListImpl::merge(const VectorImpl& vector) { + // naive merge... + if (!vector.isEmpty()) { + const void* buffer = vector.arrayImpl(); + const size_t is = itemSize(); + size_t s = vector.size(); + for (size_t i = 0; i < s; i++) { + ssize_t err = add(reinterpret_cast<const char*> (buffer) + i * is); + if (err < 0) { + return err; + } + } + } + return NO_ERROR; +} + +ssize_t SortedListImpl::merge(const SortedListImpl& vector) { + // we've merging a sorted vector... nice! + ssize_t err = NO_ERROR; + if (!vector.isEmpty()) { + // first take care of the case where the vectors are sorted together + if (do_compare(vector.itemLocation(vector.size() - 1), arrayImpl()) <= 0) { + err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&> (vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size() - 1)) >= 0) { + err = VectorImpl::appendVector(static_cast<const VectorImpl&> (vector)); + } else { + // this could be made a little better + err = merge(static_cast<const VectorImpl&> (vector)); + } + } + return err; +} + +ssize_t SortedListImpl::remove(const void* item) { + ssize_t i = indexOf(item); + if (i >= 0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h new file mode 100644 index 0000000..7da09ef --- /dev/null +++ b/libs/hwui/utils/SortedListImpl.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_SORTED_LIST_IMPL_H +#define ANDROID_UI_SORTED_LIST_IMPL_H + +#include <utils/VectorImpl.h> + +namespace android { +namespace uirenderer { + +class SortedListImpl: public VectorImpl { +public: + SortedListImpl(size_t itemSize, uint32_t flags); + SortedListImpl(const VectorImpl& rhs); + virtual ~SortedListImpl(); + + SortedListImpl& operator =(const SortedListImpl& rhs); + + ssize_t indexOf(const void* item) const; + size_t orderOf(const void* item) const; + ssize_t add(const void* item); + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedListImpl& vector); + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_SORTED_LIST_IMPL_H |