14ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky/*
24ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * Copyright (C) 2013 The Android Open Source Project
34ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky *
44ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * Licensed under the Apache License, Version 2.0 (the "License");
54ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * you may not use this file except in compliance with the License.
64ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * You may obtain a copy of the License at
74ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky *
84ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky *      http://www.apache.org/licenses/LICENSE-2.0
94ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky *
104ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * Unless required by applicable law or agreed to in writing, software
114ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * distributed under the License is distributed on an "AS IS" BASIS,
124ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * See the License for the specific language governing permissions and
144ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * limitations under the License.
154ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky */
164ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskypackage com.example.android.imagepixelization;
174ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
184ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.animation.ObjectAnimator;
194ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.app.Activity;
204ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.graphics.Bitmap;
214ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.graphics.BitmapFactory;
224ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.graphics.Color;
234ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.graphics.drawable.BitmapDrawable;
244ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.os.AsyncTask;
254ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.os.Build;
264ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.os.Bundle;
274ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.view.Menu;
284ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.view.MenuItem;
294ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.view.animation.LinearInterpolator;
304ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.widget.ImageView;
314ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport android.widget.SeekBar;
324ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
334ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskyimport java.util.Arrays;
344ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
354ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky/**
364ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * This application shows three different graphics/animation concepts.
374ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky *
384ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * A pixelization effect is applied to an image with varying pixelization
394ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * factors to achieve an image that is pixelized to varying degrees. In
404ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * order to optimize the amount of image processing performed on the image
414ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * being pixelized, the pixelization effect only takes place if a predefined
424ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * amount of time has elapsed since the main image was last pixelized. The
434ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * effect is also applied when the user stops moving the seekbar.
444ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky *
454ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * This application also shows how to use a ValueAnimator to achieve a
464ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * smooth self-animating seekbar.
474ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky *
484ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * Lastly, this application shows a use case of AsyncTask where some
494ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * computation heavy processing can be moved onto a background thread,
504ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky * so as to keep the UI completely responsive to user input.
514ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky */
524ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshanskypublic class ImagePixelization extends Activity {
534ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
544ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    final private static int SEEKBAR_ANIMATION_DURATION = 10000;
554ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    final private static int TIME_BETWEEN_TASKS = 400;
564ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    final private static int SEEKBAR_STOP_CHANGE_DELTA = 5;
574ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    final private static float PROGRESS_TO_PIXELIZATION_FACTOR = 4000.0f;
584ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
594ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    Bitmap mImageBitmap;
604ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    ImageView mImageView;
614ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    SeekBar mSeekBar;
624ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    boolean mIsChecked = false;
634ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    boolean mIsBuiltinPixelizationChecked = false;
644ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    int mLastProgress = 0;
654ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    long mLastTime = 0;
664ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    Bitmap mPixelatedBitmap;
674ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
684ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    @Override
694ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    protected void onCreate(Bundle savedInstanceState) {
704ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        super.onCreate(savedInstanceState);
714ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        setContentView(R.layout.activity_image_pixelization);
724ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
734ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        mImageView = (ImageView) findViewById(R.id.pixelView);
744ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        mSeekBar = (SeekBar)findViewById(R.id.seekbar);
754ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
764ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        mImageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
774ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        mImageView.setImageBitmap(mImageBitmap);
784ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
794ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        mSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
804ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
814ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
824ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener =
834ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            new SeekBar.OnSeekBarChangeListener() {
844ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
854ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        @Override
864ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        public void onStopTrackingTouch(SeekBar seekBar) {
874ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            if (Math.abs(mSeekBar.getProgress() - mLastProgress) > SEEKBAR_STOP_CHANGE_DELTA) {
884ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                invokePixelization();
894ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            }
904ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
914ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
924ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        @Override
934ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        public void onStartTrackingTouch(SeekBar seekBar) {
944ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
954ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
964ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        @Override
974ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
984ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            checkIfShouldPixelize();
994ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
1004ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    };
1014ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1024ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    /**
1034ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * Checks if enough time has elapsed since the last pixelization call was invoked.
1044ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * This prevents too many pixelization processes from being invoked at the same time
1054ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * while previous ones have not yet completed.
1064ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     */
1074ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    public void checkIfShouldPixelize() {
1084ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        if ((System.currentTimeMillis() - mLastTime) > TIME_BETWEEN_TASKS) {
1094ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            invokePixelization();
1104ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
1114ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
1124ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1134ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    @Override
1144ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    public boolean onCreateOptionsMenu(Menu menu) {
1154ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        getMenuInflater().inflate(R.menu.image_pixelization, menu);
1164ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        return true;
1174ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
1184ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1194ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    @Override
1204ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    public boolean onOptionsItemSelected (MenuItem item) {
1214ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        switch (item.getItemId()){
1224ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            case R.id.animate:
1234ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                ObjectAnimator animator = ObjectAnimator.ofInt(mSeekBar, "progress", 0,
1244ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                        mSeekBar.getMax());
1254ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                animator.setInterpolator(new LinearInterpolator());
1264ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                animator.setDuration(SEEKBAR_ANIMATION_DURATION);
1274ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                animator.start();
1284ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                break;
1294ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            case R.id.checkbox:
1304ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                if (mIsChecked) {
1314ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    item.setChecked(false);
1324ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    mIsChecked = false;
1334ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                } else {
1344ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    item.setChecked(true);
1354ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    mIsChecked = true;
1364ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                }
1374ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                break;
1384ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            case R.id.builtin_pixelation_checkbox:
1394ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                mIsBuiltinPixelizationChecked = !mIsBuiltinPixelizationChecked;
1404ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                item.setChecked(mIsBuiltinPixelizationChecked);
1414ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                break;
1424ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            default:
1434ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                break;
1444ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
1454ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        return true;
1464ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
1474ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1484ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    /**
1494ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * A simple pixelization algorithm. This uses a box blur algorithm where all the
1504ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * pixels within some region are averaged, and that average pixel value is then
1514ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * applied to all the pixels within that region. A higher pixelization factor
1524ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * imposes a smaller number of regions of greater size. Similarly, a smaller
1534ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * pixelization factor imposes a larger number of regions of smaller size.
1544ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     */
1554ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    public BitmapDrawable customImagePixelization(float pixelizationFactor, Bitmap bitmap) {
1564ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1574ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int width = bitmap.getWidth();
1584ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int height = bitmap.getHeight();
1594ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1604ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        if (mPixelatedBitmap == null || !(width == mPixelatedBitmap.getWidth() && height ==
1614ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                mPixelatedBitmap.getHeight())) {
1624ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            mPixelatedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1634ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
1644ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1654ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int xPixels = (int) (pixelizationFactor * ((float)width));
1664ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        xPixels = xPixels > 0 ? xPixels : 1;
1674ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int yPixels = (int)  (pixelizationFactor * ((float)height));
1684ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        yPixels = yPixels > 0 ? yPixels : 1;
1694ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int pixel = 0, red = 0, green = 0, blue = 0, numPixels = 0;
1704ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1714ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int[] bitmapPixels = new int[width * height];
1724ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        bitmap.getPixels(bitmapPixels, 0, width, 0, 0, width, height);
1734ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1744ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int[] pixels = new int[yPixels * xPixels];
1754ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1764ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int maxX, maxY;
1774ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1784ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        for (int y = 0; y < height; y+=yPixels) {
1794ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            for (int x = 0; x < width; x+=xPixels) {
1804ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1814ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                numPixels = red = green = blue = 0;
1824ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1834ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                maxX = Math.min(x + xPixels, width);
1844ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                maxY = Math.min(y + yPixels, height);
1854ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1864ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                for (int i = x; i < maxX; i++) {
1874ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    for (int j = y; j < maxY; j++) {
1884ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                        pixel = bitmapPixels[j * width + i];
1894ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                        red += Color.red(pixel);
1904ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                        green += Color.green(pixel);
1914ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                        blue += Color.blue(pixel);
1924ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                        numPixels ++;
1934ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    }
1944ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                }
1954ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1964ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                pixel = Color.rgb(red / numPixels, green / numPixels, blue / numPixels);
1974ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
1984ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                Arrays.fill(pixels, pixel);
1994ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2004ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                int w = Math.min(xPixels, width - x);
2014ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                int h = Math.min(yPixels, height - y);
2024ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2034ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                mPixelatedBitmap.setPixels(pixels, 0 , w, x , y, w, h);
2044ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            }
2054ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
2064ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2074ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        return new BitmapDrawable(getResources(), mPixelatedBitmap);
2084ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
2094ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2104ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    /**
2114ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * This method of image pixelization utilizes the bitmap scaling operations built
2124ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * into the framework. By downscaling the bitmap and upscaling it back to its
2134ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * original size (while setting the filter flag to false), the same effect can be
2144ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * achieved with much better performance.
2154ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     */
2164ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    public BitmapDrawable builtInPixelization(float pixelizationFactor, Bitmap bitmap) {
2174ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2184ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int width = bitmap.getWidth();
2194ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int height = bitmap.getHeight();
2204ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2214ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int downScaleFactorWidth = (int)(pixelizationFactor * width);
2224ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        downScaleFactorWidth = downScaleFactorWidth > 0 ? downScaleFactorWidth : 1;
2234ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int downScaleFactorHeight = (int)(pixelizationFactor * height);
2244ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        downScaleFactorHeight = downScaleFactorHeight > 0 ? downScaleFactorHeight : 1;
2254ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2264ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int downScaledWidth =  width / downScaleFactorWidth;
2274ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        int downScaledHeight = height / downScaleFactorHeight;
2284ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2294ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        Bitmap pixelatedBitmap = Bitmap.createScaledBitmap(bitmap, downScaledWidth,
2304ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                downScaledHeight, false);
2314ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2324ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        /* Bitmap's createScaledBitmap method has a filter parameter that can be set to either
2334ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * true or false in order to specify either bilinear filtering or point sampling
2344ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * respectively when the bitmap is scaled up or now.
2354ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         *
2364ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * Similarly, a BitmapDrawable also has a flag to specify the same thing. When the
2374ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * BitmapDrawable is applied to an ImageView that has some scaleType, the filtering
2384ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * flag is taken into consideration. However, for optimization purposes, this flag was
2394ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * ignored in BitmapDrawables before Jelly Bean MR1.
2404ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         *
2414ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * Here, it is important to note that prior to JBMR1, two bitmap scaling operations
2424ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * are required to achieve the pixelization effect. Otherwise, a BitmapDrawable
2434ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * can be created corresponding to the downscaled bitmap such that when it is
2444ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * upscaled to fit the ImageView, the upscaling operation is a lot faster since
2454ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * it uses internal optimizations to fit the ImageView.
2464ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky         * */
2474ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
2484ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), pixelatedBitmap);
2494ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            bitmapDrawable.setFilterBitmap(false);
2504ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            return bitmapDrawable;
2514ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        } else {
2524ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            Bitmap upscaled = Bitmap.createScaledBitmap(pixelatedBitmap, width, height, false);
2534ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            return new BitmapDrawable(getResources(), upscaled);
2544ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
2554ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
2564ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2574ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    /**
2584ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * Invokes pixelization either on the main thread or on a background thread
2594ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * depending on whether or not the checkbox was checked.
2604ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     */
2614ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    public void invokePixelization () {
2624ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        mLastTime = System.currentTimeMillis();
2634ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        mLastProgress = mSeekBar.getProgress();
2644ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        if (mIsChecked) {
2654ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            PixelizeImageAsyncTask asyncPixelateTask = new PixelizeImageAsyncTask();
2664ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            asyncPixelateTask.execute(mSeekBar.getProgress() / PROGRESS_TO_PIXELIZATION_FACTOR,
2674ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    mImageBitmap);
2684ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        } else {
2694ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            mImageView.setImageDrawable(pixelizeImage(mSeekBar.getProgress()
2704ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky                    / PROGRESS_TO_PIXELIZATION_FACTOR, mImageBitmap));
2714ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
2724ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
2734ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2744ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    /**
2754ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     *  Selects either the custom pixelization algorithm that sets and gets bitmap
2764ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     *  pixels manually or the one that uses built-in bitmap operations.
2774ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     */
2784ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    public BitmapDrawable pixelizeImage(float pixelizationFactor, Bitmap bitmap) {
2794ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        if (mIsBuiltinPixelizationChecked) {
2804ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            return builtInPixelization(pixelizationFactor, bitmap);
2814ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        } else {
2824ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            return customImagePixelization(pixelizationFactor, bitmap);
2834ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
2844ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
2854ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2864ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    /**
2874ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * Implementation of the AsyncTask class showing how to run the
2884ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * pixelization algorithm in the background, and retrieving the
2894ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     * pixelated image from the resulting operation.
2904ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky     */
2914ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    private class PixelizeImageAsyncTask extends AsyncTask<Object, Void, BitmapDrawable> {
2924ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
2934ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        @Override
2944ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        protected BitmapDrawable doInBackground(Object... params) {
2954ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            float pixelizationFactor = (Float)params[0];
2964ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            Bitmap originalBitmap = (Bitmap)params[1];
2974ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            return pixelizeImage(pixelizationFactor, originalBitmap);
2984ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
2994ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
3004ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        @Override
3014ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        protected void onPostExecute(BitmapDrawable result) {
3024ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky            mImageView.setImageDrawable(result);
3034ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
3044ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
3054ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        @Override
3064ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        protected void onPreExecute() {
3074ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
3084ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
3094ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
3104ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        @Override
3114ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        protected void onProgressUpdate(Void... values) {
3124ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky
3134ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky        }
3144ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky    }
3154ea559806d2c4839e1c4019445c43c5fe41c9a0bDaniel Olshansky}