ihucos
ihucos

Reputation: 1861

image quality becomes bad after many drawBitmap's

my program draws a bitmap on the live wallpaper canvas. It works, but after some time the image becomes very bad (http://img855.imageshack.us/img855/9756/deviceq.png)

any ideas why?

package com.tripr;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Align;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;



/*
 * This animated wallpaper draws a rotating wireframe cube.
 */
public class MyWallpaperService extends WallpaperService{

    private final String TAG = "tripr";



    @Override
    public void onCreate() {
        super.onCreate();

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public Engine onCreateEngine() {
        return new CubeEngine(this);
    }


    class CubeEngine extends Engine  implements LocationListener{

        private MyWallpaperService mws;
        private float xOffset;
        private float xStep;
        private int xPixels;
        private String lastPhotoUrl = "";
        private LocationManager lm;
        private Bitmap bmp = null;
        private String locationName;
        private String status = "waiting for location update...";
        private final Handler mHandler = new Handler();

        private final Runnable mDrawBtmp = new Runnable() {
            public void run() {
                drawFrame();
            }
        };
        private boolean mVisible;


        CubeEngine(MyWallpaperService mymws) {
            mws = mymws;

            lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
            requestLocationUpdates();
            MyThread myThread = new MyThread(lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
            myThread.start();
        }


        //taken from http://p-xr.com/android-tutorial-how-to-parse-read-json-data-into-a-android-listview/
        private JSONObject getJSONfromURL(String url){

            //initialize
            InputStream is = null;
            String result = "";
            JSONObject jArray = null;

            //http post
            try{
                HttpClient httpclient = new DefaultHttpClient();
                HttpGet httpget = new HttpGet(url);
                HttpResponse response = httpclient.execute(httpget);
                HttpEntity entity = response.getEntity();
                is = entity.getContent();

            }catch(Exception e){
                Log.e(TAG, "Error in http connection "+e.toString());
            }

            //convert response to string
            try{
                BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"), 8);
                StringBuilder sb = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
                is.close();
                result=sb.toString();
            }catch(Exception e){
                Log.e(TAG, "Error converting result "+e.toString());
            }

            //try parse the string to a JSON object
            //Log.d(TAG, result);
            try{
                    jArray = new JSONObject(result);
            }catch(JSONException e){
                Log.e(TAG, "Error parsing data "+e.toString());
            }

            return jArray;
        }


        String getFlickrUrl(double lat, double lon, double radius){
            return "http://api.flickr.com/services/rest/?" +
                    "method=flickr.photos.search" +
                    "&api_key=a6d9db5ff2885dd2f8949590e7a44762" +
                    "&tags=architecture" +
                    "&lat=" + lat +
                    "&lon=" + lon +
                    "&radius=" + radius +
                    "&radius_units=km" +
                    "&extras=geo%2Curl_z%2Ctags" +
                    "&per_page=250" +
                    "&format=json" +
                    "&sort=interestingness-desc" +
                    "&nojsoncallback=1";
        }


        void requestLocationUpdates(){
            if (!(lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER))){
                status = "locating over network disabled";
                //bmp = null;
            }
            lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000*30, 10, this);
        }

        void removeUpdates(){
            lm.removeUpdates(this);
        }


        @Override
        public void onLocationChanged(Location location) {
            MyThread myThread = new MyThread(location);
            myThread.start();

        }

        @Override
        public void onProviderDisabled(String provider) {}

        @Override
        public void onProviderEnabled(String provider) {}

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {}


        @Override
        public void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacks(mDrawBtmp);
        }

        @Override
        public void onVisibilityChanged(boolean visible) {
            mVisible = visible;
            if (visible) {
                requestLocationUpdates();
                drawFrame();
            } else {
                mHandler.removeCallbacks(mDrawBtmp);
                removeUpdates();
            }
        }

        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            super.onSurfaceChanged(holder, format, width, height);
            drawFrame();
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
        }

        @Override
        public void onSurfaceDestroyed(SurfaceHolder holder) {
            super.onSurfaceDestroyed(holder);
            mVisible = false;
            mHandler.removeCallbacks(mDrawBtmp);
        }

        @Override
        public void onOffsetsChanged(float xOffset, float yOffset,
                float xStep, float yStep, int xPixels, int yPixels) {
            /*Log.d(TAG, "onOffsetsChanged");
            Log.d(TAG, "xOffset: " + String.valueOf(xOffset));
            Log.d(TAG, "yOffset: " + String.valueOf(yOffset));
            Log.d(TAG, "xStep: " + String.valueOf(xStep));
            Log.d(TAG, "yStep: " + String.valueOf(yStep));
            Log.d(TAG, "xPixels: " + String.valueOf(xPixels));
            Log.d(TAG, "yPixels: " + String.valueOf(yPixels));
            //Log.d(TAG, String.valueOf((xOffset / xStep)));
            Log.d(TAG, " ");*/

            this.xPixels = xPixels;
            this.xStep = xStep;
            this.xOffset = xOffset;
            drawFrame();
        }

        /*
         * Store the position of the touch event so we can use it for drawing later
         */
        @Override
        public void onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
        }

        /*
         * Draw one frame of the animation. This method gets called repeatedly
         * by posting a delayed Runnable. You can do any drawing you want in
         * here. This example draws a wireframe cube.
         */
        void drawFrame() {
            final SurfaceHolder holder = getSurfaceHolder();

            Canvas c = null;
            try {
                c = holder.lockCanvas();
                if (c != null) {
                    // draw something
                    drawBmp(c);
                }
            } finally {
                if (c != null) holder.unlockCanvasAndPost(c);
            }

            // Reschedule the next redraw
            mHandler.removeCallbacks(mDrawBtmp);
            if (mVisible) {
                mHandler.postDelayed(mDrawBtmp, 1000 / 5);
            }
        }

        private class MyThread extends Thread {
            Location loc;

            MyThread(Location loc){
                this.loc = loc;
            }

            public synchronized void run(){ // bringt synchronized was? weil wir kreieren ja immer eine neue intanz...

                    try{

                        if (loc == null){
                            Log.d(TAG, "location is null");
                            return;
                        }


         <SNIP>
         //the main code, update `status` and `bmp`
        }

        void drawBmp(Canvas canvas) {
            //canvas.restore();
            //canvas.save();
            //canvas.translate(0, 0);
            //canvas.drawColor(0xff00aa00);
            canvas.drawColor(Color.BLACK);

            if (bmp != null){
                //int width = (int) (((double)bmp.getHeight()/(double)canvas.getHeight())*canvas.getWidth());
                if (bmp.getHeight() != canvas.getHeight()){
                    Float width  = new Float(bmp.getWidth());
                    Float height = new Float(bmp.getHeight());
                    Float ratio = width/height;
                    Log.d(TAG, "scaling");
                    bmp = Bitmap.createScaledBitmap(bmp, (int)(canvas.getHeight()*ratio), canvas.getHeight(), true);
                }


                int x;
                if (bmp.getWidth() >= canvas.getWidth()){
                    x = -1*(int)((xOffset*(bmp.getWidth()-canvas.getWidth())));
                }else{
                    x = (bmp.getWidth()-canvas.getWidth())/2;
                }
                //Log.d(TAG,  String.valueOf(scale));
                //Log.d(TAG,  String.valueOf(canvas.getWidth()));
                //Log.d(TAG, " ");
                canvas.drawBitmap(bmp, x, 0, null);


                //canvas.drawLine(0, 0, bmp.getWidth(), 0, new Paint());

            }else if(status != null){
                Paint textPaint = new Paint();
                textPaint.setColor(Color.WHITE);
                textPaint.setAntiAlias(true);
                textPaint.setTextSize(25);
                textPaint.setAlpha(120);
                textPaint.setTextAlign(Align.CENTER);
                canvas.drawText(status, canvas.getWidth()/2, canvas.getHeight()/2, textPaint);
            }
            if (locationName != null){
                Paint textPaint = new Paint();
                textPaint.setColor(Color.BLACK);
                textPaint.setAntiAlias(true);
                textPaint.setTextSize(30);
                textPaint.setAlpha(150);
                textPaint.setTextAlign(Align.LEFT);
                Rect rect = new Rect();
                textPaint.getTextBounds(locationName, 0, locationName.length(), rect);
                //Log.d(TAG, String.valueOf(textPaint.getTextSize()));
                canvas.drawText(locationName, 0, 70, textPaint);
            }

        }
    }
}

Upvotes: 1

Views: 552

Answers (1)

Nik
Nik

Reputation: 2414

In drawBmp() you repeatedly take your bitmap (bmp), scale it (the call to createScaledBitmap) and then assign it back to bmp. Over time all these scaling operations are going to result in the artifacts that you see.

To solve this, store the original bitmap in a different variable (e.g., private Bitmap originalImage;) and create the scaled bitmap from original.

bmp = Bitmap.createScaledBitmap(originalImage, (int)(canvas.getHeight()*ratio), canvas.getHeight(), true);

Upvotes: 3

Related Questions