diff options
author | Amith Yamasani <yamasani@google.com> | 2012-04-04 10:27:15 -0700 |
---|---|---|
committer | Amith Yamasani <yamasani@google.com> | 2012-04-04 12:05:59 -0700 |
commit | ec4a50428d5f26a22df3edaf7e5b08f41d5cb54b (patch) | |
tree | cd47b15082ff35879cf6e22a86f44108f45920aa | |
parent | 58a40a3e92f8a53ac1068f0839dec4a1707fa6fb (diff) | |
download | frameworks_base-ec4a50428d5f26a22df3edaf7e5b08f41d5cb54b.zip frameworks_base-ec4a50428d5f26a22df3edaf7e5b08f41d5cb54b.tar.gz frameworks_base-ec4a50428d5f26a22df3edaf7e5b08f41d5cb54b.tar.bz2 |
Embed layout padding in nine patch images
- Added a new custom PNG chunk that carries the layout padding ints.
- Extract the padding ticks from .9.png images and store in the chunk.
- Load the padding information at runtime into Bitmap and NinePatchDrawable.
- The new chunk is ordered first so that it doesn't cause a problem in older
versions of the platform.
Bug: 6087201
Change-Id: I5de46167a1d44b3ec21065b0c165e594b1dc8399
-rw-r--r-- | core/jni/android/graphics/Bitmap.cpp | 8 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 21 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapRegionDecoder.cpp | 2 | ||||
-rw-r--r-- | core/jni/android/graphics/Graphics.cpp | 10 | ||||
-rw-r--r-- | core/jni/android/graphics/GraphicsJNI.h | 3 | ||||
-rw-r--r-- | core/jni/android/graphics/NinePatchPeeker.cpp | 10 | ||||
-rw-r--r-- | core/jni/android/graphics/NinePatchPeeker.h | 10 | ||||
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 36 | ||||
-rw-r--r-- | graphics/java/android/graphics/BitmapFactory.java | 2 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/Drawable.java | 16 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/NinePatchDrawable.java | 10 | ||||
-rw-r--r-- | tools/aapt/Images.cpp | 194 | ||||
-rw-r--r-- | tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java | 3 |
13 files changed, 273 insertions, 52 deletions
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 5e73a5f..3c27caf 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -236,7 +236,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 0, 0, width, height, bitmap);
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL, NULL);
}
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
@@ -248,7 +248,7 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src, return NULL;
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL, NULL);
}
static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
@@ -407,7 +407,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { bitmap->unlockPixels();
blob.release();
- return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density);
+ return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, NULL, density);
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
@@ -485,7 +485,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(offsetXY, array, 0);
}
- return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL);
+ return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL, NULL);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index dcd1d28..dd59444 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -35,6 +35,7 @@ jfieldID gOptions_mimeFieldID; jfieldID gOptions_mCancelID; jfieldID gOptions_bitmapFieldID; jfieldID gBitmap_nativeBitmapFieldID; +jfieldID gBitmap_layoutBoundsFieldID; #if 0 #define TRACE_BITMAP(code) code @@ -276,7 +277,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, } jbyteArray ninePatchChunk = NULL; - if (peeker.fPatchIsValid) { + if (peeker.fPatch != NULL) { if (willScale) { scaleNinePatchChunk(peeker.fPatch, scale); } @@ -296,6 +297,18 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); } + jintArray layoutBounds = NULL; + if (peeker.fLayoutBounds != NULL) { + layoutBounds = env->NewIntArray(4); + if (layoutBounds == NULL) { + return nullObjectReturn("layoutBounds == null"); + } + + env->SetIntArrayRegion(layoutBounds, 0, 4, (jint*) peeker.fLayoutBounds); + if (javaBitmap != NULL) { + env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds); + } + } // detach bitmap from its autodeleter, since we want to own it now adb.detach(); @@ -321,7 +334,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, } if (padding) { - if (peeker.fPatchIsValid) { + if (peeker.fPatch != NULL) { GraphicsJNI::set_jrect(env, padding, peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop, peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom); @@ -350,7 +363,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, } // now create the java bitmap return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), - isMutable, ninePatchChunk); + isMutable, ninePatchChunk, layoutBounds, -1); } static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, @@ -576,7 +589,7 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); SkASSERT(bitmap_class); gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I"); - + gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I"); int ret = AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory$Options", gOptionsMethods, diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index 682877a..dd8e84f 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -244,7 +244,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *b JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); jbyteArray buff = allocator->getStorageObjAndReset(); - return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1); + return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, NULL, -1); } static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index a1d41ee..d4c7600 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -345,14 +345,14 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, - bool isMutable, jbyteArray ninepatch, int density) + bool isMutable, jbyteArray ninepatch, jintArray layoutbounds, + int density) { SkASSERT(bitmap); SkASSERT(bitmap->pixelRef()); - jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID, static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)), - buffer, isMutable, ninepatch, density); + buffer, isMutable, ninepatch, layoutbounds, density); hasException(env); // For the side effect of logging. return obj; } @@ -360,7 +360,7 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buff jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, jbyteArray ninepatch, int density) { - return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density); + return createBitmap(env, bitmap, NULL, isMutable, ninepatch, NULL, density); } @@ -587,7 +587,7 @@ int register_android_graphics_Graphics(JNIEnv* env) gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", - "(I[BZ[BI)V"); + "(I[BZ[B[II)V"); gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index cc32f44..c5b06f5 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -53,7 +53,8 @@ public: storage array (may be null). */ static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, - bool isMutable, jbyteArray ninepatch, int density = -1); + bool isMutable, jbyteArray ninepatch, jintArray layoutbounds, + int density = -1); static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, jbyteArray ninepatch, int density = -1); diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp index 365d985..df996af 100644 --- a/core/jni/android/graphics/NinePatchPeeker.cpp +++ b/core/jni/android/graphics/NinePatchPeeker.cpp @@ -31,14 +31,11 @@ bool NinePatchPeeker::peek(const char tag[], const void* data, size_t length) { // this relies on deserialization being done in place Res_png_9patch::deserialize(patchNew); patchNew->fileToDevice(); - if (fPatchIsValid) { - free(fPatch); - } + free(fPatch); fPatch = patchNew; //printf("9patch: (%d,%d)-(%d,%d)\n", // fPatch.sizeLeft, fPatch.sizeTop, // fPatch.sizeRight, fPatch.sizeBottom); - fPatchIsValid = true; // now update our host to force index or 32bit config // 'cause we don't want 565 predithered, since as a 9patch, we know @@ -52,8 +49,9 @@ bool NinePatchPeeker::peek(const char tag[], const void* data, size_t length) { SkBitmap::kARGB_8888_Config, }; fHost->setPrefConfigTable(gNo565Pref); - } else { - fPatch = NULL; + } else if (strcmp("npLb", tag) == 0 && length == sizeof(int) * 4) { + fLayoutBounds = new int[4]; + memcpy(fLayoutBounds, data, sizeof(int) * 4); } return true; // keep on decoding } diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h index 207536c..10d268a 100644 --- a/core/jni/android/graphics/NinePatchPeeker.h +++ b/core/jni/android/graphics/NinePatchPeeker.h @@ -28,17 +28,17 @@ public: NinePatchPeeker(SkImageDecoder* host) { // the host lives longer than we do, so a raw ptr is safe fHost = host; - fPatchIsValid = false; + fPatch = NULL; + fLayoutBounds = NULL; } ~NinePatchPeeker() { - if (fPatchIsValid) { - free(fPatch); - } + free(fPatch); + delete fLayoutBounds; } - bool fPatchIsValid; Res_png_9patch* fPatch; + int *fLayoutBounds; virtual bool peek(const char tag[], const void* data, size_t length); }; diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 6f939be..ed5b2f6 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -16,9 +16,12 @@ package android.graphics; +import android.os.Debug; import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; +import android.util.Log; + import java.io.OutputStream; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -57,6 +60,7 @@ public final class Bitmap implements Parcelable { private final boolean mIsMutable; private byte[] mNinePatchChunk; // may be null + private int[] mLayoutBounds; // may be null private int mWidth = -1; private int mHeight = -1; private boolean mRecycled; @@ -95,6 +99,19 @@ public final class Bitmap implements Parcelable { */ /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk, int density) { + this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density); + } + + /** + * @noinspection UnusedDeclaration + */ + /* Private constructor that must received an already allocated native + bitmap int (pointer). + + This can be called from JNI code. + */ + /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk, + int[] layoutBounds, int density) { if (nativeBitmap == 0) { throw new RuntimeException("internal error: native bitmap is 0"); } @@ -106,6 +123,7 @@ public final class Bitmap implements Parcelable { mIsMutable = isMutable; mNinePatchChunk = ninePatchChunk; + mLayoutBounds = layoutBounds; if (density >= 0) { mDensity = density; } @@ -164,6 +182,16 @@ public final class Bitmap implements Parcelable { } /** + * Sets the layout bounds as an array of left, top, right, bottom integers + * @param padding the array containing the padding values + * + * @hide + */ + public void setLayoutBounds(int[] bounds) { + mLayoutBounds = bounds; + } + + /** * Free the native object associated with this bitmap, and clear the * reference to the pixel data. This will not free the pixel data synchronously; * it simply allows it to be garbage collected if there are no other references. @@ -690,6 +718,14 @@ public final class Bitmap implements Parcelable { } /** + * @hide + * @return the layout padding [left, right, top, bottom] + */ + public int[] getLayoutBounds() { + return mLayoutBounds; + } + + /** * Specifies the known formats a bitmap can be compressed into */ public enum CompressFormat { diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index c5705f6..1599e40 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -424,6 +424,7 @@ public class BitmapFactory { throw new ArrayIndexOutOfBoundsException(); } Bitmap bm = nativeDecodeByteArray(data, offset, length, opts); + if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); } @@ -554,7 +555,6 @@ public class BitmapFactory { if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { return bm; } - byte[] np = bm.getNinePatchChunk(); final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); if (opts.inScaled || isNinePatch) { diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 043adae..86e824b 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -773,7 +773,13 @@ public abstract class Drawable { np = null; pad = null; } - return drawableFromBitmap(res, bm, np, pad, srcName); + int[] layoutBounds = bm.getLayoutBounds(); + Rect layoutBoundsRect = null; + if (layoutBounds != null) { + layoutBoundsRect = new Rect(layoutBounds[0], layoutBounds[1], + layoutBounds[2], layoutBounds[3]); + } + return drawableFromBitmap(res, bm, np, pad, layoutBoundsRect, srcName); } return null; } @@ -875,7 +881,7 @@ public abstract class Drawable { Bitmap bm = BitmapFactory.decodeFile(pathName); if (bm != null) { - return drawableFromBitmap(null, bm, null, null, pathName); + return drawableFromBitmap(null, bm, null, null, null, pathName); } return null; @@ -956,10 +962,12 @@ public abstract class Drawable { } private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np, - Rect pad, String srcName) { + Rect pad, Rect layoutBounds, String srcName) { if (np != null) { - return new NinePatchDrawable(res, bm, np, pad, srcName); + NinePatchDrawable npd = new NinePatchDrawable(res, bm, np, pad, srcName); + npd.setLayoutBounds(layoutBounds); + return npd; } return new BitmapDrawable(res, bm); diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 18b8bc7..1272071 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -47,6 +47,7 @@ public class NinePatchDrawable extends Drawable { private NinePatchState mNinePatchState; private NinePatch mNinePatch; private Rect mPadding; + private Rect mLayoutBounds; private Paint mPaint; private boolean mMutated; @@ -98,6 +99,13 @@ public class NinePatchDrawable extends Drawable { mNinePatchState.mTargetDensity = mTargetDensity; } + /** + * @hide + */ + void setLayoutBounds(Rect layoutBounds) { + mLayoutBounds = layoutBounds; + } + private void setNinePatchState(NinePatchState state, Resources res) { mNinePatchState = state; mNinePatch = state.mNinePatch; @@ -258,7 +266,7 @@ public class NinePatchDrawable extends Drawable { } options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; - final Rect padding = new Rect(); + final Rect padding = new Rect(); Bitmap bitmap = null; try { diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 2b9b056..9de685a 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -57,6 +57,13 @@ struct image_info bool is9Patch; Res_png_9patch info9Patch; + // Layout padding, if relevant + bool haveLayoutBounds; + int32_t layoutBoundsLeft; + int32_t layoutBoundsTop; + int32_t layoutBoundsRight; + int32_t layoutBoundsBottom; + png_uint_32 allocHeight; png_bytepp allocRows; }; @@ -129,33 +136,62 @@ static void read_png(const char* imageName, &interlace_type, &compression_type, NULL); } -static bool is_tick(png_bytep p, bool transparent, const char** outError) +#define COLOR_TRANSPARENT 0 +#define COLOR_WHITE 0xFFFFFFFF +#define COLOR_TICK 0xFF000000 +#define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF + +enum { + TICK_TYPE_NONE, + TICK_TYPE_TICK, + TICK_TYPE_LAYOUT_BOUNDS, + TICK_TYPE_BOTH +}; + +static int tick_type(png_bytep p, bool transparent, const char** outError) { + png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + if (transparent) { if (p[3] == 0) { - return false; + return TICK_TYPE_NONE; + } + if (color == COLOR_LAYOUT_BOUNDS_TICK) { + return TICK_TYPE_LAYOUT_BOUNDS; } + if (color == COLOR_TICK) { + return TICK_TYPE_TICK; + } + + // Error cases if (p[3] != 0xff) { *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)"; - return false; + return TICK_TYPE_NONE; } if (p[0] != 0 || p[1] != 0 || p[2] != 0) { - *outError = "Ticks in transparent frame must be black"; + *outError = "Ticks in transparent frame must be black or red"; } - return true; + return TICK_TYPE_TICK; } if (p[3] != 0xFF) { *outError = "White frame must be a solid color (no alpha)"; } - if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) { - return false; + if (color == COLOR_WHITE) { + return TICK_TYPE_NONE; + } + if (color == COLOR_TICK) { + return TICK_TYPE_TICK; } + if (color == COLOR_LAYOUT_BOUNDS_TICK) { + return TICK_TYPE_LAYOUT_BOUNDS; + } + if (p[0] != 0 || p[1] != 0 || p[2] != 0) { - *outError = "Ticks in white frame must be black"; - return false; + *outError = "Ticks in white frame must be black or red"; + return TICK_TYPE_NONE; } - return true; + return TICK_TYPE_TICK; } enum { @@ -175,7 +211,7 @@ static status_t get_horizontal_ticks( bool found = false; for (i=1; i<width-1; i++) { - if (is_tick(row+i*4, transparent, outError)) { + if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) { if (state == TICK_START || (state == TICK_OUTSIDE_1 && multipleAllowed)) { *outLeft = i-1; @@ -224,7 +260,7 @@ static status_t get_vertical_ticks( bool found = false; for (i=1; i<height-1; i++) { - if (is_tick(rows[i]+offset, transparent, outError)) { + if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) { if (state == TICK_START || (state == TICK_OUTSIDE_1 && multipleAllowed)) { *outTop = i-1; @@ -262,6 +298,83 @@ static status_t get_vertical_ticks( return NO_ERROR; } +static status_t get_horizontal_layout_bounds_ticks( + png_bytep row, int width, bool transparent, bool required, + int32_t* outLeft, int32_t* outRight, const char** outError) +{ + int i; + *outLeft = *outRight = 0; + + // Look for left tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) { + // Starting with a layout padding tick + i = 1; + while (i < width - 1) { + (*outLeft)++; + i++; + int tick = tick_type(row + i * 4, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + // Look for right tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) { + // Ending with a layout padding tick + i = width - 2; + while (i > 1) { + (*outRight)++; + i--; + int tick = tick_type(row+i*4, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + return NO_ERROR; +} + +static status_t get_vertical_layout_bounds_ticks( + png_bytepp rows, int offset, int height, bool transparent, bool required, + int32_t* outTop, int32_t* outBottom, const char** outError) +{ + int i; + *outTop = *outBottom = 0; + + // Look for top tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) { + // Starting with a layout padding tick + i = 1; + while (i < height - 1) { + (*outTop)++; + i++; + int tick = tick_type(rows[i] + offset, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + // Look for bottom tick + if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) { + // Ending with a layout padding tick + i = height - 2; + while (i > 1) { + (*outBottom)++; + i--; + int tick = tick_type(rows[i] + offset, transparent, outError); + if (tick != TICK_TYPE_LAYOUT_BOUNDS) { + break; + } + } + } + + return NO_ERROR; +} + + static uint32_t get_color( png_bytepp rows, int left, int top, int right, int bottom) { @@ -353,6 +466,9 @@ static status_t do_9patch(const char* imageName, image_info* image) image->info9Patch.paddingLeft = image->info9Patch.paddingRight = image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1; + image->layoutBoundsLeft = image->layoutBoundsRight = + image->layoutBoundsTop = image->layoutBoundsBottom = 0; + png_bytep p = image->rows[0]; bool transparent = p[3] == 0; bool hasColor = false; @@ -408,6 +524,25 @@ static status_t do_9patch(const char* imageName, image_info* image) goto getout; } + // Find left and right of layout padding... + get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false, + &image->layoutBoundsLeft, + &image->layoutBoundsRight, &errorMsg); + + get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false, + &image->layoutBoundsTop, + &image->layoutBoundsBottom, &errorMsg); + + image->haveLayoutBounds = image->layoutBoundsLeft != 0 + || image->layoutBoundsRight != 0 + || image->layoutBoundsTop != 0 + || image->layoutBoundsBottom != 0; + + if (image->haveLayoutBounds) { + NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop, + image->layoutBoundsRight, image->layoutBoundsBottom)); + } + // Copy patch data into image image->info9Patch.numXDivs = numXDivs; image->info9Patch.numYDivs = numYDivs; @@ -845,8 +980,9 @@ static void write_png(const char* imageName, int bit_depth, interlace_type, compression_type; int i; - png_unknown_chunk unknowns[1]; + png_unknown_chunk unknowns[2]; unknowns[0].data = NULL; + unknowns[1].data = NULL; png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep)); if (outRows == (png_bytepp) 0) { @@ -916,23 +1052,42 @@ static void write_png(const char* imageName, } if (imageInfo.is9Patch) { + int chunk_count = 1 + (imageInfo.haveLayoutBounds ? 1 : 0); + int p_index = imageInfo.haveLayoutBounds ? 1 : 0; + int b_index = 0; + png_byte *chunk_names = imageInfo.haveLayoutBounds + ? (png_byte*)"npLb\0npTc\0" + : (png_byte*)"npTc"; NOISY(printf("Adding 9-patch info...\n")); - strcpy((char*)unknowns[0].name, "npTc"); - unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize(); - unknowns[0].size = imageInfo.info9Patch.serializedSize(); + strcpy((char*)unknowns[p_index].name, "npTc"); + unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize(); + unknowns[p_index].size = imageInfo.info9Patch.serializedSize(); // TODO: remove the check below when everything works - checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data); + checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data); + + if (imageInfo.haveLayoutBounds) { + int chunk_size = sizeof(png_uint_32) * 4; + strcpy((char*)unknowns[b_index].name, "npLb"); + unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1); + memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size); + unknowns[b_index].size = chunk_size; + } + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, - (png_byte*)"npTc", 1); - png_set_unknown_chunks(write_ptr, write_info, unknowns, 1); + chunk_names, chunk_count); + png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count); // XXX I can't get this to work without forcibly changing // the location to what I want... which apparently is supposed // to be a private API, but everything else I have tried results // in the location being set to what I -last- wrote so I never // get written. :p png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE); + if (imageInfo.haveLayoutBounds) { + png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE); + } } + png_write_info(write_ptr, write_info); png_bytepp rows; @@ -954,6 +1109,7 @@ static void write_png(const char* imageName, } free(outRows); free(unknowns[0].data); + free(unknowns[1].data); png_get_IHDR(write_ptr, write_info, &width, &height, &bit_depth, &color_type, &interlace_type, diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index eadec02..b76b8cf 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -524,7 +524,8 @@ public final class Bitmap_Delegate { int nativeInt = sManager.addNewDelegate(delegate); // and create/return a new Bitmap with it - return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density); + return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, + density); } /** |