diff options
| author | Romain Guy <romainguy@google.com> | 2010-08-05 17:25:48 -0700 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-08-05 17:25:48 -0700 |
| commit | 8cc44cb6696995d8387226cd131fc22386aa4f7f (patch) | |
| tree | 21b9feb715bce7f3d019b8d93e205b5c480818e5 | |
| parent | 434e4b4b64235a756047a55e642f0858252f2dd2 (diff) | |
| parent | 7fbcc0492fca03857e3c45064f4aa040af817d55 (diff) | |
| download | frameworks_base-8cc44cb6696995d8387226cd131fc22386aa4f7f.zip frameworks_base-8cc44cb6696995d8387226cd131fc22386aa4f7f.tar.gz frameworks_base-8cc44cb6696995d8387226cd131fc22386aa4f7f.tar.bz2 | |
Merge "Add support for paths."
| -rw-r--r-- | core/java/android/view/GLES20Canvas.java | 7 | ||||
| -rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 6 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Path.java | 5 | ||||
| -rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 80 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 3 | ||||
| -rw-r--r-- | libs/hwui/PathCache.cpp | 165 | ||||
| -rw-r--r-- | libs/hwui/PathCache.h | 144 | ||||
| -rw-r--r-- | libs/hwui/ProgramCache.cpp | 2 | ||||
| -rw-r--r-- | libs/hwui/Properties.h | 2 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 9 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java | 144 |
12 files changed, 561 insertions, 7 deletions
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 63a0c4c..8e1338d 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -350,7 +350,6 @@ class GLES20Canvas extends Canvas { @Override public void setDrawFilter(DrawFilter filter) { - // TODO: Implement PaintDrawFilter mFilter = filter; } @@ -522,9 +521,13 @@ class GLES20Canvas extends Canvas { @Override public void drawPath(Path path, Paint paint) { - throw new UnsupportedOperationException(); + boolean hasModifier = setupModifiers(paint); + nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint); + if (hasModifier) nResetModifiers(mRenderer); } + private native void nDrawPath(int renderer, int path, int paint); + @Override public void drawPicture(Picture picture) { throw new UnsupportedOperationException(); diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 142e194..9fd2b86 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -228,6 +228,11 @@ static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas, renderer->drawRect(left, top, right, bottom, paint); } +static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) { + renderer->drawPath(path, paint); +} + // ---------------------------------------------------------------------------- // Shaders and color filters // ---------------------------------------------------------------------------- @@ -317,6 +322,7 @@ static JNINativeMethod gMethods[] = { { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch }, { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, + { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath }, { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers }, { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader }, diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index 281823a..cb2c6a2 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -593,5 +593,8 @@ public class Path { private static native void native_transform(int nPath, int matrix); private static native void finalizer(int nPath); - private final int mNativePath; + /** + * @hide + */ + public final int mNativePath; } diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 8f28612..0444964 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \ OpenGLRenderer.cpp \ Patch.cpp \ PatchCache.cpp \ + PathCache.cpp \ Program.cpp \ ProgramCache.cpp \ SkiaColorFilter.cpp \ diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index d694039..a72045b 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -37,7 +37,8 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// #define DEFAULT_TEXTURE_CACHE_SIZE 20.0f -#define DEFAULT_LAYER_CACHE_SIZE 10.0f +#define DEFAULT_LAYER_CACHE_SIZE 6.0f +#define DEFAULT_PATH_CACHE_SIZE 6.0f #define DEFAULT_PATCH_CACHE_SIZE 100 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f @@ -105,6 +106,7 @@ OpenGLRenderer::OpenGLRenderer(): mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)), mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)), mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)), + mPathCache(MB(DEFAULT_PATH_CACHE_SIZE)), mPatchCache(DEFAULT_PATCH_CACHE_SIZE) { LOGD("Create OpenGLRenderer"); @@ -125,11 +127,18 @@ OpenGLRenderer::OpenGLRenderer(): if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) { LOGD(" Setting gradient cache size to %sMB", property); - mLayerCache.setMaxSize(MB(atof(property))); + mGradientCache.setMaxSize(MB(atof(property))); } else { LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); } + if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting path cache size to %sMB", property); + mPathCache.setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE); + } + mCurrentProgram = NULL; mShader = NULL; mColorFilter = NULL; @@ -597,6 +606,73 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, glDisableVertexAttribArray(texCoordsSlot); } +void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { + GLuint textureUnit = 0; + glActiveTexture(gTextureUnits[textureUnit]); + + PathTexture* texture = mPathCache.get(path, paint); + + 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; + + // Describe the required shaders + ProgramDescription description; + description.hasTexture = true; + description.hasAlpha8Texture = true; + if (mShader) { + mShader->describe(description, mExtensions); + } + if (mColorFilter) { + mColorFilter->describe(description, mExtensions); + } + + // Build and use the appropriate shader + useProgram(mProgramCache.get(description)); + + // Setup the blending mode + chooseBlending(true, mode); + bindTexture(texture->id, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit); + glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit); + + int texCoordsSlot = mCurrentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(texCoordsSlot); + + // Setup attributes + glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE, + gMeshStride, &mMeshVertices[0].position[0]); + glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, + gMeshStride, &mMeshVertices[0].texture[0]); + + // Setup uniforms + mModelView.loadTranslate(texture->left - texture->offset, + texture->top - texture->offset, 0.0f); + mModelView.scale(texture->width, texture->height, 1.0f); + mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); + + glUniform4f(mCurrentProgram->color, r, g, b, a); + + textureUnit++; + // Setup attributes and uniforms required by the shaders + if (mShader) { + mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit); + } + if (mColorFilter) { + mColorFilter->setupProgram(mCurrentProgram); + } + + // Draw the mesh + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + + glDisableVertexAttribArray(texCoordsSlot); +} + /////////////////////////////////////////////////////////////////////////////// // Shaders /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index d2a291f..76783e9 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -44,6 +44,7 @@ #include "ProgramCache.h" #include "SkiaShader.h" #include "SkiaColorFilter.h" +#include "PathCache.h" namespace android { namespace uirenderer { @@ -92,6 +93,7 @@ public: 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 resetShader(); void setupShader(SkiaShader* shader); @@ -307,6 +309,7 @@ private: LayerCache mLayerCache; GradientCache mGradientCache; ProgramCache mProgramCache; + PathCache mPathCache; PatchCache mPatchCache; }; // class OpenGLRenderer diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp new file mode 100644 index 0000000..67a5f92 --- /dev/null +++ b/libs/hwui/PathCache.cpp @@ -0,0 +1,165 @@ +/* + * 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 "PathCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +PathCache::PathCache(uint32_t maxByteSize): + mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + mCache.setOnEntryRemovedListener(this); +} + +PathCache::~PathCache() { + mCache.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t PathCache::getSize() { + return mSize; +} + +uint32_t PathCache::getMaxSize() { + return mMaxSize; +} + +void PathCache::setMaxSize(uint32_t maxSize) { + 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 +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { + PathCacheEntry entry(path, paint); + PathTexture* texture = mCache.get(entry); + + if (!texture) { + texture = addTexture(entry, path, paint); + } else if (path->getGenerationID() != texture->generation) { + mCache.remove(entry); + texture = addTexture(entry, path, paint); + } + + // TODO: Do something to destroy the texture object if it's too big for the cache + return texture; +} + +PathTexture* PathCache::addTexture(const PathCacheEntry& entry, + const SkPath *path, const SkPaint* paint) { + const SkRect& bounds = path->getBounds(); + const float offset = entry.strokeWidth * 1.5f; + const uint32_t width = uint32_t(bounds.width() + offset * 2.0 + 0.5); + const uint32_t height = uint32_t(bounds.height() + 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) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } + + 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) { + mSize += size; + mCache.put(entry, texture); + } + + return texture; +} + +void PathCache::clear() { + 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); + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap.bytesPerPixel()); + + texture->blend = true; + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap.rowBytesAsPixels(), 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..87a9141 --- /dev/null +++ b/libs/hwui/PathCache.h @@ -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. + */ + +#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 "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 { + return memcmp(this, &rhs, sizeof(PathCacheEntry)) < 0; + } +}; // struct PathCacheEntry + +/** + * Alpha texture used to represent a path. + */ +struct PathTexture: public 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(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(); + + /** + * 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); + + GenerationCache<PathCacheEntry, PathTexture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; +}; // class PathCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_PATH_CACHE_H diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 3205258..8a97b4c 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -90,7 +90,7 @@ const char* gFS_Uniforms_ColorOp[4] = { }; const char* gFS_Main = "\nvoid main(void) {\n" - " vec4 fragColor;\n"; + " lowp vec4 fragColor;\n"; const char* gFS_Main_FetchColor = " fragColor = color;\n"; const char* gFS_Main_FetchTexture = diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 4f20a95..915b0be 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -26,10 +26,10 @@ #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" // 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" #endif // ANDROID_UI_PROPERTIES_H - diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index ef18787..5dc720f 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -152,6 +152,15 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + <activity + android:name="PathsActivity" + android:label="_Paths"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> </application> </manifest> diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java new file mode 100644 index 0000000..39d9942 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java @@ -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. + */ + +package com.google.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.os.Bundle; +import android.view.View; + +@SuppressWarnings({"UnusedDeclaration"}) +public class PathsActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final PathsView view = new PathsView(this); + setContentView(view); + } + + static class PathsView extends View { + private final Bitmap mBitmap1; + private final Paint mSmallPaint; + private final Paint mMediumPaint; + private final Paint mLargePaint; + private final BitmapShader mShader; + private final Path mPath; + private final RectF mPathBounds; + private final Paint mBoundsPaint; + private final Bitmap mBitmap; + private final float mOffset; + private final Paint mLinePaint; + + PathsView(Context c) { + super(c); + + mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1); + + mSmallPaint = new Paint(); + mSmallPaint.setAntiAlias(true); + mSmallPaint.setColor(0xffff0000); + mSmallPaint.setStrokeWidth(1.0f); + mSmallPaint.setStyle(Paint.Style.STROKE); + + mLinePaint = new Paint(); + mLinePaint.setAntiAlias(true); + mLinePaint.setColor(0xffff00ff); + mLinePaint.setStrokeWidth(1.0f); + mLinePaint.setStyle(Paint.Style.STROKE); + + mMediumPaint = new Paint(); + mMediumPaint.setAntiAlias(true); + mMediumPaint.setColor(0xff0000ff); + mMediumPaint.setStrokeWidth(10.0f); + mMediumPaint.setStyle(Paint.Style.STROKE); + + mLargePaint = new Paint(); + mLargePaint.setAntiAlias(true); + mLargePaint.setColor(0xff00ff00); + mLargePaint.setStrokeWidth(15.0f); + mLargePaint.setStyle(Paint.Style.FILL); + + mShader = new BitmapShader(mBitmap1, BitmapShader.TileMode.MIRROR, + BitmapShader.TileMode.MIRROR); + + mPath = new Path(); + mPath.moveTo(0.0f, 0.0f); + mPath.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f); + mPath.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f); + mPath.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f); + + mPathBounds = new RectF(); + mPath.computeBounds(mPathBounds, true); + + mBoundsPaint = new Paint(); + mBoundsPaint.setColor(0x4000ff00); + + mOffset = mMediumPaint.getStrokeWidth(); + final int width = (int) (mPathBounds.width() + mOffset * 3.0f + 0.5f); + final int height = (int) (mPathBounds.height() + mOffset * 3.0f + 0.5f); + mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8); + Canvas canvas = new Canvas(mBitmap); + canvas.translate(-mPathBounds.left + mOffset * 1.5f, -mPathBounds.top + mOffset * 1.5f); + canvas.drawPath(mPath, mMediumPaint); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.drawARGB(255, 255, 255, 255); + + canvas.save(); + canvas.translate(200.0f, 60.0f); + canvas.drawPath(mPath, mSmallPaint); + + canvas.translate(350.0f, 0.0f); + canvas.drawPath(mPath, mMediumPaint); + + mLargePaint.setShader(mShader); + canvas.translate(350.0f, 0.0f); + canvas.drawPath(mPath, mLargePaint); + mLargePaint.setShader(null); + canvas.restore(); + + canvas.save(); + canvas.translate(200.0f, 360.0f); + canvas.drawPath(mPath, mSmallPaint); + canvas.drawRect(mPathBounds, mBoundsPaint); + + canvas.translate(350.0f, 0.0f); + canvas.drawBitmap(mBitmap, mPathBounds.left - mOffset * 1.5f, + mPathBounds.top - mOffset * 1.5f, null); + canvas.drawRect(mPathBounds, mBoundsPaint); + canvas.drawLine(0.0f, -360.0f, 0.0f, 500.0f, mLinePaint); + + mLargePaint.setShader(mShader); + canvas.translate(350.0f, 0.0f); + canvas.drawPath(mPath, mLargePaint); + canvas.drawRect(mPathBounds, mBoundsPaint); + mLargePaint.setShader(null); + canvas.restore(); + } + } +} |
