Подтвердить что ты не робот

Canvas.drawBitmap() прерывисто замедляется, вызывая белые вспышки

Я работаю над живыми обоями с прокруткой. У меня есть два объекта растровых изображений, которые я чередую, чтобы сохранить ранее нарисованные пиксели для следующего кадра. Я рисую новую строку в верхней части холста, а затем вызываю drawBitmap, чтобы скопировать остальные пиксели на холст.

Я использую объект Runnable для тяжелого подъема. Он выполняет все операции копирования и вычисления, а затем блокирует холст, вводит синхронный блок на держателе и делает один вызов Canvas.drawBitmap(растровое изображение, прямоугольник, прямоугольник, краска). Иногда на экране появляется белая вспышка, которая, похоже, коррелирует с высокой активностью процессора. При использовании traceview я обнаружил, что операция drawBitmap, в частности Canvas.native_drawBitmap(), занимает гораздо больше времени, чем обычно. Обычно он заканчивается в 2-4 мсек, но когда я вижу белую вспышку, она может занять от 10 до 100 мсек.

private void draw() {
    SurfaceHolder holder = getSurfaceHolder();

    Canvas canvas = null;
    prepareFrame();
    try {
        canvas = holder.lockCanvas();
        synchronized (holder) {
            if (canvas != null) {
                drawFrame(canvas);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (canvas != null)
            holder.unlockCanvasAndPost(canvas);
    }
    afterDrawFrame();
    handler.removeCallbacks(drawRunner);
    if (visible) {
        handler.post(drawRunner);
    }
}

Функция draw() вызывается в run() Runnable.

private void prepareFrame() {
    num++;
    if (num%2 == 0) {
        mainBmp = mainBmp1;
        mainCan.setBitmap(mainBmp1);
        mainCan.drawBitmap(mainBmp2, source, destination, null);
    } else {
        mainBmp = mainBmp2;
        mainCan.setBitmap(mainBmp2);
        mainCan.drawBitmap(mainBmp1, source, destination, null);
    }
}

Функция prepareFrame() заключается в том, как я сохраняю предыдущие пиксели, которые я нарисовал. Rect, называемый источником, - это одна строка, которая меньше полного размера экрана внизу, где в качестве места назначения находится одна строка в верхней части. Вызов drawBitmap() в prepareFrame() не превышает 2-4 мсек.

private void drawFrame(Canvas can) {
    can.drawBitmap(mainBmp, source, destination,null);
}

Эта одиночная операция выполняется на холсте, удерживая блокировку.

private void afterDrawFrame() {
    ca.calcNextRow();
    mainBmp.setPixels(ca.getRow(), 0, canWidth, 0, 0, canWidth, 1);
}

Затем следующая новая строка пикселей нарисована на одном из моих растровых изображений в памяти.

Я попытался использовать различные подписи drawBitmap(), но только нашел их более медленными в среднем и все еще приводят к аномальным белым вспышкам.

Моя общая скорость отличная. Без прерывистых вспышек он работает очень хорошо. У кого-нибудь есть предложения по устранению вспышек?

4b9b3361

Ответ 1

Трудно точно узнать, что происходит здесь, потому что вы не включаете определение или использование некоторых центральных переменных, таких как "mainCan" или "ca". Более полная ссылка на источник будет отличной.

Но...

Что, вероятно, происходит, так как drawFrame (холст) синхронизируется на держателе, но

handler.post(drawRunner);

нет, будут случаи, когда вы пытаетесь нарисовать mainBmp на системном холсте одновременно с тем, как вы пишете в него в файле prepareFrame().

Лучшим решением этой проблемы, вероятно, будет какая-то двойная буферизация, где вы делаете что-то вроде

1) Write to a temporary bitmap
2) Change the ref of that bitmap to the double buffer i.e. mainBmp = tempBitmap;

Основная цель - никогда не записывать длинные записи на переменные, которые вы используете для рендеринга системного холста, просто измените ссылку на объект.

Надеюсь, что это поможет.