Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

timob-10080: Android: Parity for Ti.Blob methods #2833

Merged
merged 8 commits into from
Aug 30, 2012
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,25 @@ public void propertyChanged(String key, Object oldValue, Object newValue, KrollP
setImages();
}
} else {
// Update requestedWidth / requestedHeight when width / height is changed.
if (key.equals(TiC.PROPERTY_WIDTH)) {
View parentView = getParentView();
if (TiC.LAYOUT_FILL.equals(TiConvert.toString(newValue)) && parentView != null) {
// Use the parent's width when it's fill
requestedWidth = TiConvert.toTiDimension(parentView.getMeasuredWidth(), TiDimension.TYPE_WIDTH);
} else {
requestedWidth = TiConvert.toTiDimension(newValue, TiDimension.TYPE_WIDTH);
}
} else if (key.equals(TiC.PROPERTY_HEIGHT)) {
View parentView = getParentView();
// Use the parent's height when it's fill
if (TiC.LAYOUT_FILL.equals(TiConvert.toString(newValue)) && parentView != null) {
requestedHeight = TiConvert.toTiDimension(parentView.getMeasuredHeight(), TiDimension.TYPE_HEIGHT);
} else {
requestedHeight = TiConvert.toTiDimension(newValue, TiDimension.TYPE_HEIGHT);
}
}

super.propertyChanged(key, oldValue, newValue, proxy);
}
}
Expand Down
198 changes: 196 additions & 2 deletions android/titanium/src/java/org/appcelerator/titanium/TiBlob.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,29 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.HashMap;

import org.apache.commons.codec.binary.Base64;
import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.kroll.common.Log;
import org.appcelerator.kroll.util.KrollStreamHelper;
import org.appcelerator.titanium.io.TiBaseFile;
import org.appcelerator.titanium.io.TitaniumBlob;
import org.appcelerator.titanium.util.TiImageHelper;
import org.appcelerator.titanium.util.TiMimeTypeHelper;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.RectF;
import android.media.ThumbnailUtils;

/**
* A Titanium Blob object. A Blob can represent any opaque data or input stream.
Expand Down Expand Up @@ -60,6 +71,7 @@ public class TiBlob extends KrollProxy
private int type;
private Object data;
private String mimetype;
private Bitmap image;
private int width, height;

private TiBlob(int type, Object data, String mimetype)
Expand All @@ -68,6 +80,7 @@ private TiBlob(int type, Object data, String mimetype)
this.type = type;
this.data = data;
this.mimetype = mimetype;
this.image = null;
this.width = 0;
this.height = 0;
}
Expand Down Expand Up @@ -107,7 +120,9 @@ public static TiBlob blobFromFile(TiBaseFile file, String mimeType)
if (mimeType == null) {
mimeType = TiMimeTypeHelper.getMimeType(file.nativePath());
}
return new TiBlob(TYPE_FILE, file, mimeType);
TiBlob blob = new TiBlob(TYPE_FILE, file, mimeType);
blob.loadBitmapInfo();
return blob;
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we can't save off the image width and height whenever we change it in order to avoid calculating the property values each time we try and access with the width or height via kroll?

Expand All @@ -125,6 +140,7 @@ public static TiBlob blobFromImage(Bitmap image)
}

TiBlob blob = new TiBlob(TYPE_IMAGE, data, "image/bitmap");
blob.image = image;
blob.width = image.getWidth();
blob.height = image.getHeight();
return blob;
Expand Down Expand Up @@ -154,7 +170,62 @@ public static TiBlob blobFromData(byte[] data, String mimetype)
if (mimetype == null || mimetype.length() == 0) {
return new TiBlob(TYPE_DATA, data, "application/octet-stream");
}
return new TiBlob(TYPE_DATA, data, mimetype);
TiBlob blob = new TiBlob(TYPE_DATA, data, mimetype);
blob.loadBitmapInfo();
return blob;
}

/**
* Determines the MIME-type by reading first few characters from the given input stream.
* @return the guessed MIME-type or null if the type could not be determined.
*/
public String guessContentTypeFromStream()
{
String mt = null;
InputStream is = getInputStream();
if (is != null) {
try {
mt = URLConnection.guessContentTypeFromStream(is);
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e, Log.DEBUG_MODE);
}
}
return mt;
}

