Commit c304d108 authored by Daniel Olshansky's avatar Daniel Olshansky Committed by Chet Haase
Browse files

First commit for ImagePixelization.

Change-Id: I02ab79d17cb32773a9bd1a4c50bc76bde8925368
(cherry picked from commit 4ea55980)
parent 4964c9ad
<?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>
samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png

9.18 KB

samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg

265 KB

samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png

2.67 KB

samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png

5.11 KB

<!-- 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>
<!-- 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
<!-- 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>
<!-- 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>
<!-- 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>
/*
* 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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment