Лучшая частота кадров при рисовании растровых изображений на холсте? - PullRequest
4 голосов
/ 29 мая 2011

Я снимаю для анимации в живых обоях.Вот кодЭто в значительной степени следует демонстрации CubeWallpaper:

    void drawFrame() {
        final SurfaceHolder holder = getSurfaceHolder();
    final BufferedInputStream buf;
    final Bitmap bitmap, rbitmap;

        Canvas c = null;
        try {
            c = holder.lockCanvas();
            if (c != null) {
        try {
        buf = new 
            BufferedInputStream(assets.
                    open(folder+"/"
                         +imageList[ilen++])
                    );
        bitmap = BitmapFactory.
            decodeStream(buf);
        rbitmap = Bitmap.createBitmap
            (bitmap,
             0,0,imageWidth,imageHeight,
             transMatrix,false);
        c.drawBitmap(rbitmap,
                 paddingX,
                 paddingY,
                 null);
        if ( ilen >= imageCount ) ilen=0;
        }
        catch (Exception e) { e.printStackTrace(); }
            }
        } finally {
            if (c != null) holder.unlockCanvasAndPost(c);
        }

        // Reschedule the next redraw
        mHandler.removeCallbacks(mDrawCube);
        if (mVisible) {
            mHandler.postDelayed(mDrawCube, fps);
        }
    }

, где "transMatrix" - это матрица масштабирования и поворота, предопределенная ранее.

Предполагается, что она будет отображаться со скоростью 30 кадров в секунду, но, конечно, это не так.тот.Мое первоначальное предположение, что BufferedInputStream является одним из факторов.Я, вероятно, должен кэшировать некоторые из них, поскольку я согласен с растровыми изображениями.Но есть ли другие идеи?Придется ли мне использовать взлом OpenGL для живых обоев?

Ответы [ 2 ]

4 голосов
/ 29 мая 2011

Да, BufferedInputStream и BitmapFactory действительно не должны быть в drawFrame() вообще.Вы загружаете и создаете ресурсы для каждого отдельного кадра, и это огромная трата.Как вы сказали, предварительно кэшируйте столько, сколько сможете, и, если вы обнаружите необходимость загружать больше во время рисования, используйте для этого отдельный поток, чтобы он не замедлял рисование.

3 голосов
/ 25 июня 2012

У меня была та же проблема: медленный рендеринг холста в контексте живых обоев.

Я согласен с другими, говоря, что вы не должны делать какие-либо тяжелые CPU / IO при рендеринге, например, при загрузке изображений, особенно в потоке пользовательского интерфейса.,

Однако есть еще одна вещь, на которую стоит обратить внимание.Вы запрашиваете перерисовку (mHandler.postDelayed (...)) ПОСЛЕ отображения кадра.Если вы хотите 30 кадров в секунду и, таким образом, запрашиваете перерисовку в (1000/30) 33 мс, это НЕ приведет к 30 кадрам в секунду.Почему?

Предположим, что для рендеринга всех ваших вещей на холст требуется 28 мс.После того, как это сделано, вы запрашиваете перерисовку через 33 миллис.То есть период между перерисовками составляет 61 мс, что равно 16 кадрам в секунду.

У вас есть два варианта решения:

1) Поместите mHandler.postDelayed (...)код начала метода drawFrame (...).Это выглядит нормально, но у него есть некоторые недостатки: если ваш фактический FPS очень близок к максимально возможному FPS на реальном устройстве - другими словами, поток пользовательского интерфейса все время занят рендерингом холста - тогда не будет времени дляпоток пользовательского интерфейса, чтобы делать другие вещи.Это не обязательно означает, что ваш LWP или домашний экран будут отставать, но вы (ваш LWP) можете начать пропускать некоторые сенсорные события (как это сделал мой LWP).

2) Лучшее решение - запустить отдельныйнить, когда поверхность создана, и передайте ей ссылку на SurfaceHolder.Сделайте рендеринг в этой отдельной теме.Метод визуализации в этой теме будет выглядеть так:

private static final int DESIRED_FPS = 25;
private static final int DESIRED_PERIOD_BETWEEN_FRAMES_MS = (int) (1000.0 / DESIRED_FPS + 0.5);



@Override
public void run() {
    while (mRunning) {
        long beforeRenderMs = SystemClock.currentThreadTimeMillis();            
        // do actual canvas rendering
        long afterRenderMs = SystemClock.currentThreadTimeMillis();
        long renderLengthMs = afterRenderMs - beforeRenderMs;
        sleep(Math.max(DESIRED_PERIOD_BETWEEN_FRAMES_MS - renderLengthMs, 0));
    }
}
...