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
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
@ -163,6 +181,16 @@ public final class Bitmap implements Parcelable {
|
||||
mNinePatchChunk = chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@ -689,6 +717,14 @@ public final class Bitmap implements Parcelable {
|
||||
return mNinePatchChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @return the layout padding [left, right, top, bottom]
|
||||
*/
|
||||
public int[] getLayoutBounds() {
|
||||
return mLayoutBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the known formats a bitmap can be compressed into
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user