/**
* Update width and height if the file / data can be decoded into a bitmap successfully.
*/
public void loadBitmapInfo()
{
String mt = guessContentTypeFromStream();
// Update mimetype based on the guessed MIME-type.
if (mt != null && mt != mimetype) {
mimetype = mt;
}

// If the MIME-type is "image/*" or undetermined, try to decode the file / data into a bitmap.
if (mt == null || mt.startsWith("image/")) {
// Query the dimensions of a bitmap without allocating the memory for its pixels
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;

switch (type) {
case TYPE_FILE:
BitmapFactory.decodeStream(getInputStream(), null, opts);
break;
case TYPE_DATA:
byte[] byteArray = (byte[]) data;
BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, opts);
break;
}

// Update width and height after the file / data is decoded successfully
if (opts.outWidth != -1 && opts.outHeight != -1) {
width = opts.outWidth;
height = opts.outHeight;
}
}
}

/**
Expand Down Expand Up @@ -402,4 +473,127 @@ public String toBase64()
{
return new String(Base64.encodeBase64(getBytes()));
}

public Bitmap getImage()
{
// If the image is not available but the width and height of the image are successfully fetched, the image can
// be created by decoding the data.
if (image == null && (width > 0 && height > 0)) {
switch (type) {
case TYPE_FILE:
return BitmapFactory.decodeStream(getInputStream());
case TYPE_DATA:
byte[] byteArray = (byte[]) data;
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
}
}
return image;
}

@Kroll.method
public TiBlob imageAsCropped(Object params)
{
Bitmap img = getImage();
if (img == null) {
return null;
}
if (!(params instanceof HashMap)) {
Log.e(TAG, "Argument for imageAsCropped must be a dictionary");
return null;
}

KrollDict options = new KrollDict((HashMap) params);
int widthCropped = options.optInt(TiC.PROPERTY_WIDTH, width);
int heightCropped = options.optInt(TiC.PROPERTY_HEIGHT, height);
int x = options.optInt(TiC.PROPERTY_X, (width - widthCropped) / 2);
int y = options.optInt(TiC.PROPERTY_Y, (height - heightCropped) / 2);
Bitmap imageCropped = Bitmap.createBitmap(img, x, y, widthCropped, heightCropped);
return blobFromImage(imageCropped);
}

@Kroll.method
public TiBlob imageAsResized(Number width, Number height)
{
Bitmap img = getImage();
if (img == null) {
return null;
}

int dstWidth = width.intValue();
int dstHeight = height.intValue();
Bitmap imageResized = Bitmap.createScaledBitmap(img, dstWidth, dstHeight, true);
return blobFromImage(imageResized);
}

@Kroll.method
public TiBlob imageAsThumbnail(Number size, @Kroll.argument(optional = true) Number borderSize,
@Kroll.argument(optional = true) Number cornerRadius)
{
Bitmap img = getImage();
if (img == null) {
return null;
}

int thumbnailSize = size.intValue();
Bitmap imageThumbnail = ThumbnailUtils.extractThumbnail(img, thumbnailSize, thumbnailSize);

float border = 1f;
if (borderSize != null) {
border = borderSize.floatValue();
}
float radius = 0f;
if (cornerRadius != null) {
radius = cornerRadius.floatValue();
}

if (border == 0 && radius == 0) {
return blobFromImage(imageThumbnail);
}

Bitmap imageThumbnailBorder = TiImageHelper.imageWithRoundedCorner(imageThumbnail, radius, border);
return blobFromImage(imageThumbnailBorder);
}

@Kroll.method
public TiBlob imageWithAlpha()
{
Bitmap img = getImage();
if (img == null) {
return null;
}

Bitmap imageWithAlpha = TiImageHelper.imageWithAlpha(img);
return blobFromImage(imageWithAlpha);
}

@Kroll.method
public TiBlob imageWithRoundedCorner(Number cornerRadius, @Kroll.argument(optional = true) Number borderSize)
{
Bitmap img = getImage();
if (img == null) {
return null;
}

float radius = cornerRadius.floatValue();
float border = 1f;
if (borderSize != null) {
border = borderSize.floatValue();
}

Bitmap imageRoundedCorner = TiImageHelper.imageWithRoundedCorner(img, radius, border);
return blobFromImage(imageRoundedCorner);
}

@Kroll.method
public TiBlob imageWithTransparentBorder(Number size)
{
Bitmap img = getImage();
if (img == null) {
return null;
}

int borderSize = size.intValue();
Bitmap imageWithBorder = TiImageHelper.imageWithTransparentBorder(img, borderSize);
return blobFromImage(imageWithBorder);
}
}