Merge "Merge WebKit at r73109: update web_user_agent string."
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 3dd1ecd..d8d63206 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -361,101 +361,103 @@
if (fitbottom > okbottom)
okbottom = fitbottom;
}
- } else if (breakOnlyAtSpaces) {
- if (ok != here) {
- // Log.e("text", "output ok " + here + " to " +ok);
-
- while (ok < spanEnd && chs[ok - paraStart] == ' ') {
- ok++;
- }
-
- v = out(source,
- here, ok,
- okascent, okdescent, oktop, okbottom,
- v,
- spacingmult, spacingadd, chooseht,
- choosehtv, fm, hasTabOrEmoji,
- needMultiply, paraStart, chdirs, dir, easy,
- ok == bufend, includepad, trackpad,
- chs, widths, here - paraStart,
- where, ellipsizedWidth, okwidth,
- paint);
-
- here = ok;
- } else {
- // Act like it fit even though it didn't.
-
- fitwidth = w;
- fit = j + 1;
-
- if (fmtop < fittop)
- fittop = fmtop;
- if (fmascent < fitascent)
- fitascent = fmascent;
- if (fmdescent > fitdescent)
- fitdescent = fmdescent;
- if (fmbottom > fitbottom)
- fitbottom = fmbottom;
- }
} else {
- if (ok != here) {
- // Log.e("text", "output ok " + here + " to " +ok);
+ if (breakOnlyAtSpaces) {
+ if (ok != here) {
+ // Log.e("text", "output ok " + here + " to " +ok);
- while (ok < spanEnd && chs[ok - paraStart] == ' ') {
- ok++;
+ while (ok < spanEnd && chs[ok - paraStart] == ' ') {
+ ok++;
+ }
+
+ v = out(source,
+ here, ok,
+ okascent, okdescent, oktop, okbottom,
+ v,
+ spacingmult, spacingadd, chooseht,
+ choosehtv, fm, hasTabOrEmoji,
+ needMultiply, paraStart, chdirs, dir, easy,
+ ok == bufend, includepad, trackpad,
+ chs, widths, here - paraStart,
+ where, ellipsizedWidth, okwidth,
+ paint);
+
+ here = ok;
+ } else {
+ // Act like it fit even though it didn't.
+
+ fitwidth = w;
+ fit = j + 1;
+
+ if (fmtop < fittop)
+ fittop = fmtop;
+ if (fmascent < fitascent)
+ fitascent = fmascent;
+ if (fmdescent > fitdescent)
+ fitdescent = fmdescent;
+ if (fmbottom > fitbottom)
+ fitbottom = fmbottom;
}
-
- v = out(source,
- here, ok,
- okascent, okdescent, oktop, okbottom,
- v,
- spacingmult, spacingadd, chooseht,
- choosehtv, fm, hasTabOrEmoji,
- needMultiply, paraStart, chdirs, dir, easy,
- ok == bufend, includepad, trackpad,
- chs, widths, here - paraStart,
- where, ellipsizedWidth, okwidth,
- paint);
-
- here = ok;
- } else if (fit != here) {
- // Log.e("text", "output fit " + here + " to " +fit);
- v = out(source,
- here, fit,
- fitascent, fitdescent,
- fittop, fitbottom,
- v,
- spacingmult, spacingadd, chooseht,
- choosehtv, fm, hasTabOrEmoji,
- needMultiply, paraStart, chdirs, dir, easy,
- fit == bufend, includepad, trackpad,
- chs, widths, here - paraStart,
- where, ellipsizedWidth, fitwidth,
- paint);
-
- here = fit;
} else {
- // Log.e("text", "output one " + here + " to " +(here + 1));
- // XXX not sure why the existing fm wasn't ok.
- // measureText(paint, mWorkPaint,
- // source, here, here + 1, fm, tab,
- // null);
+ if (ok != here) {
+ // Log.e("text", "output ok " + here + " to " +ok);
- v = out(source,
- here, here+1,
- fm.ascent, fm.descent,
- fm.top, fm.bottom,
- v,
- spacingmult, spacingadd, chooseht,
- choosehtv, fm, hasTabOrEmoji,
- needMultiply, paraStart, chdirs, dir, easy,
- here + 1 == bufend, includepad,
- trackpad,
- chs, widths, here - paraStart,
- where, ellipsizedWidth,
- widths[here - paraStart], paint);
+ while (ok < spanEnd && chs[ok - paraStart] == ' ') {
+ ok++;
+ }
- here = here + 1;
+ v = out(source,
+ here, ok,
+ okascent, okdescent, oktop, okbottom,
+ v,
+ spacingmult, spacingadd, chooseht,
+ choosehtv, fm, hasTabOrEmoji,
+ needMultiply, paraStart, chdirs, dir, easy,
+ ok == bufend, includepad, trackpad,
+ chs, widths, here - paraStart,
+ where, ellipsizedWidth, okwidth,
+ paint);
+
+ here = ok;
+ } else if (fit != here) {
+ // Log.e("text", "output fit " + here + " to " +fit);
+ v = out(source,
+ here, fit,
+ fitascent, fitdescent,
+ fittop, fitbottom,
+ v,
+ spacingmult, spacingadd, chooseht,
+ choosehtv, fm, hasTabOrEmoji,
+ needMultiply, paraStart, chdirs, dir, easy,
+ fit == bufend, includepad, trackpad,
+ chs, widths, here - paraStart,
+ where, ellipsizedWidth, fitwidth,
+ paint);
+
+ here = fit;
+ } else {
+ // Log.e("text", "output one " + here + " to " +(here + 1));
+ // XXX not sure why the existing fm wasn't ok.
+ // measureText(paint, mWorkPaint,
+ // source, here, here + 1, fm, tab,
+ // null);
+
+ v = out(source,
+ here, here+1,
+ fm.ascent, fm.descent,
+ fm.top, fm.bottom,
+ v,
+ spacingmult, spacingadd, chooseht,
+ choosehtv, fm, hasTabOrEmoji,
+ needMultiply, paraStart, chdirs, dir, easy,
+ here + 1 == bufend, includepad,
+ trackpad,
+ chs, widths, here - paraStart,
+ where, ellipsizedWidth,
+ widths[here - paraStart], paint);
+
+ here = here + 1;
+ }
}
if (here < spanStart) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a4ad97b..33ea66c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4674,8 +4674,8 @@
}
/**
- * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
- * KeyEvent.Callback.onKeyMultiple()}: perform press of the view
+ * Default implementation of {@link KeyEvent.Callback#onKeyDown(int, KeyEvent)
+ * KeyEvent.Callback.onKeyDown()}: perform press of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER}
* is released, if the view is enabled and clickable.
*
@@ -4718,8 +4718,8 @@
}
/**
- * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
- * KeyEvent.Callback.onKeyMultiple()}: perform clicking of the view
+ * Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)
+ * KeyEvent.Callback.onKeyUp()}: perform clicking of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or
* {@link KeyEvent#KEYCODE_ENTER} is released.
*
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7b3cc6c..58fadb9 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -31,6 +31,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.Build;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
@@ -350,6 +351,10 @@
mGroupFlags |= FLAG_ANIMATION_CACHE;
mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
+ mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
+ }
+
setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
mChildren = new View[ARRAY_INITIAL_CAPACITY];
@@ -1568,7 +1573,8 @@
/**
* Enable or disable the splitting of MotionEvents to multiple children during touch event
- * dispatch. This behavior is disabled by default.
+ * dispatch. This behavior is enabled by default for applications that target an
+ * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
*
* <p>When this option is enabled MotionEvents may be split and dispatched to different child
* views depending on where each pointer initially went down. This allows for user interactions
@@ -1591,6 +1597,7 @@
}
/**
+ * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
* @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
*/
public boolean isMotionEventSplittingEnabled() {
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index ca3515c5..704d4da 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -39,7 +39,7 @@
if (initialized)
return;
- sContext = context;
+ sContext = context.getApplicationContext();
initialized = true;
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 8e4852a..685b7ba 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1005,7 +1005,7 @@
private static void setupProxyListener(Context context) {
IntentFilter filter = new IntentFilter();
filter.addAction(Proxy.PROXY_CHANGE_ACTION);
- Intent currentProxy = context.registerReceiver(new ProxyReceiver(), filter);
+ Intent currentProxy = context.getApplicationContext().registerReceiver(new ProxyReceiver(), filter);
if (currentProxy != null) {
handleProxyBroadcast(currentProxy);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d31acec..ab98763 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1475,8 +1475,16 @@
ss.position = getSelectedItemPosition();
ss.firstId = INVALID_POSITION;
} else {
- if (haveChildren) {
- // Remember the position of the first child
+ if (haveChildren && mFirstPosition > 0) {
+ // Remember the position of the first child.
+ // We only do this if we are not currently at the top of
+ // the list, for two reasons:
+ // (1) The list may be in the process of becoming empty, in
+ // which case mItemCount may not be 0, but if we try to
+ // ask for any information about position 0 we will crash.
+ // (2) Being "at the top" seems like a special case, anyway,
+ // and the user wouldn't expect to end up somewhere else when
+ // they revisit the list even if its content has changed.
View v = getChildAt(0);
ss.viewTop = v.getTop();
int firstPos = mFirstPosition;
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
index e0a3ee6..e22b018 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -18,14 +18,17 @@
import android.content.BroadcastReceiver;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.Context;
import android.app.Instrumentation;
import android.os.Handler;
import android.os.Message;
+import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.Status;
+import android.net.wifi.SupplicantState;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.AndroidTestCase;
@@ -45,10 +48,62 @@
//10s delay for turning on wifi
private static final int DELAY = 10000;
+ private WifiStateListener mWifiStateListener;
+ int mWifiState;
+ int mDisableBroadcastCounter = 0;
+ int mEnableBroadcastCounter = 0;
+ NetworkInfo mNetworkInfo;
+ boolean mSupplicantConnection;
+ SupplicantState mSupplicantState;
+
+ private class WifiStateListener extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN);
+ switch (mWifiState) {
+ case WifiManager.WIFI_STATE_DISABLING:
+ if (mDisableBroadcastCounter == 0) mDisableBroadcastCounter++;
+ break;
+ case WifiManager.WIFI_STATE_DISABLED:
+ if (mDisableBroadcastCounter == 1) mDisableBroadcastCounter++;
+ break;
+ case WifiManager.WIFI_STATE_ENABLING:
+ if (mEnableBroadcastCounter == 0) mEnableBroadcastCounter++;
+ break;
+ case WifiManager.WIFI_STATE_ENABLED:
+ if (mEnableBroadcastCounter == 1) mEnableBroadcastCounter++;
+ break;
+ }
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo)
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+ } else if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
+ mSupplicantState = (SupplicantState)
+ intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
+ } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
+ mSupplicantConnection =
+ intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
+ }
+ }
+ }
@Override
protected void setUp() throws Exception {
super.setUp();
+
+ // register a connectivity receiver for CONNECTIVITY_ACTION;
+
+ mWifiStateListener = new WifiStateListener();
+ IntentFilter mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ getContext().registerReceiver(mWifiStateListener, mIntentFilter);
+
mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
mWifiManager.setWifiEnabled(true);
assertNotNull(mWifiManager);
@@ -183,4 +238,37 @@
assertTrue(ret);
mWifiManager.saveConfiguration();
}
+
+ // Test case 5: test wifi state change broadcasts
+ @LargeTest
+ public void testWifiBroadcasts() {
+
+ /* Initialize */
+ mWifiManager.setWifiEnabled(false);
+ sleepAfterWifiEnable();
+ mDisableBroadcastCounter = 0;
+ mEnableBroadcastCounter = 0;
+ mSupplicantConnection = false;
+ mNetworkInfo = null;
+ mSupplicantState = null;
+
+ /* Enable wifi */
+ mWifiManager.setWifiEnabled(true);
+ sleepAfterWifiEnable();
+ assertTrue(mEnableBroadcastCounter == 2);
+ assertTrue(mSupplicantConnection == true);
+ assertTrue(mNetworkInfo.isConnected());
+ assertTrue(mSupplicantState == SupplicantState.COMPLETED);
+
+
+ /* Disable wifi */
+ mWifiManager.setWifiEnabled(false);
+ sleepAfterWifiEnable();
+ assertTrue(mDisableBroadcastCounter == 2);
+ assertTrue(mSupplicantConnection == false);
+ assertTrue(!mNetworkInfo.isConnected());
+ assertTrue(mSupplicantState != SupplicantState.COMPLETED);
+
+ }
+
}
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 5197eeb..64eb7d2 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -37,13 +37,16 @@
ScriptC::ScriptC(Context *rsc) : Script(rsc) {
+ LOGD(">>>> ScriptC ctor called, obj=%x", this);
mBccScript = NULL;
memset(&mProgram, 0, sizeof(mProgram));
}
ScriptC::~ScriptC() {
+ LOGD(">>>> ~ScriptC()");
if (mBccScript) {
bccDeleteScript(mBccScript);
+ LOGD(">>>> ~ScriptC(mBCCScript)");
}
free(mEnviroment.mScriptText);
mEnviroment.mScriptText = NULL;
@@ -436,7 +439,7 @@
s->mEnviroment.mScriptTextLength,
modWhen,
crc32,
- NULL,
+ resName,
cacheDir);
bccCompileBC(s->mBccScript);
}
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
index 2989be0..3fdfdbb 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
@@ -31,7 +31,6 @@
android:layout_alignParentTop="true"
android:layout_marginLeft="105dip"
android:scaleType="center"
- android:background="@drawable/recents_thumbnail_bg"
/>
<ImageView android:id="@+id/app_icon"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index 3c665fe..bd9bdb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -23,8 +23,6 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.ActivityManager;
-import android.app.IThumbnailReceiver;
-import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -33,20 +31,20 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -55,7 +53,7 @@
import com.android.systemui.R;
public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnClickListener {
- private static final int COLLAPSE_DURATION = 360;
+ private static final int GLOW_PADDING = 15;
private static final String TAG = "RecentAppsPanel";
private static final boolean DEBUG = TabletStatusBar.DEBUG;
private static final int DISPLAY_TASKS_PORTRAIT = 7; // Limited by max binder transaction size
@@ -70,6 +68,7 @@
private int mIconDpi;
private AnimatorSet mAnimationSet;
private View mBackgroundProtector;
+ private Bitmap mGlowBitmap;
static class ActivityDescription {
int id;
@@ -121,6 +120,7 @@
& Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
mIconDpi = xlarge ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
+ mGlowBitmap = BitmapFactory.decodeResource(res, R.drawable.recents_thumbnail_bg);
}
@Override
@@ -225,8 +225,8 @@
if (title != null && title.length() > 0 && icon != null) {
if (DEBUG) Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title);
ActivityDescription item = new ActivityDescription(
- crop(recentInfo.thumbnail), icon, title, recentInfo.description,
- intent, id, index, info.packageName);
+ recentInfo.thumbnail, icon, title,
+ recentInfo.description, intent, id, index, info.packageName);
activityDescriptions.add(item);
++index;
} else {
@@ -255,21 +255,22 @@
updateUiElements(getResources().getConfiguration(), true);
}
- private Bitmap crop(Bitmap bitmap) {
- if (bitmap == null || bitmap.getWidth() >= bitmap.getHeight()) {
- return bitmap;
- }
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
- Bitmap outBitmap = Bitmap.createBitmap(height, width, bitmap.getConfig());
+ private Bitmap compositeBitmap(Bitmap background, Bitmap thumbnail) {
+ Bitmap outBitmap = background.copy(background.getConfig(), true);
Canvas canvas = new Canvas(outBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
- canvas.drawBitmap(bitmap,
- new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight() - height * width / height),
- new Rect(0, 0, outBitmap.getWidth(), outBitmap.getHeight()),
- paint);
+ paint.setAlpha(255);
+ final int srcWidth = thumbnail.getWidth();
+ final int height = thumbnail.getHeight();
+ final int srcHeight = srcWidth > height ? height : (height - height * srcWidth / height);
+ canvas.drawBitmap(thumbnail,
+ new Rect(0, 0, srcWidth-1, srcHeight-1),
+ new RectF(GLOW_PADDING,
+ GLOW_PADDING - 4.0f,
+ outBitmap.getWidth() - GLOW_PADDING + 2.0f,
+ outBitmap.getHeight() - GLOW_PADDING + 3.0f), paint);
return outBitmap;
}
@@ -291,7 +292,7 @@
TextView appLabel = (TextView) view.findViewById(R.id.app_label);
TextView appDesc = (TextView) view.findViewById(R.id.app_description);
final Bitmap thumb = activityDescription.thumbnail;
- appThumbnail.setImageBitmap(crop(thumb));
+ appThumbnail.setImageBitmap(compositeBitmap(mGlowBitmap, thumb));
appIcon.setImageDrawable(activityDescription.icon);
appLabel.setText(activityDescription.label);
appDesc.setText(activityDescription.description);
diff --git a/tests/HwAccelerationTest/res/layout/_layers.xml b/tests/HwAccelerationTest/res/layout/_layers.xml
new file mode 100644
index 0000000..c2b186d
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/_layers.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.test.hwui.LayersActivity.LayersView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+</com.android.test.hwui.LayersActivity.LayersView>
diff --git a/tests/HwAccelerationTest/res/layout/_newlayers.xml b/tests/HwAccelerationTest/res/layout/_newlayers.xml
new file mode 100644
index 0000000..062a2e1
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/_newlayers.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.test.hwui.NewLayersActivity.LayersView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+</com.android.test.hwui.NewLayersActivity.LayersView>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
index b705117..9d5cd28 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
@@ -33,11 +33,11 @@
setContentView(new LayersView(this));
}
- static class LayersView extends View {
+ public static class LayersView extends View {
private Paint mLayerPaint;
private final Paint mRectPaint;
- LayersView(Context c) {
+ public LayersView(Context c) {
super(c);
mLayerPaint = new Paint();
@@ -47,11 +47,11 @@
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
+
canvas.translate(140.0f, 100.0f);
//canvas.drawRGB(255, 255, 255);
-
+
int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
Canvas.ALL_SAVE_FLAG);
@@ -59,9 +59,9 @@
canvas.drawRect(0.0f, 0.0f, 200.0f, 100.0f, mRectPaint);
canvas.restoreToCount(count);
-
+
canvas.translate(0.0f, 125.0f);
-
+
count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
Canvas.ALL_SAVE_FLAG);
@@ -75,8 +75,8 @@
mRectPaint.setColor(0xff0000ff);
mRectPaint.setAlpha(255);
- canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);
-
+ canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);
+
mLayerPaint.setAlpha(127);
mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
count = canvas.saveLayer(50.0f, 25.0f, 150.0f, 75.0f, mLayerPaint,
@@ -86,13 +86,13 @@
canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint);
canvas.restoreToCount(count);
-
+
canvas.translate(0.0f, 125.0f);
mRectPaint.setColor(0xff0000ff);
mRectPaint.setAlpha(255);
- canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);
-
+ canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);
+
mLayerPaint.setColor(0xffff0000);
mLayerPaint.setAlpha(127);
mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
@@ -103,7 +103,7 @@
canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint);
canvas.restoreToCount(count);
-
+
mLayerPaint = new Paint();
}
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
index d9a2893..2509d367 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
@@ -31,11 +31,11 @@
setContentView(new LayersView(this));
}
- static class LayersView extends View {
+ public static class LayersView extends View {
private Paint mLayerPaint;
private final Paint mRectPaint;
- LayersView(Context c) {
+ public LayersView(Context c) {
super(c);
mLayerPaint = new Paint();
@@ -57,7 +57,7 @@
canvas.translate(0.0f, 200.0f);
drawStuff(canvas, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
-
+
canvas.restore();
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index b5f5005..2ce6a36 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -17,6 +17,7 @@
package android.graphics;
import com.android.ide.common.rendering.api.ResourceDensity;
+import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import android.graphics.Bitmap.Config;
@@ -29,6 +30,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.Buffer;
+import java.util.Arrays;
import javax.imageio.ImageIO;
@@ -57,6 +59,7 @@
private final Config mConfig;
private BufferedImage mImage;
private boolean mHasAlpha = true;
+ private int mGenerationId = 0;
// ---- Public Helper methods ----
@@ -173,6 +176,22 @@
return mConfig;
}
+ /**
+ * Returns the hasAlpha rendering hint
+ * @return true if the bitmap alpha should be used at render time
+ */
+ public boolean hasAlpha() {
+ return mHasAlpha && mConfig != Config.RGB_565;
+ }
+
+ /**
+ * Update the generationId.
+ *
+ * @see Bitmap#getGenerationId()
+ */
+ public void change() {
+ mGenerationId++;
+ }
// ---- native methods ----
@@ -183,7 +202,9 @@
// create the image
BufferedImage image = new BufferedImage(width, height, imageType);
- // FIXME fill the bitmap!
+ if (colors != null) {
+ image.setRGB(0, 0, width, height, colors, offset, stride);
+ }
// create a delegate with the content of the stream.
Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.sConfigs[nativeConfig]);
@@ -192,8 +213,31 @@
}
/*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+ Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
+ if (srcBmpDelegate == null) {
+ assert false;
+ return null;
+ }
+
+ BufferedImage srcImage = srcBmpDelegate.getImage();
+
+ int width = srcImage.getWidth();
+ int height = srcImage.getHeight();
+
+ int imageType = getBufferedImageType(nativeConfig);
+
+ // create the image
+ BufferedImage image = new BufferedImage(width, height, imageType);
+
+ // copy the source image into the image.
+ int[] argb = new int[width * height];
+ srcImage.getRGB(0, 0, width, height, argb, 0, width);
+ image.setRGB(0, 0, width, height, argb, 0, width);
+
+ // create a delegate with the content of the stream.
+ Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.sConfigs[nativeConfig]);
+
+ return createBitmap(delegate, isMutable, Bitmap.getDefaultDensity());
}
/*package*/ static void nativeDestructor(int nativeBitmap) {
@@ -206,8 +250,8 @@
/*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
OutputStream stream, byte[] tempStorage) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+ Bridge.getLog().error(null, "Bitmap.compress() is not supported");
+ return true;
}
/*package*/ static void nativeErase(int nativeBitmap, int color) {
@@ -222,7 +266,7 @@
Graphics2D g = image.createGraphics();
try {
- g.setColor(new java.awt.Color(color, delegate.mHasAlpha));
+ g.setColor(new java.awt.Color(color, true));
g.fillRect(0, 0, image.getWidth(), image.getHeight());
} finally {
@@ -298,20 +342,35 @@
/*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset,
int stride, int x, int y, int width, int height) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeGetPixels");
+ Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+ if (delegate == null) {
+ assert false;
+ return;
+ }
+
+ delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
}
/*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSetPixel");
+ Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+ if (delegate == null) {
+ assert false;
+ return;
+ }
+
+ delegate.getImage().setRGB(x, y, color);
}
/*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset,
int stride, int x, int y, int width, int height) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSetPixels");
+ Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+ if (delegate == null) {
+ assert false;
+ return;
+ }
+
+ delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
}
/*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) {
@@ -325,31 +384,68 @@
}
/*package*/ static int nativeGenerationId(int nativeBitmap) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeGenerationId");
+ Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+ if (delegate == null) {
+ assert false;
+ return 0;
+ }
+
+ return delegate.mGenerationId;
}
/*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCreateFromParcel");
+ // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
+ // used during aidl call so really this should not be called.
+ Bridge.getLog().error(null,
+ "AIDL is not suppored, and therefore bitmap cannot be created from parcels");
+ return null;
}
/*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable,
int density, Parcel p) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeWriteToParcel");
+ // This is only called when sending a bitmap through aidl, so really this should not
+ // be called.
+ Bridge.getLog().error(null,
+ "AIDL is not suppored, and therefore bitmap cannot be written to parcels");
+ return false;
}
/*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint,
int[] offsetXY) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeExtractAlpha");
+ Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
+ if (bitmap == null) {
+ assert false;
+ return null;
+ }
+
+ Paint_Delegate paint = null;
+ if (nativePaint > 0) {
+ paint = Paint_Delegate.getDelegate(nativePaint);
+ if (paint == null) {
+ assert false;
+ return null;
+ }
+ }
+
+ if (paint != null && paint.getMaskFilter() != 0) {
+ Bridge.getLog().fidelityWarning(null,
+ "MaskFilter not supported in Bitmap.extractAlpha",
+ null);
+ }
+
+ int alpha = paint != null ? paint.getAlpha() : 0xFF;
+ BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
+
+ // create the delegate. The actual Bitmap config is only an alpha channel
+ Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
+
+ // the density doesn't matter, it's set by the Java method.
+ return createBitmap(delegate, false /*isMutable*/,
+ ResourceDensity.DEFAULT_DENSITY /*density*/);
}
-
/*package*/ static void nativePrepareToDraw(int nativeBitmap) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativePrepareToDraw");
+ // nothing to be done here.
}
/*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) {
@@ -364,8 +460,48 @@
}
/*package*/ static boolean nativeSameAs(int nb0, int nb1) {
- // FIXME implement native delegate
- throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSameAs");
+ Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
+ if (delegate1 == null) {
+ assert false;
+ return false;
+ }
+
+ Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
+ if (delegate2 == null) {
+ assert false;
+ return false;
+ }
+
+ BufferedImage image1 = delegate1.getImage();
+ BufferedImage image2 = delegate2.getImage();
+ if (delegate1.mConfig != delegate2.mConfig ||
+ image1.getWidth() != image2.getWidth() ||
+ image1.getHeight() != image2.getHeight()) {
+ return false;
+ }
+
+ // get the internal data
+ int w = image1.getWidth();
+ int h = image2.getHeight();
+ int[] argb1 = new int[w*h];
+ int[] argb2 = new int[w*h];
+
+ image1.getRGB(0, 0, w, h, argb1, 0, w);
+ image2.getRGB(0, 0, w, h, argb2, 0, w);
+
+ // compares
+ if (delegate1.mConfig == Config.ALPHA_8) {
+ // in this case we have to manually compare the alpha channel as the rest is garbage.
+ final int length = w*h;
+ for (int i = 0 ; i < length ; i++) {
+ if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return Arrays.equals(argb1, argb2);
}
// ---- Private delegate/helper methods ----
@@ -382,4 +518,37 @@
// and create/return a new Bitmap with it
return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density);
}
+
+ /**
+ * Creates and returns a copy of a given BufferedImage.
+ * <p/>
+ * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
+ *
+ * @param image the image to copy
+ * @param imageType the type of the new image
+ * @param alpha an optional alpha modifier
+ * @return a new BufferedImage
+ */
+ /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
+ int w = image.getWidth();
+ int h = image.getHeight();
+
+ BufferedImage result = new BufferedImage(w, h, imageType);
+
+ int[] argb = new int[w * h];
+ image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+
+ if (alpha != 255) {
+ final int length = argb.length;
+ for (int i = 0 ; i < length; i++) {
+ int a = (argb[i] >>> 24 * alpha) / 255;
+ argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
+ }
+ }
+
+ result.setRGB(0, 0, w, h, argb, 0, w);
+
+ return result;
+ }
+
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 2720b61..88ec88eb 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -23,14 +23,12 @@
import android.graphics.Paint_Delegate.FontInfo;
import android.text.TextUtils;
-import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
-import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.List;
@@ -57,13 +55,11 @@
// ---- delegate helper data ----
- private interface Drawable {
- void draw(Graphics2D graphics, Paint_Delegate paint);
- }
+ private final static boolean[] sBoolOut = new boolean[1];
// ---- delegate data ----
- private BufferedImage mBufferedImage;
- private GcSnapshot mSnapshot = new GcSnapshot();
+ private Bitmap_Delegate mBitmap;
+ private GcSnapshot mSnapshot;
// ---- Public Helper methods ----
@@ -84,7 +80,7 @@
/**
* Returns the current {@link Graphics2D} used to draw.
*/
- public GcSnapshot getGcSnapshot() {
+ public GcSnapshot getSnapshot() {
return mSnapshot;
}
@@ -103,7 +99,7 @@
return 0;
}
- return canvasDelegate.mBufferedImage.getWidth();
+ return canvasDelegate.mBitmap.getImage().getWidth();
}
/*package*/ static int getHeight(Canvas thisCanvas) {
@@ -114,7 +110,7 @@
return 0;
}
- return canvasDelegate.mBufferedImage.getHeight();
+ return canvasDelegate.mBitmap.getImage().getHeight();
}
/*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
@@ -125,7 +121,7 @@
return;
}
- canvasDelegate.getGcSnapshot().translate(dx, dy);
+ canvasDelegate.getSnapshot().translate(dx, dy);
}
/*package*/ static void rotate(Canvas thisCanvas, float degrees) {
@@ -136,7 +132,7 @@
return;
}
- canvasDelegate.getGcSnapshot().rotate(Math.toRadians(degrees));
+ canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
}
/*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
@@ -147,7 +143,7 @@
return;
}
- canvasDelegate.getGcSnapshot().scale(sx, sy);
+ canvasDelegate.getSnapshot().scale(sx, sy);
}
/*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
@@ -159,7 +155,7 @@
}
// get the current top graphics2D object.
- GcSnapshot g = canvasDelegate.getGcSnapshot();
+ GcSnapshot g = canvasDelegate.getSnapshot();
// get its current matrix
AffineTransform currentTx = g.getTransform();
@@ -235,7 +231,7 @@
return 0;
}
- return canvasDelegate.getGcSnapshot().size();
+ return canvasDelegate.getSnapshot().size();
}
/*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
@@ -260,33 +256,18 @@
throw new UnsupportedOperationException();
}
- /*package*/ static void drawLines(Canvas thisCanvas, float[] pts, int offset, int count,
+ /*package*/ static void drawLines(Canvas thisCanvas,
+ final float[] pts, final int offset, final int count,
Paint paint) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
- if (canvasDelegate == null) {
- assert false;
- return;
- }
-
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint.mNativePaint);
- if (paintDelegate == null) {
- assert false;
- return;
- }
-
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
-
- try {
- for (int i = 0 ; i < count ; i += 4) {
- g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
- (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
- }
- } finally {
- // dispose Graphics2D object
- g.dispose();
- }
+ draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
+ false /*forceSrcMode*/, new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ for (int i = 0 ; i < count ; i += 4) {
+ graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
+ (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
+ }
+ }
+ });
}
/*package*/ static void freeCaches() {
@@ -300,7 +281,7 @@
Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
// create a new Canvas_Delegate with the given bitmap and return its new native int.
- Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate.getImage());
+ Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
return sManager.addDelegate(newDelegate);
} else {
@@ -326,34 +307,71 @@
return;
}
- canvasDelegate.setBitmap(bitmapDelegate.getImage());
+ canvasDelegate.setBitmap(bitmapDelegate);
}
/*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
int paint, int layerFlags) {
- // FIXME
- throw new UnsupportedOperationException();
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ assert false;
+ return 0;
+ }
+
+ Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+ if (paintDelegate == null) {
+ assert false;
+ return 0;
+ }
+
+ return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
}
/*package*/ static int native_saveLayer(int nativeCanvas, float l,
float t, float r, float b,
int paint, int layerFlags) {
- // FIXME
- throw new UnsupportedOperationException();
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ assert false;
+ return 0;
+ }
+
+ Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+ if (paintDelegate == null) {
+ assert false;
+ return 0;
+ }
+
+ return canvasDelegate.saveLayer(new RectF(l, t, r, b),
+ paintDelegate, layerFlags);
}
/*package*/ static int native_saveLayerAlpha(int nativeCanvas,
RectF bounds, int alpha,
int layerFlags) {
- // FIXME
- throw new UnsupportedOperationException();
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ assert false;
+ return 0;
+ }
+
+ return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
}
/*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
float t, float r, float b,
int alpha, int layerFlags) {
- // FIXME
- throw new UnsupportedOperationException();
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ assert false;
+ return 0;
+ }
+
+ return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
}
@@ -372,7 +390,7 @@
}
// get the current top graphics2D object.
- GcSnapshot snapshot = canvasDelegate.getGcSnapshot();
+ GcSnapshot snapshot = canvasDelegate.getSnapshot();
// get its current matrix
AffineTransform currentTx = snapshot.getTransform();
@@ -399,7 +417,7 @@
}
// get the current top graphics2D object.
- GcSnapshot snapshot = canvasDelegate.getGcSnapshot();
+ GcSnapshot snapshot = canvasDelegate.getSnapshot();
// get the AffineTransform of the given matrix
AffineTransform matrixTx = matrixDelegate.getAffineTransform();
@@ -458,7 +476,7 @@
return false;
}
- Rectangle rect = canvasDelegate.getGcSnapshot().getClip().getBounds();
+ Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
if (rect != null) {
bounds.left = rect.x;
bounds.top = rect.y;
@@ -511,7 +529,7 @@
native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
}
- /*package*/ static void native_drawColor(int nativeCanvas, int color, int mode) {
+ /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
@@ -519,27 +537,26 @@
return;
}
- // get a new graphics context.
- Graphics2D graphics = (Graphics2D)canvasDelegate.getGcSnapshot().create();
- try {
- // reset its transform just in case
- graphics.setTransform(new AffineTransform());
+ final int w = canvasDelegate.mBitmap.getImage().getWidth();
+ final int h = canvasDelegate.mBitmap.getImage().getHeight();
+ draw(nativeCanvas, new GcSnapshot.Drawable() {
- // set the color
- graphics.setColor(new Color(color, true /*alpha*/));
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ // reset its transform just in case
+ graphics.setTransform(new AffineTransform());
- Composite composite = PorterDuffXfermode_Delegate.getComposite(
- PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
- if (composite != null) {
- graphics.setComposite(composite);
+ // set the color
+ graphics.setColor(new Color(color, true /*alpha*/));
+
+ Composite composite = PorterDuffXfermode_Delegate.getComposite(
+ PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
+ if (composite != null) {
+ graphics.setComposite(composite);
+ }
+
+ graphics.fillRect(0, 0, w, h);
}
-
- graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(),
- canvasDelegate.mBufferedImage.getHeight());
- } finally {
- // dispose Graphics2D object
- graphics.dispose();
- }
+ });
}
/*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
@@ -547,32 +564,16 @@
throw new UnsupportedOperationException();
}
- /*package*/ static void native_drawLine(int nativeCanvas, float startX,
- float startY, float stopX,
- float stopY, int paint) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- assert false;
- return;
- }
+ /*package*/ static void native_drawLine(int nativeCanvas,
+ final float startX, final float startY, final float stopX, final float stopY,
+ int paint) {
- // get the delegate from the native int.
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
- assert false;
- return;
- }
-
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
-
- try {
- g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
- } finally {
- // dispose Graphics2D object
- g.dispose();
- }
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
+ }
+ });
}
/*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
@@ -580,82 +581,51 @@
native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
}
- /*package*/ static void native_drawRect(int nativeCanvas, float left,
- float top, float right,
- float bottom, int paint) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- assert false;
- return;
- }
+ /*package*/ static void native_drawRect(int nativeCanvas,
+ final float left, final float top, final float right, final float bottom, int paint) {
- // get the delegate from the native int.
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
- assert false;
- return;
- }
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ int style = paint.getStyle();
- if (right > left && bottom > top) {
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
+ // draw
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fillRect((int)left, (int)top,
+ (int)(right-left), (int)(bottom-top));
+ }
- try {
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
- }
- } finally {
- // dispose Graphics2D object
- g.dispose();
- }
- }
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.drawRect((int)left, (int)top,
+ (int)(right-left), (int)(bottom-top));
+ }
+ }
+ });
}
- /*package*/ static void native_drawOval(int nativeCanvas, RectF oval,
- int paint) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- assert false;
- return;
- }
-
- // get the delegate from the native int.
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
- assert false;
- return;
- }
-
+ /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) {
if (oval.right > oval.left && oval.bottom > oval.top) {
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ int style = paint.getStyle();
- int style = paintDelegate.getStyle();
+ // draw
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fillOval((int)oval.left, (int)oval.top,
+ (int)oval.width(), (int)oval.height());
+ }
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
- }
-
- // dispose Graphics2D object
- g.dispose();
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.drawOval((int)oval.left, (int)oval.top,
+ (int)oval.width(), (int)oval.height());
+ }
+ }
+ });
}
}
@@ -675,46 +645,31 @@
}
/*package*/ static void native_drawRoundRect(int nativeCanvas,
- RectF rect, float rx,
- float ry, int paint) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- assert false;
- return;
- }
+ final RectF rect, final float rx, final float ry, int paint) {
- // get the delegate from the native int.
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
- assert false;
- return;
- }
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ int style = paint.getStyle();
- if (rect.right > rect.left && rect.bottom > rect.top) {
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
+ // draw
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fillRoundRect(
+ (int)rect.left, (int)rect.top,
+ (int)rect.width(), (int)rect.height(),
+ (int)rx, (int)ry);
+ }
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- g.fillRoundRect(
- (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
- (int)rx, (int)ry);
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- g.drawRoundRect(
- (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
- (int)rx, (int)ry);
- }
-
- // dispose Graphics2D object
- g.dispose();
- }
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.drawRoundRect(
+ (int)rect.left, (int)rect.top,
+ (int)rect.width(), (int)rect.height(),
+ (int)rx, (int)ry);
+ }
+ }
+ });
}
/*package*/ static void native_drawPath(int nativeCanvas, int path,
@@ -725,21 +680,22 @@
return;
}
- draw(nativeCanvas, paint, new Drawable() {
- public void draw(Graphics2D graphics, Paint_Delegate paint) {
- Shape shape = pathDelegate.getJavaShape();
- int style = paint.getStyle();
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ Shape shape = pathDelegate.getJavaShape();
+ int style = paint.getStyle();
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fill(shape);
- }
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fill(shape);
+ }
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.draw(shape);
- }
- }
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.draw(shape);
+ }
+ }
});
}
@@ -760,7 +716,7 @@
float right = left + image.getWidth();
float bottom = top + image.getHeight();
- drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
0, 0, image.getWidth(), image.getHeight(),
(int)left, (int)top, (int)right, (int)bottom);
}
@@ -780,11 +736,11 @@
BufferedImage image = bitmapDelegate.getImage();
if (src == null) {
- drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
0, 0, image.getWidth(), image.getHeight(),
(int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
} else {
- drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
src.left, src.top, src.width(), src.height(),
(int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
}
@@ -805,29 +761,87 @@
BufferedImage image = bitmapDelegate.getImage();
if (src == null) {
- drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
0, 0, image.getWidth(), image.getHeight(),
dst.left, dst.top, dst.right, dst.bottom);
} else {
- drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
src.left, src.top, src.width(), src.height(),
dst.left, dst.top, dst.right, dst.bottom);
}
}
/*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
- int offset, int stride, float x,
- float y, int width, int height,
+ int offset, int stride, final float x,
+ final float y, int width, int height,
boolean hasAlpha,
int nativePaintOrZero) {
- // FIXME
- throw new UnsupportedOperationException();
+
+ // create a temp BufferedImage containing the content.
+ final BufferedImage image = new BufferedImage(width, height,
+ hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
+ image.setRGB(0, 0, width, height, colors, offset, stride);
+
+ draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ if (paint != null && paint.isFilterBitmap()) {
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ }
+
+ graphics.drawImage(image, (int) x, (int) y, null);
+ }
+ });
}
/*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
int nMatrix, int nPaint) {
- // FIXME
- throw new UnsupportedOperationException();
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ if (canvasDelegate == null) {
+ assert false;
+ return;
+ }
+
+ // get the delegate from the native int.
+ Paint_Delegate paintDelegate = null;
+ if (nPaint > 0) {
+ paintDelegate = Paint_Delegate.getDelegate(nPaint);
+ if (paintDelegate == null) {
+ assert false;
+ return;
+ }
+ }
+
+ // get the delegate from the native int.
+ Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap);
+ if (bitmapDelegate == null) {
+ assert false;
+ return;
+ }
+
+ final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
+
+ Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+ if (matrixDelegate == null) {
+ assert false;
+ return;
+ }
+
+ final AffineTransform mtx = matrixDelegate.getAffineTransform();
+
+ canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ if (paint != null && paint.isFilterBitmap()) {
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ }
+
+ //FIXME add support for canvas, screen and bitmap densities.
+ graphics.drawImage(image, mtx, null);
+ }
+ }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
}
/*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
@@ -846,116 +860,103 @@
throw new UnsupportedOperationException();
}
- /*package*/ static void native_drawText(int nativeCanvas, char[] text,
- int index, int count, float x,
- float y, int flags, int paint) {
- // WARNING: the logic in this method is similar to Paint.measureText.
- // Any change to this method should be reflected in Paint.measureText
-
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- assert false;
- return;
- }
-
- // get the delegate from the native int.
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
- assert false;
- return;
- }
-
- Graphics2D g = (Graphics2D) canvasDelegate.createCustomGraphics(paintDelegate);
- try {
- // Paint.TextAlign indicates how the text is positioned relative to X.
- // LEFT is the default and there's nothing to do.
- if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
- float m = paintDelegate.measureText(text, index, count);
- if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
- x -= m / 2;
- } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
- x -= m;
- }
- }
-
- List<FontInfo> fonts = paintDelegate.getFonts();
-
- if (fonts.size() > 0) {
- FontInfo mainFont = fonts.get(0);
- int i = index;
- int lastIndex = index + count;
- while (i < lastIndex) {
- // always start with the main font.
- int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
- if (upTo == -1) {
- // draw all the rest and exit.
- g.setFont(mainFont.mFont);
- g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
- return;
- } else if (upTo > 0) {
- // draw what's possible
- g.setFont(mainFont.mFont);
- g.drawChars(text, i, upTo - i, (int)x, (int)y);
-
- // compute the width that was drawn to increase x
- x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-
- // move index to the first non displayed char.
- i = upTo;
-
- // don't call continue at this point. Since it is certain the main font
- // cannot display the font a index upTo (now ==i), we move on to the
- // fallback fonts directly.
+ /*package*/ static void native_drawText(int nativeCanvas,
+ final char[] text, final int index, final int count,
+ final float startX, final float startY, int flags, int paint) {
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ // WARNING: the logic in this method is similar to Paint.measureText.
+ // Any change to this method should be reflected in Paint.measureText
+ // Paint.TextAlign indicates how the text is positioned relative to X.
+ // LEFT is the default and there's nothing to do.
+ float x = startX;
+ float y = startY;
+ if (paint.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+ float m = paint.measureText(text, index, count);
+ if (paint.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+ x -= m / 2;
+ } else if (paint.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+ x -= m;
}
+ }
- // no char supported, attempt to read the next char(s) with the
- // fallback font. In this case we only test the first character
- // and then go back to test with the main font.
- // Special test for 2-char characters.
- boolean foundFont = false;
- for (int f = 1 ; f < fonts.size() ; f++) {
- FontInfo fontInfo = fonts.get(f);
+ List<FontInfo> fonts = paint.getFonts();
- // need to check that the font can display the character. We test
- // differently if the char is a high surrogate.
- int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
- upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+ if (fonts.size() > 0) {
+ FontInfo mainFont = fonts.get(0);
+ int i = index;
+ int lastIndex = index + count;
+ while (i < lastIndex) {
+ // always start with the main font.
+ int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
if (upTo == -1) {
- // draw that char
- g.setFont(fontInfo.mFont);
- g.drawChars(text, i, charCount, (int)x, (int)y);
+ // draw all the rest and exit.
+ graphics.setFont(mainFont.mFont);
+ graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y);
+ return;
+ } else if (upTo > 0) {
+ // draw what's possible
+ graphics.setFont(mainFont.mFont);
+ graphics.drawChars(text, i, upTo - i, (int)x, (int)y);
- // update x
- x += fontInfo.mMetrics.charsWidth(text, i, charCount);
+ // compute the width that was drawn to increase x
+ x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
- // update the index in the text, and move on
+ // move index to the first non displayed char.
+ i = upTo;
+
+ // don't call continue at this point. Since it is certain the main font
+ // cannot display the font a index upTo (now ==i), we move on to the
+ // fallback fonts directly.
+ }
+
+ // no char supported, attempt to read the next char(s) with the
+ // fallback font. In this case we only test the first character
+ // and then go back to test with the main font.
+ // Special test for 2-char characters.
+ boolean foundFont = false;
+ for (int f = 1 ; f < fonts.size() ; f++) {
+ FontInfo fontInfo = fonts.get(f);
+
+ // need to check that the font can display the character. We test
+ // differently if the char is a high surrogate.
+ int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+ upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+ if (upTo == -1) {
+ // draw that char
+ graphics.setFont(fontInfo.mFont);
+ graphics.drawChars(text, i, charCount, (int)x, (int)y);
+
+ // update x
+ x += fontInfo.mMetrics.charsWidth(text, i, charCount);
+
+ // update the index in the text, and move on
+ i += charCount;
+ foundFont = true;
+ break;
+
+ }
+ }
+
+ // in case no font can display the char, display it with the main font.
+ // (it'll put a square probably)
+ if (foundFont == false) {
+ int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+
+ graphics.setFont(mainFont.mFont);
+ graphics.drawChars(text, i, charCount, (int)x, (int)y);
+
+ // measure it to advance x
+ x += mainFont.mMetrics.charsWidth(text, i, charCount);
+
+ // and move to the next chars.
i += charCount;
- foundFont = true;
- break;
-
}
}
-
- // in case no font can display the char, display it with the main font.
- // (it'll put a square probably)
- if (foundFont == false) {
- int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-
- g.setFont(mainFont.mFont);
- g.drawChars(text, i, charCount, (int)x, (int)y);
-
- // measure it to advance x
- x += mainFont.mMetrics.charsWidth(text, i, charCount);
-
- // and move to the next chars.
- i += charCount;
- }
}
}
- } finally {
- g.dispose();
- }
+ });
}
/*package*/ static void native_drawText(int nativeCanvas, String text,
@@ -1041,9 +1042,12 @@
// ---- Private delegate/helper methods ----
/**
- * Executes a {@link Drawable} with a given canvas and paint.
+ * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
+ * <p>Note that the drawable may actually be executed several times if there are
+ * layers involved (see {@link #saveLayer(RectF, int, int)}.
*/
- private static void draw(int nCanvas, int nPaint, Drawable drawable) {
+ private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode,
+ GcSnapshot.Drawable drawable) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
if (canvasDelegate == null) {
@@ -1051,28 +1055,42 @@
return;
}
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
- if (paintDelegate == null) {
+ // paint could be 0 meaning no paint
+ Paint_Delegate paintDelegate = null;
+ if (nPaint > 0) {
+ paintDelegate = Paint_Delegate.getDelegate(nPaint);
+ if (paintDelegate == null) {
+ assert false;
+ return;
+ }
+ }
+
+ canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
+ }
+
+ /**
+ * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
+ * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
+ * <p>Note that the drawable may actually be executed several times if there are
+ * layers involved (see {@link #saveLayer(RectF, int, int)}.
+ */
+ private static void draw(int nCanvas, GcSnapshot.Drawable drawable) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ if (canvasDelegate == null) {
assert false;
return;
}
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
-
- try {
- drawable.draw(g, paintDelegate);
- } finally {
- // dispose Graphics2D object
- g.dispose();
- }
+ canvasDelegate.mSnapshot.draw(drawable);
}
- private Canvas_Delegate(BufferedImage image) {
- setBitmap(image);
+ private Canvas_Delegate(Bitmap_Delegate bitmap) {
+ mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
}
private Canvas_Delegate() {
+ mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
}
/**
@@ -1086,8 +1104,23 @@
// get the current save count
int count = mSnapshot.size();
- // create a new snapshot and add it to the stack
- mSnapshot = new GcSnapshot(mSnapshot, saveFlags);
+ mSnapshot = mSnapshot.save(saveFlags);
+
+ // return the old save count
+ return count;
+ }
+
+ private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
+ Paint_Delegate paint = new Paint_Delegate();
+ paint.setAlpha(alpha);
+ return saveLayer(rect, paint, saveFlags);
+ }
+
+ private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
+ // get the current save count
+ int count = mSnapshot.size();
+
+ mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
// return the old save count
return count;
@@ -1113,129 +1146,18 @@
return mSnapshot.clipRect(left, top, right, bottom, regionOp);
}
- private void setBitmap(BufferedImage image) {
- mBufferedImage = image;
+ private void setBitmap(Bitmap_Delegate bitmap) {
+ mBitmap = bitmap;
assert mSnapshot.size() == 1;
- mSnapshot.setGraphics2D(mBufferedImage.createGraphics());
- }
-
- /**
- * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
- * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
- */
- /*package*/ Graphics2D createCustomGraphics(Paint_Delegate paint) {
- // make new one
- Graphics2D g = getGcSnapshot().create();
-
- // configure it
-
- if (paint.isAntiAliased()) {
- g.setRenderingHint(
- RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g.setRenderingHint(
- RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
- }
-
- // get the shader first, as it'll replace the color if it can be used it.
- boolean customShader = false;
- int nativeShader = paint.getShader();
- if (nativeShader > 0) {
- Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(nativeShader);
- assert shaderDelegate != null;
- if (shaderDelegate != null) {
- if (shaderDelegate.isSupported()) {
- java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
- assert shaderPaint != null;
- if (shaderPaint != null) {
- g.setPaint(shaderPaint);
- customShader = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(null,
- shaderDelegate.getSupportMessage(),
- null);
- }
- }
- }
-
- // if no shader, use the paint color
- if (customShader == false) {
- g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
- }
-
- boolean customStroke = false;
- int pathEffect = paint.getPathEffect();
- if (pathEffect > 0) {
- PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect);
- assert effectDelegate != null;
- if (effectDelegate != null) {
- if (effectDelegate.isSupported()) {
- Stroke stroke = effectDelegate.getStroke(paint);
- assert stroke != null;
- if (stroke != null) {
- g.setStroke(stroke);
- customStroke = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(null,
- effectDelegate.getSupportMessage(),
- null);
- }
- }
- }
-
- // if no custom stroke as been set, set the default one.
- if (customStroke == false) {
- g.setStroke(new BasicStroke(
- paint.getStrokeWidth(),
- paint.getJavaCap(),
- paint.getJavaJoin(),
- paint.getJavaStrokeMiter()));
- }
-
- // the alpha for the composite. Always opaque if the normal paint color is used since
- // it contains the alpha
- int alpha = customShader ? paint.getAlpha() : 0xFF;
-
- boolean customXfermode = false;
- int xfermode = paint.getXfermode();
- if (xfermode > 0) {
- Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
- assert xfermodeDelegate != null;
- if (xfermodeDelegate != null) {
- if (xfermodeDelegate.isSupported()) {
- Composite composite = xfermodeDelegate.getComposite(alpha);
- assert composite != null;
- if (composite != null) {
- g.setComposite(composite);
- customXfermode = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(null,
- xfermodeDelegate.getSupportMessage(),
- null);
- }
- }
- }
-
- // if there was no custom xfermode, but we have alpha (due to a shader and a non
- // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
- // that will handle the alpha.
- if (customXfermode == false && alpha != 0xFF) {
- g.setComposite(PorterDuffXfermode_Delegate.getComposite(
- PorterDuff.Mode.SRC_OVER, alpha));
- }
-
- return g;
+ mSnapshot.setBitmap(mBitmap);
}
private static void drawBitmap(
int nativeCanvas,
- BufferedImage image,
- Bitmap.Config mBitmapConfig,
+ Bitmap_Delegate bitmap,
int nativePaintOrZero,
- int sleft, int stop, int sright, int sbottom,
- int dleft, int dtop, int dright, int dbottom) {
+ final int sleft, final int stop, final int sright, final int sbottom,
+ final int dleft, final int dtop, final int dright, final int dbottom) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
@@ -1253,47 +1175,85 @@
}
}
- drawBitmap(canvasDelegate, image, mBitmapConfig, paintDelegate,
- sleft, stop, sright, sbottom,
- dleft, dtop, dright, dbottom);
+ final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
+
+ draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
+ new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ if (paint != null && paint.isFilterBitmap()) {
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ }
+
+ //FIXME add support for canvas, screen and bitmap densities.
+ graphics.drawImage(image, dleft, dtop, dright, dbottom,
+ sleft, stop, sright, sbottom, null);
+ }
+ });
}
- private static void drawBitmap(
- Canvas_Delegate canvasDelegate,
- BufferedImage image,
- Bitmap.Config mBitmapConfig,
- Paint_Delegate paintDelegate,
- int sleft, int stop, int sright, int sbottom,
- int dleft, int dtop, int dright, int dbottom) {
- //FIXME add support for canvas, screen and bitmap densities.
- Graphics2D g = canvasDelegate.getGcSnapshot().create();
- try {
- if (paintDelegate != null && paintDelegate.isFilterBitmap()) {
- g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- }
+ /**
+ * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
+ * The image returns, through a 1-size boolean array, whether the drawing code should
+ * use a SRC composite no matter what the paint says.
+ *
+ * @param bitmap the bitmap
+ * @param paint the paint that will be used to draw
+ * @param forceSrcMode whether the composite will have to be SRC
+ * @return the image to draw
+ */
+ private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
+ boolean[] forceSrcMode) {
+ BufferedImage image = bitmap.getImage();
+ forceSrcMode[0] = false;
- // if the bitmap config is alpha_8, then we erase all color value from it
- // before drawing it.
- if (mBitmapConfig == Bitmap.Config.ALPHA_8) {
- int w = image.getWidth();
- int h = image.getHeight();
- int[] argb = new int[w*h];
- image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+ // if the bitmap config is alpha_8, then we erase all color value from it
+ // before drawing it.
+ if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
+ fixAlpha8Bitmap(image);
+ } else if (bitmap.hasAlpha() == false) {
+ // hasAlpha is merely a rendering hint. There can in fact be alpha values
+ // in the bitmap but it should be ignored at drawing time.
+ // There is two ways to do this:
+ // - override the composite to be SRC. This can only be used if the composite
+ // was going to be SRC or SRC_OVER in the first place
+ // - Create a different bitmap to draw in which all the alpha channel values is set
+ // to 0xFF.
+ if (paint != null) {
+ int xfermode = paint.getXfermode();
+ if (xfermode > 0) {
+ Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(xfermode);
+ if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
+ PorterDuff.Mode mode =
+ ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
- final int length = argb.length;
- for (int i = 0 ; i < length; i++) {
- argb[i] &= 0xFF000000;
+ forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
+ mode == PorterDuff.Mode.SRC;
+ }
}
- image.setRGB(0, 0, w, h, argb, 0, w);
}
- g.drawImage(image, dleft, dtop, dright, dbottom,
- sleft, stop, sright, sbottom, null);
- } finally {
- g.dispose();
+ // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
+ if (forceSrcMode[0] == false) {
+ image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
+ }
}
+
+ return image;
+ }
+
+ private static void fixAlpha8Bitmap(final BufferedImage image) {
+ int w = image.getWidth();
+ int h = image.getHeight();
+ int[] argb = new int[w * h];
+ image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+
+ final int length = argb.length;
+ for (int i = 0 ; i < length; i++) {
+ argb[i] &= 0xFF000000;
+ }
+ image.setRGB(0, 0, w, h, argb, 0, w);
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
index 954c658..a6844d4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
@@ -55,7 +55,7 @@
@Override
public String getSupportMessage() {
- return "Composte Path Effects are not supported in Layout Preview mode.";
+ return "Compose Path Effects are not supported in Layout Preview mode.";
}
// ---- native methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
index 2853222..b4baa6f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -55,7 +55,7 @@
@Override
public String getSupportMessage() {
- return "Compose Shader are not supported in Layout Preview mode.";
+ return "Compose Shaders are not supported in Layout Preview mode.";
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index f6cee5e..463f4e9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -106,6 +106,45 @@
return true;
}
+ public static Matrix_Delegate make(AffineTransform matrix) {
+ float[] values = new float[MATRIX_SIZE];
+ values[0] = (float) matrix.getScaleX();
+ values[1] = (float) matrix.getShearX();
+ values[2] = (float) matrix.getTranslateX();
+ values[3] = (float) matrix.getShearY();
+ values[4] = (float) matrix.getScaleY();
+ values[5] = (float) matrix.getTranslateY();
+ values[6] = 0.f;
+ values[7] = 0.f;
+ values[8] = 1.f;
+
+ return new Matrix_Delegate(values);
+ }
+
+ public boolean mapRect(RectF dst, RectF src) {
+ // array with 4 corners
+ float[] corners = new float[] {
+ src.left, src.top,
+ src.right, src.top,
+ src.right, src.bottom,
+ src.left, src.bottom,
+ };
+
+ // apply the transform to them.
+ mapPoints(corners);
+
+ // now put the result in the rect. We take the min/max of Xs and min/max of Ys
+ dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
+ dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
+
+ dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
+ dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
+
+
+ return (computeTypeMask() & kRectStaysRect_Mask) != 0;
+ }
+
+
/**
* Returns an {@link AffineTransform} matching the matrix.
*/
@@ -655,26 +694,7 @@
return false;
}
- // array with 4 corners
- float[] corners = new float[] {
- src.left, src.top,
- src.right, src.top,
- src.right, src.bottom,
- src.left, src.bottom,
- };
-
- // apply the transform to them.
- d.mapPoints(corners);
-
- // now put the result in the rect. We take the min/max of Xs and min/max of Ys
- dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
- dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
- dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
- dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-
-
- return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+ return d.mapRect(dst, src);
}
/*package*/ static float native_mapRadius(int native_object, float radius) {
@@ -740,7 +760,6 @@
matrix[4], matrix[2], matrix[5]);
}
-
/**
* Reset a matrix to the identity
*/
@@ -830,6 +849,10 @@
reset();
}
+ private Matrix_Delegate(float[] values) {
+ System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
+ }
+
/**
* Adds the given transformation to the current Matrix
* <p/>This in effect does this = this*matrix
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index b8f76a6..25e0795 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -18,6 +18,7 @@
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
import com.android.ninepatch.NinePatchChunk;
import android.graphics.drawable.NinePatchDrawable;
@@ -123,11 +124,11 @@
}
private static void draw(int canvas_instance,
- int left, int top, int right, int bottom,
+ final int left, final int top, final int right, final int bottom,
int bitmap_instance, byte[] c, int paint_instance_or_null,
- int destDensity, int srcDensity) {
+ final int destDensity, final int srcDensity) {
// get the delegate from the native int.
- Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
+ final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
if (bitmap_delegate == null) {
assert false;
return;
@@ -143,7 +144,7 @@
return;
}
- NinePatchChunk chunkObject = getChunk(c);
+ final NinePatchChunk chunkObject = getChunk(c);
assert chunkObject != null;
if (chunkObject == null) {
return;
@@ -158,19 +159,13 @@
// this one can be null
Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
- Graphics2D graphics;
- if (paint_delegate != null) {
- graphics = canvas_delegate.createCustomGraphics(paint_delegate);
- } else {
- graphics = canvas_delegate.getGcSnapshot().create();
- }
+ canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ chunkObject.draw(bitmap_delegate.getImage(), graphics,
+ left, top, right - left, bottom - top, destDensity, srcDensity);
+ }
+ }, paint_delegate, true /*compositeOnly*/, false /*forceSrcMode*/);
- try {
- chunkObject.draw(bitmap_delegate.getImage(), graphics,
- left, top, right - left, bottom - top, destDensity, srcDensity);
- } finally {
- graphics.dispose();
- }
}
/*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 0a597ca..7cb30dd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -118,6 +118,10 @@
return mColor >>> 24;
}
+ public void setAlpha(int alpha) {
+ mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
+ }
+
public int getTextAlign() {
return mTextAlign;
}
@@ -130,7 +134,11 @@
* returns the value of stroke miter needed by the java api.
*/
public float getJavaStrokeMiter() {
- return mStrokeMiter * mStrokeWidth;
+ float miter = mStrokeMiter * mStrokeWidth;
+ if (miter < 1.f) {
+ miter = 1.f;
+ }
+ return miter;
}
public int getJavaCap() {
@@ -274,7 +282,7 @@
return;
}
- delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF);
+ delegate.setAlpha(a);
}
/*package*/ static float getStrokeWidth(Paint thisPaint) {
@@ -835,7 +843,7 @@
// ---- Private delegate/helper methods ----
- private Paint_Delegate() {
+ /*package*/ Paint_Delegate() {
reset();
}
@@ -866,14 +874,14 @@
private void reset() {
mFlags = Paint.DEFAULT_PAINT_FLAGS;
- mColor = 0;
+ mColor = 0xFF000000;
mStyle = Paint.Style.FILL.nativeInt;
mCap = Paint.Cap.BUTT.nativeInt;
mJoin = Paint.Join.MITER.nativeInt;
mTextAlign = 0;
mTypeface = Typeface.sDefaults[0].native_instance;
mStrokeWidth = 1.f;
- mStrokeMiter = 2.f;
+ mStrokeMiter = 4.f;
mTextSize = 20.f;
mTextScaleX = 1.f;
mTextSkewX = 0.f;
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index 097dfce..b7d5dc9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -45,6 +45,10 @@
// ---- Public Helper methods ----
+ public PorterDuff.Mode getMode() {
+ return getPorterDuffMode(mMode);
+ }
+
@Override
public Composite getComposite(int alpha) {
return getComposite(getPorterDuffMode(mMode), alpha);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index a09524c..6ebc56c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -107,7 +107,8 @@
Bridge.prepareThread();
mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT);
if (mLastResult.isSuccess()) {
- mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index, listener);
+ mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index,
+ listener);
}
} finally {
mSession.release();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 8c6b1be..27268fc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -16,20 +16,41 @@
package com.android.layoutlib.bridge.impl;
-import android.graphics.Canvas;
-import android.graphics.Region;
+import com.android.layoutlib.bridge.Bridge;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint_Delegate;
+import android.graphics.PathEffect_Delegate;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.Shader_Delegate;
+import android.graphics.Xfermode_Delegate;
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Composite;
import java.awt.Graphics2D;
+import java.awt.RenderingHints;
import java.awt.Shape;
+import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
/**
* Class representing a graphics context snapshot, as well as a context stack as a linked list.
* <p>
* This is based on top of {@link Graphics2D} but can operate independently if none are available
* yet when setting transforms and clip information.
+ * <p>
+ * This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
+ * {@link #draw(Drawable, Paint_Delegate)}
*
*/
public class GcSnapshot {
@@ -37,38 +58,241 @@
private final GcSnapshot mPrevious;
private final int mFlags;
- private Graphics2D mGraphics2D = null;
+ /** list of layers. */
+ private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
+
/** temp transform in case transformation are set before a Graphics2D exists */
private AffineTransform mTransform = null;
/** temp clip in case clipping is set before a Graphics2D exists */
private Area mClip = null;
+ // local layer data
+ private final Layer mLocalLayer;
+ private final Paint_Delegate mLocalLayerPaint;
+ private Rect mLocalLayerRegion;
+
+ public interface Drawable {
+ void draw(Graphics2D graphics, Paint_Delegate paint);
+ }
+
/**
- * Creates a new {@link GcSnapshot} on top of another one.
- * <p/>
- * This is basically the equivalent of {@link Canvas#save(int)}
- * @param previous the previous snapshot head.
- * @param flags the flags regarding what should be saved.
+ * class containing information about a layer.
*/
- public GcSnapshot(GcSnapshot previous, int flags) {
- assert previous != null;
- mPrevious = previous;
- mFlags = flags;
- mGraphics2D = (Graphics2D) previous.mGraphics2D.create();
+ private static class Layer {
+ private final Graphics2D mGraphics;
+ private final Bitmap_Delegate mBitmap;
+ private final BufferedImage mImage;
+ private BufferedImage mOriginalCopy;
+
+ /**
+ * Creates a layer with a graphics and a bitmap.
+ *
+ * @param graphics the graphics
+ * @param bitmap the bitmap
+ */
+ Layer(Graphics2D graphics, Bitmap_Delegate bitmap) {
+ mGraphics = graphics;
+ mBitmap = bitmap;
+ mImage = mBitmap.getImage();
+ }
+
+ /**
+ * Creates a layer with a graphics and an image. If the image belongs to a
+ * {@link Bitmap_Delegate}, then {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should
+ * be used.
+ *
+ * @param graphics the graphics
+ * @param image the image
+ */
+ Layer(Graphics2D graphics, BufferedImage image) {
+ mGraphics = graphics;
+ mBitmap = null;
+ mImage = image;
+ }
+
+ /** The Graphics2D, guaranteed to be non null */
+ Graphics2D getGraphics() {
+ return mGraphics;
+ }
+
+ /** The BufferedImage, guaranteed to be non null */
+ BufferedImage getImage() {
+ return mImage;
+ }
+
+ Layer makeCopy() {
+ if (mBitmap != null) {
+ return new Layer((Graphics2D) mGraphics.create(), mBitmap);
+ }
+
+ return new Layer((Graphics2D) mGraphics.create(), mImage);
+ }
+
+ /** sets an optional copy of the original content to be used during restore */
+ void setOriginalCopy(BufferedImage image) {
+ mOriginalCopy = image;
+ }
+
+ BufferedImage getOriginalCopy() {
+ return mOriginalCopy;
+ }
+
+ void change() {
+ if (mBitmap != null) {
+ mBitmap.change();
+ }
+ }
+ }
+
+ /**
+ * Creates the root snapshot associating it with a given bitmap.
+ * <p>
+ * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be
+ * called before the snapshot can be used to draw. Transform and clip operations are permitted
+ * before.
+ *
+ * @param image the image to associate to the snapshot or null.
+ * @return the root snapshot
+ */
+ public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
+ GcSnapshot snapshot = new GcSnapshot();
+ if (bitmap != null) {
+ snapshot.setBitmap(bitmap);
+ }
+
+ return snapshot;
+ }
+
+ /**
+ * Saves the current state according to the given flags and returns the new current snapshot.
+ * <p/>
+ * This is the equivalent of {@link Canvas#save(int)}
+ *
+ * @param flags the save flags.
+ * @return the new snapshot
+ *
+ * @see Canvas#save(int)
+ */
+ public GcSnapshot save(int flags) {
+ return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags);
+ }
+
+ /**
+ * Saves the current state and creates a new layer, and returns the new current snapshot.
+ * <p/>
+ * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)}
+ *
+ * @param layerBounds the layer bounds
+ * @param paint the Paint information used to blit the layer back into the layers underneath
+ * upon restore
+ * @param flags the save flags.
+ * @return the new snapshot
+ *
+ * @see Canvas#saveLayer(RectF, Paint, int)
+ */
+ public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) {
+ return new GcSnapshot(this, layerBounds, paint, flags);
}
/**
* Creates the root snapshot.
* {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
*/
- public GcSnapshot() {
+ private GcSnapshot() {
mPrevious = null;
mFlags = 0;
+ mLocalLayer = null;
+ mLocalLayerPaint = null;
+ }
+
+ /**
+ * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored
+ * into the main graphics when {@link #restore()} is called.
+ *
+ * @param previous the previous snapshot head.
+ * @param layerBounds the region of the layer. Optional, if null, this is a normal save()
+ * @param paint the Paint information used to blit the layer back into the layers underneath
+ * upon restore
+ * @param flags the flags regarding what should be saved.
+ */
+ private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) {
+ assert previous != null;
+ mPrevious = previous;
+ mFlags = flags;
+
+ // make a copy of the current layers before adding the new one.
+ // This keeps the same BufferedImage reference but creates new Graphics2D for this
+ // snapshot.
+ // It does not copy whatever original copy the layers have, as they will be done
+ // only if the new layer doesn't clip drawing to itself.
+ for (Layer layer : mPrevious.mLayers) {
+ mLayers.add(layer.makeCopy());
+ }
+
+ if (layerBounds != null) {
+ // get the current transform
+ AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
+
+ // transform the layerBounds and puts it into a int rect
+ RectF rect2 = new RectF();
+ mapRect(matrix, rect2, layerBounds);
+ mLocalLayerRegion = new Rect();
+ rect2.round(mLocalLayerRegion);
+
+ // get the base layer (always at index 0)
+ Layer baseLayer = mLayers.get(0);
+
+ // create the image for the layer
+ BufferedImage layerImage = new BufferedImage(
+ baseLayer.getImage().getWidth(),
+ baseLayer.getImage().getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+
+ // create a graphics for it so that drawing can be done.
+ Graphics2D layerGraphics = layerImage.createGraphics();
+
+ // because this layer inherits the current context for transform and clip,
+ // set them to one from the base layer.
+ AffineTransform currentMtx = baseLayer.getGraphics().getTransform();
+ layerGraphics.setTransform(currentMtx);
+
+ Shape currentClip = baseLayer.getGraphics().getClip();
+ layerGraphics.setClip(currentClip);
+
+ // create a new layer for this new layer and add it to the list at the end.
+ mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage));
+
+ // if the drawing is not clipped to the local layer only, we save the current content
+ // of all other layers. We are only interested in the part that will actually
+ // be drawn, so we create as small bitmaps as we can.
+ // This is so that we can erase the drawing that goes in the layers below that will
+ // be coming from the layer itself.
+ if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
+ int w = mLocalLayerRegion.width();
+ int h = mLocalLayerRegion.height();
+ for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
+ Layer layer = mLayers.get(i);
+ BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = image.createGraphics();
+ graphics.drawImage(layer.getImage(),
+ 0, 0, w, h,
+ mLocalLayerRegion.left, mLocalLayerRegion.top,
+ mLocalLayerRegion.right, mLocalLayerRegion.bottom,
+ null);
+ graphics.dispose();
+ layer.setOriginalCopy(image);
+ }
+ }
+ } else {
+ mLocalLayer = null;
+ }
+
+ mLocalLayerPaint = paint;
}
public void dispose() {
- if (mGraphics2D != null) {
- mGraphics2D.dispose();
+ for (Layer layer : mLayers) {
+ layer.getGraphics().dispose();
}
if (mPrevious != null) {
@@ -102,35 +326,35 @@
}
/**
- * Sets the Graphics2D object for this snapshot if it was created through {@link #GcSnapshot()}.
+ * Link the snapshot to a Bitmap_Delegate.
+ * <p/>
+ * This is only for the case where the snapshot was created with a null image when calling
+ * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to
+ * a previous snapshot.
+ * <p/>
* If any transform or clip information was set before, they are put into the Graphics object.
- * @param graphics2D the graphics object to set.
+ * @param bitmap the bitmap to link to.
*/
- public void setGraphics2D(Graphics2D graphics2D) {
- mGraphics2D = graphics2D;
+ public void setBitmap(Bitmap_Delegate bitmap) {
+ assert mLayers.size() == 0;
+ Graphics2D graphics2D = bitmap.getImage().createGraphics();
+ mLayers.add(new Layer(graphics2D, bitmap));
if (mTransform != null) {
- mGraphics2D.setTransform(mTransform);
+ graphics2D.setTransform(mTransform);
mTransform = null;
}
if (mClip != null) {
- mGraphics2D.setClip(mClip);
+ graphics2D.setClip(mClip);
mClip = null;
}
}
- /**
- * Creates and return a copy of the current {@link Graphics2D}.
- * @return a new {@link Graphics2D}.
- */
- public Graphics2D create() {
- assert mGraphics2D != null;
- return (Graphics2D) mGraphics2D.create();
- }
-
public void translate(float dx, float dy) {
- if (mGraphics2D != null) {
- mGraphics2D.translate(dx, dy);
+ if (mLayers.size() > 0) {
+ for (Layer layer : mLayers) {
+ layer.getGraphics().translate(dx, dy);
+ }
} else {
if (mTransform == null) {
mTransform = new AffineTransform();
@@ -140,8 +364,10 @@
}
public void rotate(double radians) {
- if (mGraphics2D != null) {
- mGraphics2D.rotate(radians);
+ if (mLayers.size() > 0) {
+ for (Layer layer : mLayers) {
+ layer.getGraphics().rotate(radians);
+ }
} else {
if (mTransform == null) {
mTransform = new AffineTransform();
@@ -151,8 +377,10 @@
}
public void scale(float sx, float sy) {
- if (mGraphics2D != null) {
- mGraphics2D.scale(sx, sy);
+ if (mLayers.size() > 0) {
+ for (Layer layer : mLayers) {
+ layer.getGraphics().scale(sx, sy);
+ }
} else {
if (mTransform == null) {
mTransform = new AffineTransform();
@@ -162,8 +390,9 @@
}
public AffineTransform getTransform() {
- if (mGraphics2D != null) {
- return mGraphics2D.getTransform();
+ if (mLayers.size() > 0) {
+ // all graphics2D in the list have the same transform
+ return mLayers.get(0).getGraphics().getTransform();
} else {
if (mTransform == null) {
mTransform = new AffineTransform();
@@ -173,8 +402,10 @@
}
public void setTransform(AffineTransform transform) {
- if (mGraphics2D != null) {
- mGraphics2D.setTransform(transform);
+ if (mLayers.size() > 0) {
+ for (Layer layer : mLayers) {
+ layer.getGraphics().setTransform(transform);
+ }
} else {
if (mTransform == null) {
mTransform = new AffineTransform();
@@ -184,40 +415,52 @@
}
public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
- if (mGraphics2D != null) {
+ if (mLayers.size() > 0) {
+ Shape clip = null;
if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
- Area newClip = new Area(mGraphics2D.getClip());
+ Area newClip = new Area(getClip());
newClip.subtract(new Area(
new Rectangle2D.Float(left, top, right - left, bottom - top)));
- mGraphics2D.setClip(newClip);
+ clip = newClip;
} else if (regionOp == Region.Op.INTERSECT.nativeInt) {
- mGraphics2D.clipRect((int) left, (int) top,
- (int) (right - left), (int) (bottom - top));
+ for (Layer layer : mLayers) {
+ layer.getGraphics().clipRect(
+ (int) left, (int) top, (int) (right - left), (int) (bottom - top));
+ }
} else if (regionOp == Region.Op.UNION.nativeInt) {
- Area newClip = new Area(mGraphics2D.getClip());
+ Area newClip = new Area(getClip());
newClip.add(new Area(
new Rectangle2D.Float(left, top, right - left, bottom - top)));
- mGraphics2D.setClip(newClip);
+ clip = newClip;
} else if (regionOp == Region.Op.XOR.nativeInt) {
- Area newClip = new Area(mGraphics2D.getClip());
+ Area newClip = new Area(getClip());
newClip.exclusiveOr(new Area(
new Rectangle2D.Float(left, top, right - left, bottom - top)));
- mGraphics2D.setClip(newClip);
+ clip = newClip;
} else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
Area newClip = new Area(
new Rectangle2D.Float(left, top, right - left, bottom - top));
- newClip.subtract(new Area(mGraphics2D.getClip()));
- mGraphics2D.setClip(newClip);
+ newClip.subtract(new Area(getClip()));
+ clip = newClip;
+
} else if (regionOp == Region.Op.REPLACE.nativeInt) {
- mGraphics2D.setClip((int) left, (int) top,
- (int) (right - left), (int) (bottom - top));
+ for (Layer layer : mLayers) {
+ layer.getGraphics().setClip(
+ (int) left, (int) top, (int) (right - left), (int) (bottom - top));
+ }
}
- return mGraphics2D.getClip().getBounds().isEmpty() == false;
+ if (clip != null) {
+ for (Layer layer : mLayers) {
+ layer.getGraphics().setClip(clip);
+ }
+ }
+
+ return getClip().getBounds().isEmpty() == false;
} else {
if (mClip == null) {
mClip = new Area();
@@ -238,8 +481,9 @@
}
public Shape getClip() {
- if (mGraphics2D != null) {
- return mGraphics2D.getClip();
+ if (mLayers.size() > 0) {
+ // they all have the same clip
+ return mLayers.get(0).getGraphics().getClip();
} else {
if (mClip == null) {
mClip = new Area();
@@ -263,26 +507,268 @@
}
}
+ /**
+ * Executes the Drawable's draw method, with a null paint delegate.
+ * <p/>
+ * Note that the method can be called several times if there are more than one active layer.
+ * @param drawable
+ */
+ public void draw(Drawable drawable) {
+ draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
+ }
+
+ /**
+ * Executes the Drawable's draw method.
+ * <p/>
+ * Note that the method can be called several times if there are more than one active layer.
+ * @param drawable
+ * @param paint
+ * @param compositeOnly whether the paint is used for composite only. This is typically
+ * the case for bitmaps.
+ * @param forceSrcMode if true, this overrides the composite to be SRC
+ */
+ public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
+ boolean forceSrcMode) {
+ // if we clip to the layer, then we only draw in the layer
+ if (mLocalLayer != null && (mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) != 0) {
+ drawInLayer(mLocalLayer, drawable, paint, compositeOnly, forceSrcMode);
+ } else {
+ // draw in all the layers
+ for (Layer layer : mLayers) {
+ drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
+ }
+ }
+ }
+
+ private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
+ boolean compositeOnly, boolean forceSrcMode) {
+ Graphics2D originalGraphics = layer.getGraphics();
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D configuredGraphics2D =
+ paint != null ?
+ createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
+ (Graphics2D) originalGraphics.create();
+
+ try {
+ drawable.draw(configuredGraphics2D, paint);
+ layer.change();
+ } finally {
+ // dispose Graphics2D object
+ configuredGraphics2D.dispose();
+ }
+ }
+
private GcSnapshot doRestore() {
- // if this snapshot does not save everything, then set the previous snapshot
- // to this snapshot content
if (mPrevious != null) {
+ boolean forceAllSave = false;
+ if (mLocalLayer != null) {
+ forceAllSave = true; // layers always save both clip and transform
+
+ // prepare to draw the current layer in the previous layers, ie all layers but
+ // the last one, since the last one is the local layer
+ for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
+ Layer layer = mLayers.get(i);
+
+ Graphics2D baseGfx = layer.getImage().createGraphics();
+
+ // if the layer contains an original copy this means the flags
+ // didn't restrict drawing to the local layer and we need to make sure the
+ // layer bounds in the layer beneath didn't receive any drawing.
+ // so we use the originalCopy to erase the new drawings in there.
+ BufferedImage originalCopy = layer.getOriginalCopy();
+ if (originalCopy != null) {
+ Graphics2D g = (Graphics2D) baseGfx.create();
+ g.setComposite(AlphaComposite.Src);
+
+ g.drawImage(originalCopy,
+ mLocalLayerRegion.left, mLocalLayerRegion.top,
+ mLocalLayerRegion.right, mLocalLayerRegion.bottom,
+ 0, 0, mLocalLayerRegion.width(), mLocalLayerRegion.height(),
+ null);
+ g.dispose();
+ }
+
+ // now draw put the content of the local layer onto the layer, using the paint
+ // information
+ Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
+ true /*alphaOnly*/, false /*forceSrcMode*/);
+
+ g.drawImage(mLocalLayer.getImage(),
+ mLocalLayerRegion.left, mLocalLayerRegion.top,
+ mLocalLayerRegion.right, mLocalLayerRegion.bottom,
+ mLocalLayerRegion.left, mLocalLayerRegion.top,
+ mLocalLayerRegion.right, mLocalLayerRegion.bottom,
+ null);
+ g.dispose();
+
+ baseGfx.dispose();
+ }
+ }
+
+ // if this snapshot does not save everything, then set the previous snapshot
+ // to this snapshot content
+
// didn't save the matrix? set the current matrix on the previous snapshot
- if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
- mPrevious.mGraphics2D.setTransform(getTransform());
+ if (forceAllSave == false && (mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
+ AffineTransform mtx = getTransform();
+ for (Layer layer : mPrevious.mLayers) {
+ layer.getGraphics().setTransform(mtx);
+ }
}
// didn't save the clip? set the current clip on the previous snapshot
- if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
- mPrevious.mGraphics2D.setClip(mGraphics2D.getClip());
+ if (forceAllSave == false && (mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
+ Shape clip = getClip();
+ for (Layer layer : mPrevious.mLayers) {
+ layer.getGraphics().setClip(clip);
+ }
}
}
- if (mGraphics2D != null) {
- mGraphics2D.dispose();
+ for (Layer layer : mLayers) {
+ layer.getGraphics().dispose();
}
return mPrevious;
}
+ /**
+ * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
+ * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
+ */
+ private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
+ boolean compositeOnly, boolean forceSrcMode) {
+ // make new one graphics
+ Graphics2D g = (Graphics2D) original.create();
+
+ // configure it
+
+ if (paint.isAntiAliased()) {
+ g.setRenderingHint(
+ RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(
+ RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ }
+
+ boolean customShader = false;
+
+ // get the shader first, as it'll replace the color if it can be used it.
+ if (compositeOnly == false) {
+ int nativeShader = paint.getShader();
+ if (nativeShader > 0) {
+ Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(nativeShader);
+ assert shaderDelegate != null;
+ if (shaderDelegate != null) {
+ if (shaderDelegate.isSupported()) {
+ java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
+ assert shaderPaint != null;
+ if (shaderPaint != null) {
+ g.setPaint(shaderPaint);
+ customShader = true;
+ }
+ } else {
+ Bridge.getLog().fidelityWarning(null,
+ shaderDelegate.getSupportMessage(),
+ null);
+ }
+ }
+ }
+
+ // if no shader, use the paint color
+ if (customShader == false) {
+ g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
+ }
+
+ boolean customStroke = false;
+ int pathEffect = paint.getPathEffect();
+ if (pathEffect > 0) {
+ PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect);
+ assert effectDelegate != null;
+ if (effectDelegate != null) {
+ if (effectDelegate.isSupported()) {
+ Stroke stroke = effectDelegate.getStroke(paint);
+ assert stroke != null;
+ if (stroke != null) {
+ g.setStroke(stroke);
+ customStroke = true;
+ }
+ } else {
+ Bridge.getLog().fidelityWarning(null,
+ effectDelegate.getSupportMessage(),
+ null);
+ }
+ }
+ }
+
+ // if no custom stroke as been set, set the default one.
+ if (customStroke == false) {
+ g.setStroke(new BasicStroke(
+ paint.getStrokeWidth(),
+ paint.getJavaCap(),
+ paint.getJavaJoin(),
+ paint.getJavaStrokeMiter()));
+ }
+ }
+
+ // the alpha for the composite. Always opaque if the normal paint color is used since
+ // it contains the alpha
+ int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
+
+ if (forceSrcMode) {
+ g.setComposite(AlphaComposite.getInstance(
+ AlphaComposite.SRC, (float) alpha / 255.f));
+ } else {
+ boolean customXfermode = false;
+ int xfermode = paint.getXfermode();
+ if (xfermode > 0) {
+ Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(xfermode);
+ assert xfermodeDelegate != null;
+ if (xfermodeDelegate != null) {
+ if (xfermodeDelegate.isSupported()) {
+ Composite composite = xfermodeDelegate.getComposite(alpha);
+ assert composite != null;
+ if (composite != null) {
+ g.setComposite(composite);
+ customXfermode = true;
+ }
+ } else {
+ Bridge.getLog().fidelityWarning(null,
+ xfermodeDelegate.getSupportMessage(),
+ null);
+ }
+ }
+ }
+
+ // if there was no custom xfermode, but we have alpha (due to a shader and a non
+ // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
+ // that will handle the alpha.
+ if (customXfermode == false && alpha != 0xFF) {
+ g.setComposite(AlphaComposite.getInstance(
+ AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+ }
+ }
+
+ return g;
+ }
+
+ private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
+ // array with 4 corners
+ float[] corners = new float[] {
+ src.left, src.top,
+ src.right, src.top,
+ src.right, src.bottom,
+ src.left, src.bottom,
+ };
+
+ // apply the transform to them.
+ matrix.transform(corners, 0, corners, 0, 4);
+
+ // now put the result in the rect. We take the min/max of Xs and min/max of Ys
+ dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
+ dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
+
+ dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
+ dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
+ }
+
}
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
index f823314..08e4606 100644
--- a/wifi/java/android/net/wifi/SupplicantStateTracker.java
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -79,11 +79,6 @@
start();
}
- public void resetSupplicantState() {
- transitionTo(mUninitializedState);
- }
-
-
private void transitionOnSupplicantStateChange(StateChangeResult stateChangeResult) {
SupplicantState supState = (SupplicantState) stateChangeResult.state;
@@ -121,11 +116,11 @@
}
}
- private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
+ private void sendSupplicantStateChangedBroadcast(SupplicantState state, boolean failedAuth) {
Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
+ intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable) state);
if (failedAuth) {
intent.putExtra(
WifiManager.EXTRA_SUPPLICANT_ERROR,
@@ -153,11 +148,14 @@
break;
case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- sendSupplicantStateChangedBroadcast(stateChangeResult,
- mAuthFailureInSupplicantBroadcast);
+ SupplicantState state = stateChangeResult.state;
+ sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
mAuthFailureInSupplicantBroadcast = false;
transitionOnSupplicantStateChange(stateChangeResult);
break;
+ case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
+ transitionTo(mUninitializedState);
+ break;
default:
Log.e(TAG, "Ignoring " + message);
break;
@@ -166,6 +164,14 @@
}
}
+ /*
+ * This indicates that the supplicant state as seen
+ * by the framework is not initialized yet. We are
+ * in this state right after establishing a control
+ * channel connection before any supplicant events
+ * or after we have lost the control channel
+ * connection to the supplicant
+ */
class UninitializedState extends HierarchicalState {
@Override
public void enter() {
@@ -239,7 +245,7 @@
switch (message.what) {
case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState state = (SupplicantState) stateChangeResult.state;
+ SupplicantState state = stateChangeResult.state;
if (state == SupplicantState.ASSOCIATING ||
state == SupplicantState.ASSOCIATED ||
state == SupplicantState.FOUR_WAY_HANDSHAKE ||
@@ -253,7 +259,7 @@
WifiConfigStore.disableNetwork(stateChangeResult.networkId);
}
mLoopDetectIndex = state.ordinal();
- sendSupplicantStateChangedBroadcast(stateChangeResult,
+ sendSupplicantStateChangedBroadcast(state,
mAuthFailureInSupplicantBroadcast);
} else {
//Have the DefaultState handle the transition
@@ -280,9 +286,8 @@
switch(message.what) {
case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState state = (SupplicantState) stateChangeResult.state;
- sendSupplicantStateChangedBroadcast(stateChangeResult,
- mAuthFailureInSupplicantBroadcast);
+ SupplicantState state = stateChangeResult.state;
+ sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
/* Ignore a re-auth in completed state */
if (state == SupplicantState.ASSOCIATING ||
state == SupplicantState.ASSOCIATED ||
@@ -293,6 +298,10 @@
}
transitionOnSupplicantStateChange(stateChangeResult);
break;
+ case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
+ sendSupplicantStateChangedBroadcast(SupplicantState.DISCONNECTED, false);
+ transitionTo(mUninitializedState);
+ break;
default:
return NOT_HANDLED;
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 17a35c4..909ad43 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -326,13 +326,16 @@
/* Set the frequency band */
static final int CMD_SET_FREQUENCY_BAND = 90;
- /* Commands from the SupplicantStateTracker */
+ /* Commands from/to the SupplicantStateTracker */
/* Indicates whether a wifi network is available for connection */
- static final int CMD_SET_NETWORK_AVAILABLE = 111;
+ static final int CMD_SET_NETWORK_AVAILABLE = 111;
+ /* Reset the supplicant state tracker */
+ static final int CMD_RESET_SUPPLICANT_STATE = 112;
+
/* Commands/events reported by WpsStateMachine */
/* Indicates the completion of WPS activity */
- static final int WPS_COMPLETED_EVENT = 121;
+ static final int WPS_COMPLETED_EVENT = 121;
private static final int CONNECT_MODE = 1;
private static final int SCAN_ONLY_MODE = 2;
@@ -1453,20 +1456,17 @@
********************************************************/
/**
- * A structure for supplying information about a supplicant state
- * change in the STATE_CHANGE event message that comes from the
- * WifiMonitor
- * thread.
+ * Stores supplicant state change information passed from WifiMonitor
*/
static class StateChangeResult {
- StateChangeResult(int networkId, String BSSID, Object state) {
+ StateChangeResult(int networkId, String BSSID, SupplicantState state) {
this.state = state;
this.BSSID = BSSID;
this.networkId = networkId;
}
int networkId;
String BSSID;
- Object state;
+ SupplicantState state;
}
/**
@@ -1505,11 +1505,9 @@
*/
void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
if (newState == NetworkInfo.DetailedState.CONNECTED) {
- sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
+ sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT, networkId, 0, BSSID));
} else {
- sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
+ sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, networkId, 0, BSSID));
}
}
@@ -1755,7 +1753,6 @@
if(WifiNative.startSupplicant()) {
Log.d(TAG, "Supplicant start successful");
mWifiMonitor.startMonitoring();
- setWifiState(WIFI_STATE_ENABLED);
transitionTo(mSupplicantStartingState);
} else {
Log.e(TAG, "Failed to start supplicant!");
@@ -1914,8 +1911,11 @@
switch(message.what) {
case SUP_CONNECTION_EVENT:
Log.d(TAG, "Supplicant connection established");
+ setWifiState(WIFI_STATE_ENABLED);
mSupplicantRestartCount = 0;
- mSupplicantStateTracker.resetSupplicantState();
+ /* Reset the supplicant state to indicate the supplicant
+ * state is not known at this time */
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
/* Initialize data structures */
mLastBssid = null;
mLastNetworkId = -1;
@@ -1944,7 +1944,7 @@
Log.e(TAG, "Failed " + mSupplicantRestartCount +
" times to start supplicant, unload driver");
transitionTo(mDriverLoadedState);
- sendMessage(CMD_UNLOAD_DRIVER);
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
}
break;
case CMD_LOAD_DRIVER:
@@ -1995,8 +1995,9 @@
WifiNative.killSupplicant();
}
handleNetworkDisconnect();
+ setWifiState(WIFI_STATE_DISABLING);
sendSupplicantConnectionChangedBroadcast(false);
- mSupplicantStateTracker.resetSupplicantState();
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
transitionTo(mSupplicantStoppingState);
break;
case SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
@@ -2006,7 +2007,7 @@
WifiNative.closeSupplicantConnection();
handleNetworkDisconnect();
sendSupplicantConnectionChangedBroadcast(false);
- mSupplicantStateTracker.resetSupplicantState();
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
transitionTo(mDriverLoadedState);
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
break;
@@ -2380,7 +2381,7 @@
break;
case SUPPLICANT_STATE_CHANGE_EVENT:
stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState state = (SupplicantState) stateChangeResult.state;
+ SupplicantState state = stateChangeResult.state;
// Supplicant state change
// [31-13] Reserved for future use
// [8 - 0] Supplicant state (as defined in SupplicantState.java)
@@ -2445,13 +2446,13 @@
return NOT_HANDLED;
case NETWORK_CONNECTION_EVENT:
Log.d(TAG,"Network connection established");
- stateChangeResult = (StateChangeResult) message.obj;
+ mLastNetworkId = message.arg1;
+ mLastBssid = (String) message.obj;
//TODO: make supplicant modification to push this in events
mWifiInfo.setSSID(fetchSSID());
- mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- mLastNetworkId = stateChangeResult.networkId;
+ mWifiInfo.setBSSID(mLastBssid);
+ mWifiInfo.setNetworkId(mLastNetworkId);
/* send event to CM & network change broadcast */
setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
sendNetworkStateChangeBroadcast(mLastBssid);
@@ -2825,8 +2826,7 @@
break;
case SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState state = (SupplicantState) stateChangeResult.state;
- setNetworkDetailedState(WifiInfo.getDetailedStateOf(state));
+ setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
/* DriverStartedState does the rest of the handling */
return NOT_HANDLED;
default: