android比例位图性能

时间:2015-06-29 14:21:17

标签: android canvas bitmap

我有这个:

paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);

sm = new Matrix();
sm.setScale(scale, scale);

private Bitmap getImage(String n) {
    File dir = context.getDir("theme", Context.MODE_PRIVATE);
    File file = new File(dir, n + ".png");
    if (file.exists()) {
       return BitmapFactory.decodeFile(file.getAbsolutePath());
    } else {
        return BitmapFactory.decodeResource(getResources(), getResources().getIdentifier(n, "drawable", getPackageName()));
    }
}

private Bitmap resizeImage(Bitmap b) {
    return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), sm, true);
}

public void onTouchEvent(MotionEvent event) {
    #scrollable bitmaps, parallax effect
    updatePosition();
}

private void draw() {
    current_time = System.currentTimeMillis();
    if (current_time - last_update_time >= 25) {
        SurfaceHolder holder = getSurfaceHolder();
        Canvas c = null;
        try {
            c = holder.lockCanvas();
            if (c != null) {
                c.drawBitmap(bitmap1, bitmap1_x, bitmap1_y, paint);
                c.drawBitmap(bitmap2, bitmap2_x, bitmap2_y, paint);
                ...
                c.drawBitmap(bitmap20, bitmap20_x, bitmap20_y, paint);
            }
        } finally {
            if (c != null)
                holder.unlockCanvasAndPost(c);
        }
        last_update_time = current_time;
    }
}

我正在将图像调整为更大的尺寸,而不是更小。

  1. 没有调整大小,效果非常好,性能100%

    bitmap1 = getImage("bitmap1"); ... bitmap20 = getImage("bitmap20");

  2. 通过调整大小,性能80%

    bitmap1 = getImage("bitmap1"); ... bitmap20 = getImage("bitmap20");

    called once, when screen width and height are known bitmap1 = resizeImage(bitmap1); ... bitmap20 = resizeImage(bitmap20);

  3. 不调整大小,画布比例,性能40%

    bitmap1 = getImage("bitmap1"); ... bitmap20 = getImage("bitmap20");

    set canvas.scale(scale, scale) inside draw() method

  4. 我知道有一些框架像libgdx调整图像大小而不会失去性能,但我使用的是原生画布。

    问题:如何以100%的效果绘制已调整大小的图像?

    更新 试图制作最小样本。

    mWallpaperService.java

    import android.app.ActivityManager;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.content.SharedPreferences;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.graphics.Shader;
    import android.os.BatteryManager;
    import android.os.Handler;
    import android.preference.PreferenceManager;
    import android.service.wallpaper.WallpaperService;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.SurfaceHolder;
    import android.view.View;
    import android.view.WindowManager;
    import android.widget.LinearLayout;
    import android.widget.Scroller;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.Calendar;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class mWallpaperService extends WallpaperService {
    
        @Override
        public Engine onCreateEngine() {
            return new mEngine();
        }
    
        private class mEngine extends Engine {
            private final Handler handler = new Handler();
            private final Runnable drawRunner = new Runnable() {
                @Override
                public void run() {
                    draw();
                }
    
            };
    
            private Context context;
            private Paint paint;
            private boolean visible = true;
            private boolean draw = true;
    
            private int width, height;
            private float scale;
    
            private float begin_x, move_x;
            private int touch_cnt = 0;
    
            private int bg_max_x;
            private Bitmap bg1, bg2, bg3, bg4..., bg20;
            private float bg1_x, bg1_x2, bg2_x, bg3_x, bg4_x..., bg20_x;
            private float bg1_y, bg2_y, bg3_y, bg4_y..., bg20_y;
            private float bg2_pr, bg3_pr, bg3_pr..., bg20_pr;
            private float bg1_offset_x, bg2_offset_x, bg3_offset_x, bg4_offset_x..., bg20_offset_x;
    
    
            private long current_time;
            private long last_update_time;
            private Matrix sm;
            Scroller mScroller;
    
            public mEngine() {
                context = getApplicationContext();
                mScroller = new Scroller(context);
    
                bg1 = getImage("bg1");
                bg2 = getImage("bg2");
                bg3 = getImage("bg3");
                bg4 = getImage("bg4");
                ...
                bg20 = getImage("bg20");
    
                handler.post(drawRunner);
            }
    
            @Override
            public void onVisibilityChanged(boolean visible) {
                this.visible = visible;
                if (visible) {
                    draw = true;
                    handler.post(drawRunner);
                } else {
                    draw = false;
                    handler.removeCallbacks(drawRunner);
                }
            }
    
    
            @Override
            public void onSurfaceDestroyed(SurfaceHolder holder) {
                super.onSurfaceDestroyed(holder);
                this.visible = false;
                handler.removeCallbacks(drawRunner);
            }
    
            @Override
            public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                if (this.width != width && this.height != height) {
                    scale = (float) height / bg1.getHeight();
                    this.width = width;
                    this.height = height;
    
                    sm = new Matrix();
                    sm.setScale(scale, scale);
    
                    bg1 = resizeImage(bg1);
                    bg2 = resizeImage(bg2);
                    bg3 = resizeImage(bg3);
                    bg4 = resizeImage(bg4);
                    ...
                    bg20 = resizeImage(bg20);
    
                    bg_max_x = bg1.getWidth() - width;
                    bg1_x = bg_max_x / 2;
                    bg1_y = 0;
    
                    #scroll_speed getting from preferences, 0.1f - 1f
                    scroll_length = bg_max_x * scroll_speed;
    
                    mScroller.setFinalX((int) bg1_x);
                    mScroller.abortAnimation();
    
                    bg2_pr = 0.2f;
                    bg2_offset_x = width / 2 - bg2.getWidth() / 2 + bg1_x * bg2_pr;
                    bg2_y = height - bg2.getHeight();
    
                    bg3_pr = 0.3f;
                    bg3_offset_x = width / 2 - bg3.getWidth() / 2 + bg1_x * bg3_pr;
                    bg3_y = height - bg3.getHeight();
    
                    bg4_pr = 0.4f;
                    bg4_offset_x = width / 2 - bg4.getWidth() / 2 + bg1_x * bg4_pr;
                    bg4_y = height - bg4.getHeight();
                    ...
    
                    updatePosition();
                }
    
                super.onSurfaceChanged(holder, format, width, height);
            }
    
            private void updatePosition() {
                bg2_x = bg2_offset_x - bg1_x * bg2_pr;
                bg3_x = bg3_offset_x - bg1_x * bg3_pr;
                bg4_x = bg4_offset_x - bg1_x * bg4_pr;
                ...
                bg20_x = bg20_offset_x - bg1_x * bg20_pr;
            }
    
            @Override
            public void onTouchEvent(MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        begin_x = event.getX();
                        move_x = 0;
                        touch_cnt = 0;
                        break;
                    case MotionEvent.ACTION_UP:
                        x = event.getX();
                        if (touch_cnt >= 1)
                            fling();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        x = event.getX();
                        touch_cnt++;
                        //TODO drag
                        break;
                }
            }
    
            private boolean fling() {
                if (!mScroller.isFinished()) {
                    mScroller.forceFinished(true);
                }
                if (move_x != 0) {
                    bg1_x2 = mScroller.getCurrX() + move_x;
                    if (bg1_x2 <= 0) {
                        bg1_x2 = 0;
                    } else if (bg1_x2 > bg_max_x) {
                        bg1_x2 = bg_max_x;
                    }
    
                    if (bg1_x != bg1_x2) {
                        mScroller.fling(mScroller.getCurrX(), (int) bg1_y, -(int) (bg1_x > bg1_x2 ? 10000 : -10000), 0, mScroller.getCurrX() - scroll_length <= 0 ? 0 : (int) (mScroller.getCurrX() - scroll_length), mScroller.getCurrX() + scroll_length >= bg_max_x ? bg_max_x : (int) (mScroller.getCurrX() + scroll_length), 0, bg1.getHeight());
                        return true;
                    }
                }
                return false;
            }
    
            private void draw() {
                current_time = System.currentTimeMillis();
    
                if (mScroller.computeScrollOffset()) {
                    bg1_x = mScroller.getCurrX();
                    updatePosition();
                    draw = true;
                }
    
                if (draw && current_time - last_update_time >= 25) {
                    SurfaceHolder holder = getSurfaceHolder();
                    Canvas c = null;
                    try {
                        c = holder.lockCanvas();
                        if (c != null) {
                            c.drawBitmap(bg1, -bg1_x, bg1_y, null);
                            c.drawBitmap(bg2, bg2_x, bg2_y, null);
                            c.drawBitmap(bg3, bg3_x, bg3_y, null);
                            c.drawBitmap(bg4, bg4_x, bg4_y, null);
                            ...
                            c.drawBitmap(bg20, bg20_x, bg20_y, null);
                        }
                    } finally {
                        if (c != null)
                            holder.unlockCanvasAndPost(c);
                    }
                    last_update_time = current_time;
                    draw = false;
                }
    
                handler.removeCallbacks(drawRunner);
                if (visible) {
                    handler.postDelayed(drawRunner, 1);
                }
            }
    
    
            private Bitmap resizeImage(Bitmap b) {
                return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), sm, true);
            }
    
            private Bitmap getImage(String n) {
                File dir = context.getDir("theme", Context.MODE_PRIVATE);
                File file = new File(dir, n + ".png");
                if (file.exists()) {
                    return BitmapFactory.decodeFile(file.getAbsolutePath());
                } else {
                    return BitmapFactory.decodeResource(getResources(), getResources().getIdentifier(n, "drawable", getPackageName()));
                }
            }
    
            @Override
            public void onDestroy() {
                bg1.recycle();
                bg2.recycle();
                ...
                bg20.recycle();
            }
    
        }
    }
    

    MainActivity.java

    import android.app.Activity;
    import android.app.WallpaperManager;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    
    
    public class MainActivity extends Activity {
        Intent service;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            service = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
            service.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, new ComponentName(this, mWallpaperService.class));
            service.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    
            startActivity(service);
            finish();
        }
    
        #button click
        public void openService(View view) {
            startActivity(service);
            finish();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
        }
    }
    

