Установите коэффициент масштабирования отображаемого изображения на холсте Android

Я пытаюсь написать приложение для Android, которое позволяет мне рисовать графику поверх изображения, а затем масштабировать и масштабировать изображение, при этом графика остается на том же месте на изображении, которое было нарисовано поверх него, при изменении графики в режиме реального времени. .

Однако у меня было много проблем с увеличением масштаба при сохранении центра изображения. Я написал код, в котором у меня есть поток, который обновляет изображение. Обновления передаются с использованием класса, который я создал под названием «PendingUpdate» через ArrayBlockingQueue. Это обновление содержит желаемый уровень масштабирования, который должен быть отношением пикселей изображения к пикселям холста и центру изображения. Однако следующий код заставляет его панорамировать, пока я масштабирую, что меня смущает.

//Scale the image
canvas.scale(pendingUpdate.getZoom(), pendingUpdate.getZoom());

//Translate the image
double updateCx = pendingUpdate.getCenter().getX();
double updateCy = pendingUpdate.getCenter().getY();
double halfCanvasWidthInImagePixels = pendingUpdate.getZoom()*(canvas.getWidth()/2);
double halfCanvasHeightInImagePixels = pendingUpdate.getZoom()*(canvas.getHeight()/2);
double imageTranslateX = updateCx - halfCanvasWidthInImagePixels;
double imageTranslateY = updateCy - halfCanvasHeightInImagePixels;
canvas.translate(-(float)imageTranslateX, -(float)imageTranslateY);
canvas.drawBitmap(pendingUpdate.getImage(), matrix, new Paint());

Спасибо вам за помощь!

Изменить: вот полная функция, я также могу опубликовать PendingUpdate, если это поможет, однако это просто класс данных.

    private void doDraw(Canvas canvas, PendingUpdate pendingUpdate) {
        int iWidth = pendingUpdate.getImage().getWidth();
        int iHeight = pendingUpdate.getImage().getHeight();
        Matrix matrix = new Matrix();

        canvas.drawColor(Color.BLACK);
        //TODO: add scrolling functionality to this
        if(pendingUpdate.getZoom()>0) {

            //Scale the image
            canvas.scale(pendingUpdate.getZoom(), pendingUpdate.getZoom());

            //Translate the image
            double updateCx = pendingUpdate.getCenter().getX();
            double updateCy = pendingUpdate.getCenter().getY();
            double halfCanvasWidthInImagePixels = pendingUpdate.getZoom()*(canvas.getWidth()/2);
            double halfCanvasHeightInImagePixels = pendingUpdate.getZoom()*(canvas.getHeight()/2);
            double imageTranslateX = updateCx - halfCanvasWidthInImagePixels;
            double imageTranslateY = updateCy - halfCanvasHeightInImagePixels;
            canvas.translate(-(float)imageTranslateX, -(float)imageTranslateY);
            canvas.drawBitmap(pendingUpdate.getImage(), matrix, new Paint());

        }else {
            //matrix.postTranslate(canvas.getWidth()-iWidth/2, canvas.getWidth()-iHeight/2);
            canvas.drawBitmap(pendingUpdate.getImage(),
                    (canvas.getWidth()-iWidth)/2,
                    (canvas.getHeight()-iHeight)/2, null);
        }
        //TODO: draw other stuff on canvas here such as current location

    }

редактировать 2: Вот как я наконец заставил его работать, это был просто вопрос масштабирования перед переводом.

    private void doDraw(Canvas canvas, PendingUpdate pendingUpdate) {
        int iWidth = pendingUpdate.getImage().getWidth();
        int iHeight = pendingUpdate.getImage().getHeight();

        canvas.drawColor(Color.BLACK);
        //TODO: add scrolling functionality to this
        if(pendingUpdate.getZoom()>0) {

            //Scale the image
            canvas.save();
            double updateCx = pendingUpdate.getCenter().getX();
            double updateCy = pendingUpdate.getCenter().getY();
            double halfCanvasWidthInImagePixels = (canvas.getWidth()/2);
            double halfCanvasHeightInImagePixels = (canvas.getHeight()/2);
            double imageTranslateX = updateCx - halfCanvasWidthInImagePixels;
            double imageTranslateY = updateCy - halfCanvasHeightInImagePixels;
            //canvas.scale(pendingUpdate.getZoom(), pendingUpdate.getZoom(), (float)pendingUpdate.getCenter().getX(), (float)pendingUpdate.getCenter().getY());

            canvas.scale(pendingUpdate.getZoom(),
                    pendingUpdate.getZoom(),
                    canvas.getWidth()/2,
                    canvas.getHeight()/2);

            canvas.translate(-(float)imageTranslateX,
                    -(float)imageTranslateY);
            canvas.drawBitmap(pendingUpdate.getImage(), 0, 0, null);

            canvas.restore();
        }else {
            //TODO: update this so it displays image scaled to screen and updates current zoom somehow
            canvas.drawBitmap(pendingUpdate.getImage(),
                    (canvas.getWidth()-iWidth)/2,
                    (canvas.getHeight()-iHeight)/2, null);
        }
        //TODO: draw other stuff on canvas here such as current location    
    }
}

person Shawn    schedule 03.02.2013    source источник
comment
что такое matrix? где определяется? пожалуйста, покажите больше кода.   -  person andr    schedule 04.02.2013
comment
Спасибо за ответ, я обновил полное определение функции. Matrix — это пустая матрица, которую требует функция drawBitmap. Я попытался применить преобразования к этой матрице вместо холста, и, похоже, он делает то же самое.   -  person Shawn    schedule 04.02.2013
comment
Я также должен добавить, что значение pendingUpdate.center вообще не изменяется, а pendingUpdate.zoom корректируется в диапазоне от 1 до 10.   -  person Shawn    schedule 04.02.2013


Ответы (1)


На вашем месте я бы использовал метод Canvas.scale(float sx, float sy, float px, float py), который делает именно то, что вы хотите.

Однако, глядя на ваш код, я думаю, что вы, возможно, возитесь со слишком большим количеством преобразований одновременно, что сложнее отлаживать.

  1. Всегда (и я имею в виду всегда) вызывайте Canvas.save() и Canvas.restore() для исходной матрицы, которую вы получаете в Canvas, если вы планируете ее изменить. Это потому, что Canvas, на котором вы можете рисовать, может быть холстом, например. все окно только с отсечением, установленным на границах элемента управления, который в настоящее время рисует себя.

  2. Используйте метод матричного преобразования, предоставляемый методом Canvas, и нарисуйте растровое изображение, используя простейший вызов.

Следуя этим двум советам, посмотрите на весь View, который я только что составил, который масштабирует bitmap в 3 раз с точкой (16,16), установленной в качестве точки поворота (неизменная точка - центр масштабирования). Проверено - работает.

public class DrawingView extends View {
    Bitmap bitmap;

    public DrawingView(Context context) {
        super(context);
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        float sx = 3;
        float sy = 3;
        float px = 16;
        float py = 16;
        canvas.save();
        canvas.scale(sx, sy, px, py);
        canvas.drawBitmap(bitmap, 0, 0, null);
        canvas.restore();
    }
}
person andr    schedule 03.02.2013
comment
Спасибо за информацию, я значительно сократил свой код и считаю, что теперь он работает. Я не уверен, что понимаю, что делают canvas.save() и restore(). Это не зависит от блокировки, разблокировки и публикации? - person Shawn; 04.02.2013
comment
save и restore помещают текущую матрицу холста в стек, подобно тому, как это делается в OpenGL. дело в том, что если вы абсолютно не уверены, что после вас на холст ничего не рисуется, вам нужно восстановить матрицу в то состояние, в котором она была, когда вы начали рисовать. это потому, что холст, на который вы рисуете, скорее всего, используется всеми представлениями в вашем текущем окне. вы можете проверить это, просто вызвав canvas.setMatrix(null); для загрузки матрицы идентичности и просмотра (странных) результатов: › - person andr; 04.02.2013
comment
Это имеет смысл. Следующий вопрос, как настроить центрирование на заданную точку и увеличить ее. Если я устанавливаю точку поворота в центр изображения, оно поворачивается туда, но холст по-прежнему центрируется в том же месте, поэтому он просто пролетает, если я пытаюсь перевести его, то при увеличении масштаба кажется, что он делает странные вещи. - person Shawn; 04.02.2013
comment
что вы имеете в виду под холстом, который все еще сосредоточен на месте и пролетает мимо? Я не могу понять, чего вы пытаетесь достичь. можешь нарисовать и выложить? или отредактируйте вопрос и включите описание операции в стиле ascii или в матричном стиле, например: output = T S input, где T – это bla, а S – это bla и т. д. - person andr; 04.02.2013
comment
Итак, у меня есть изображение, которое я пытаюсь отобразить, я передаю его через pendingUpdate.getImage(). Я также обозначил точку на изображении в обновлении в пикселях, которую я хочу разместить в центре дисплея с помощью pendingUpdate. получитьцентр(). У меня есть уровень масштабирования, который определяет, насколько я хочу увеличить масштаб этой точки. Я просто не понимаю, как меняется точка поворота, когда я пытаюсь перевести изображение в центр, я думаю. - person Shawn; 04.02.2013
comment
Я знаю, я понимаю это из рассматриваемого кода, но я не понимаю, зачем вы хотите перевести изображение в конце. scale(sx,sy) вокруг точки (px,py) делается просто серией: translate(px,py); scale(sx,sy); translate(-px,-py); под капотом. просто возьмите любое руководство по преобразованию матриц, например: people.bath.ac.uk/sej20/transform .html, чтобы понять, как комбинировать основные операции для получения желаемого эффекта. - person andr; 04.02.2013
comment
давайте продолжим это обсуждение в чате - person andr; 04.02.2013
comment
Спасибо за помощь. Я прочитаю это, пройдусь по коду и посмотрю, смогу ли я понять, что происходит. Я думаю, что у меня может быть просто мой мыслительный процесс не в порядке. - person Shawn; 04.02.2013
comment
УУУУУ! Наконец-то я заработал. Спасибо за помощь! Я опубликовал то, что наконец написал. Я думаю, нужно было просто выяснить, какие координаты он искал и в каком порядке это делать. - person Shawn; 04.02.2013
comment
хорошо, я рад, что у вас все получилось. о, и спасибо, что поделились решением, есть вероятность, что кто-то когда-нибудь извлечет из этого пользу :) удачного кодирования! :) - person andr; 04.02.2013