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
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 @@
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 @@
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 @@
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 @@
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_mCancelID;
jfieldID gOptions_bitmapFieldID;
jfieldID gBitmap_nativeBitmapFieldID;
+jfieldID gBitmap_layoutBoundsFieldID;
#if 0
#define TRACE_BITMAP(code) code
@@ -276,7 +277,7 @@
}
jbyteArray ninePatchChunk = NULL;
- if (peeker.fPatchIsValid) {
+ if (peeker.fPatch != NULL) {
if (willScale) {
scaleNinePatchChunk(peeker.fPatch, scale);
}
@@ -296,6 +297,18 @@
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 @@
}
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 @@
}
// 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 @@
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 @@
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 @@
///////////////////////////////////////////////////////////////////////////////////////////
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, 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
*/
/*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 @@
mIsMutable = isMutable;
mNinePatchChunk = ninePatchChunk;
+ mLayoutBounds = layoutBounds;
if (density >= 0) {
mDensity = density;
}
@@ -164,6 +182,16 @@
}
/**
+ * 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 @@
}
/**
+ * @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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
private NinePatchState mNinePatchState;
private NinePatch mNinePatch;
private Rect mPadding;
+ private Rect mLayoutBounds;
private Paint mPaint;
private boolean mMutated;
@@ -98,6 +99,13 @@
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 @@
}
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 @@
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 @@
&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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
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);
}
/**