3 个答案:

答案 0 :(得分:2)

尝试Canvas.drawBitmap (Bitmap bitmap, null, Rect dst, Paint paint)

&#34; dst&#34;的大小rect将确定比例,将调整位图以适应它。这就是原生ImageView使用的内容,因此它应该非常快。

答案 1 :(得分:2)

看起来性能下降来自Bitmap.createBitmap(CPU调整大小)。我认为对于你的情况,没有理由使用Bitmap.createBitmap调整Bitmap的大小。相反,你应该通过GPU来实现。

调整大小的代码,Bitmap.createBitmap正在调整CPU位图大小:为新位图分配内存 - 从旧位图进行插值,填入新的位图 - 全部由CPU完成。

更好的方法是保持位图而不从CPU调整大小。相反,将整个位图加载到GPU中,并告诉GPU调整它的大小。例如,使用:

  

drawBitmap(位图位图,矩阵矩阵,油漆颜料)

     

使用指定的矩阵绘制位图。

您可以使用矩阵参数作为正在绘制的位图的调整大小矩阵。

最后,如果您有内存使用问题(例如,原始位图太大),您可以在将其加载到内存时将其缩小(使用Bitmap.createBitmap,CPU resize)一次。并且无需再次在onSurfaceChanged中调整大小(您可能需要重新计算调整大小矩阵)。

答案 2 :(得分:1)

如果我确实理解了您的问题,那么我认为此链接可能有所帮助:https://stackoverflow.com/a/4250279/2378691