First commit for ImagePixelization.
Change-Id: I02ab79d17cb32773a9bd1a4c50bc76bde8925368
(cherry picked from commit 4ea559806d2c4839e1c4019445c43c5fe41c9a0b)
diff --git a/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml b/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml
new file mode 100644
index 0000000..7bbb4f6
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.imagepixelization"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="11"
+ android:targetSdkVersion="17"/>
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".ImagePixelization"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg
new file mode 100644
index 0000000..00bdbba
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml b/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml
new file mode 100644
index 0000000..b3e30a8
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml
@@ -0,0 +1,42 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/pixelView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_above="@+id/seekbar"
+ android:scaleType="fitXY"/>
+
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:max="@integer/seek_max"
+ android:progress="0"
+ android:layout_alignParentBottom = "true"/>
+
+</RelativeLayout>
+
diff --git a/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml b/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml
new file mode 100644
index 0000000..e6a8d66
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml
@@ -0,0 +1,31 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item android:id="@+id/builtin_pixelation_checkbox"
+ android:showAsAction="never"
+ android:title="@string/builtin_pixelization"
+ android:checkable="true" />
+
+ <item android:id="@+id/checkbox"
+ android:showAsAction="never"
+ android:title="@string/async_task"
+ android:checkable="true" />
+
+ <item android:id="@+id/animate"
+ android:showAsAction="never"
+ android:title="@string/animate_pixelation"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml b/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml
new file mode 100644
index 0000000..09d93f5
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml b/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml
new file mode 100644
index 0000000..98c1438
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml
@@ -0,0 +1,19 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+ <integer name="seek_max">1000</integer>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml b/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml
new file mode 100644
index 0000000..35a87c8
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml
@@ -0,0 +1,22 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+ <string name="app_name" >ImagePixelization</string>
+ <string name="animate_pixelation">Animate</string>
+ <string name="async_task">Using AyncTask</string>
+ <string name="builtin_pixelization">Built-in Pixelization</string>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java b/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java
new file mode 100644
index 0000000..e31d95d
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.imagepixelization;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.animation.LinearInterpolator;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+
+import java.util.Arrays;
+
+/**
+ * This application shows three different graphics/animation concepts.
+ *
+ * A pixelization effect is applied to an image with varying pixelization
+ * factors to achieve an image that is pixelized to varying degrees. In
+ * order to optimize the amount of image processing performed on the image
+ * being pixelized, the pixelization effect only takes place if a predefined
+ * amount of time has elapsed since the main image was last pixelized. The
+ * effect is also applied when the user stops moving the seekbar.
+ *
+ * This application also shows how to use a ValueAnimator to achieve a
+ * smooth self-animating seekbar.
+ *
+ * Lastly, this application shows a use case of AsyncTask where some
+ * computation heavy processing can be moved onto a background thread,
+ * so as to keep the UI completely responsive to user input.
+ */
+public class ImagePixelization extends Activity {
+
+ final private static int SEEKBAR_ANIMATION_DURATION = 10000;
+ final private static int TIME_BETWEEN_TASKS = 400;
+ final private static int SEEKBAR_STOP_CHANGE_DELTA = 5;
+ final private static float PROGRESS_TO_PIXELIZATION_FACTOR = 4000.0f;
+
+ Bitmap mImageBitmap;
+ ImageView mImageView;
+ SeekBar mSeekBar;
+ boolean mIsChecked = false;
+ boolean mIsBuiltinPixelizationChecked = false;
+ int mLastProgress = 0;
+ long mLastTime = 0;
+ Bitmap mPixelatedBitmap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_image_pixelization);
+
+ mImageView = (ImageView) findViewById(R.id.pixelView);
+ mSeekBar = (SeekBar)findViewById(R.id.seekbar);
+
+ mImageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
+ mImageView.setImageBitmap(mImageBitmap);
+
+ mSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
+ }
+
+ private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener =
+ new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (Math.abs(mSeekBar.getProgress() - mLastProgress) > SEEKBAR_STOP_CHANGE_DELTA) {
+ invokePixelization();
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ checkIfShouldPixelize();
+ }
+ };
+
+ /**
+ * Checks if enough time has elapsed since the last pixelization call was invoked.
+ * This prevents too many pixelization processes from being invoked at the same time
+ * while previous ones have not yet completed.
+ */
+ public void checkIfShouldPixelize() {
+ if ((System.currentTimeMillis() - mLastTime) > TIME_BETWEEN_TASKS) {
+ invokePixelization();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.image_pixelization, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected (MenuItem item) {
+ switch (item.getItemId()){
+ case R.id.animate:
+ ObjectAnimator animator = ObjectAnimator.ofInt(mSeekBar, "progress", 0,
+ mSeekBar.getMax());
+ animator.setInterpolator(new LinearInterpolator());
+ animator.setDuration(SEEKBAR_ANIMATION_DURATION);
+ animator.start();
+ break;
+ case R.id.checkbox:
+ if (mIsChecked) {
+ item.setChecked(false);
+ mIsChecked = false;
+ } else {
+ item.setChecked(true);
+ mIsChecked = true;
+ }
+ break;
+ case R.id.builtin_pixelation_checkbox:
+ mIsBuiltinPixelizationChecked = !mIsBuiltinPixelizationChecked;
+ item.setChecked(mIsBuiltinPixelizationChecked);
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * A simple pixelization algorithm. This uses a box blur algorithm where all the
+ * pixels within some region are averaged, and that average pixel value is then
+ * applied to all the pixels within that region. A higher pixelization factor
+ * imposes a smaller number of regions of greater size. Similarly, a smaller
+ * pixelization factor imposes a larger number of regions of smaller size.
+ */
+ public BitmapDrawable customImagePixelization(float pixelizationFactor, Bitmap bitmap) {
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ if (mPixelatedBitmap == null || !(width == mPixelatedBitmap.getWidth() && height ==
+ mPixelatedBitmap.getHeight())) {
+ mPixelatedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ int xPixels = (int) (pixelizationFactor * ((float)width));
+ xPixels = xPixels > 0 ? xPixels : 1;
+ int yPixels = (int) (pixelizationFactor * ((float)height));
+ yPixels = yPixels > 0 ? yPixels : 1;
+ int pixel = 0, red = 0, green = 0, blue = 0, numPixels = 0;
+
+ int[] bitmapPixels = new int[width * height];
+ bitmap.getPixels(bitmapPixels, 0, width, 0, 0, width, height);
+
+ int[] pixels = new int[yPixels * xPixels];
+
+ int maxX, maxY;
+
+ for (int y = 0; y < height; y+=yPixels) {
+ for (int x = 0; x < width; x+=xPixels) {
+
+ numPixels = red = green = blue = 0;
+
+ maxX = Math.min(x + xPixels, width);
+ maxY = Math.min(y + yPixels, height);
+
+ for (int i = x; i < maxX; i++) {
+ for (int j = y; j < maxY; j++) {
+ pixel = bitmapPixels[j * width + i];
+ red += Color.red(pixel);
+ green += Color.green(pixel);
+ blue += Color.blue(pixel);
+ numPixels ++;
+ }
+ }
+
+ pixel = Color.rgb(red / numPixels, green / numPixels, blue / numPixels);
+
+ Arrays.fill(pixels, pixel);
+
+ int w = Math.min(xPixels, width - x);
+ int h = Math.min(yPixels, height - y);
+
+ mPixelatedBitmap.setPixels(pixels, 0 , w, x , y, w, h);
+ }
+ }
+
+ return new BitmapDrawable(getResources(), mPixelatedBitmap);
+ }
+
+ /**
+ * This method of image pixelization utilizes the bitmap scaling operations built
+ * into the framework. By downscaling the bitmap and upscaling it back to its
+ * original size (while setting the filter flag to false), the same effect can be
+ * achieved with much better performance.
+ */
+ public BitmapDrawable builtInPixelization(float pixelizationFactor, Bitmap bitmap) {
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ int downScaleFactorWidth = (int)(pixelizationFactor * width);
+ downScaleFactorWidth = downScaleFactorWidth > 0 ? downScaleFactorWidth : 1;
+ int downScaleFactorHeight = (int)(pixelizationFactor * height);
+ downScaleFactorHeight = downScaleFactorHeight > 0 ? downScaleFactorHeight : 1;
+
+ int downScaledWidth = width / downScaleFactorWidth;
+ int downScaledHeight = height / downScaleFactorHeight;
+
+ Bitmap pixelatedBitmap = Bitmap.createScaledBitmap(bitmap, downScaledWidth,
+ downScaledHeight, false);
+
+ /* Bitmap's createScaledBitmap method has a filter parameter that can be set to either
+ * true or false in order to specify either bilinear filtering or point sampling
+ * respectively when the bitmap is scaled up or now.
+ *
+ * Similarly, a BitmapDrawable also has a flag to specify the same thing. When the
+ * BitmapDrawable is applied to an ImageView that has some scaleType, the filtering
+ * flag is taken into consideration. However, for optimization purposes, this flag was
+ * ignored in BitmapDrawables before Jelly Bean MR1.
+ *
+ * Here, it is important to note that prior to JBMR1, two bitmap scaling operations
+ * are required to achieve the pixelization effect. Otherwise, a BitmapDrawable
+ * can be created corresponding to the downscaled bitmap such that when it is
+ * upscaled to fit the ImageView, the upscaling operation is a lot faster since
+ * it uses internal optimizations to fit the ImageView.
+ * */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), pixelatedBitmap);
+ bitmapDrawable.setFilterBitmap(false);
+ return bitmapDrawable;
+ } else {
+ Bitmap upscaled = Bitmap.createScaledBitmap(pixelatedBitmap, width, height, false);
+ return new BitmapDrawable(getResources(), upscaled);
+ }
+ }
+
+ /**
+ * Invokes pixelization either on the main thread or on a background thread
+ * depending on whether or not the checkbox was checked.
+ */
+ public void invokePixelization () {
+ mLastTime = System.currentTimeMillis();
+ mLastProgress = mSeekBar.getProgress();
+ if (mIsChecked) {
+ PixelizeImageAsyncTask asyncPixelateTask = new PixelizeImageAsyncTask();
+ asyncPixelateTask.execute(mSeekBar.getProgress() / PROGRESS_TO_PIXELIZATION_FACTOR,
+ mImageBitmap);
+ } else {
+ mImageView.setImageDrawable(pixelizeImage(mSeekBar.getProgress()
+ / PROGRESS_TO_PIXELIZATION_FACTOR, mImageBitmap));
+ }
+ }
+
+ /**
+ * Selects either the custom pixelization algorithm that sets and gets bitmap
+ * pixels manually or the one that uses built-in bitmap operations.
+ */
+ public BitmapDrawable pixelizeImage(float pixelizationFactor, Bitmap bitmap) {
+ if (mIsBuiltinPixelizationChecked) {
+ return builtInPixelization(pixelizationFactor, bitmap);
+ } else {
+ return customImagePixelization(pixelizationFactor, bitmap);
+ }
+ }
+
+ /**
+ * Implementation of the AsyncTask class showing how to run the
+ * pixelization algorithm in the background, and retrieving the
+ * pixelated image from the resulting operation.
+ */
+ private class PixelizeImageAsyncTask extends AsyncTask<Object, Void, BitmapDrawable> {
+
+ @Override
+ protected BitmapDrawable doInBackground(Object... params) {
+ float pixelizationFactor = (Float)params[0];
+ Bitmap originalBitmap = (Bitmap)params[1];
+ return pixelizeImage(pixelizationFactor, originalBitmap);
+ }
+
+ @Override
+ protected void onPostExecute(BitmapDrawable result) {
+ mImageView.setImageDrawable(result);
+ }
+
+ @Override
+ protected void onPreExecute() {
+
+ }
+
+ @Override
+ protected void onProgressUpdate(Void... values) {
+
+ }
+ }
+}
\ No newline at end of file