Постоянно прокручивать одно большое бесшовное изображение в любом направлении. - PullRequest
3 голосов
/ 09 сентября 2011

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

У меня бесшовное изображение больше экрана. Примерно в два раза больше. Цель состоит в том, чтобы это изображение можно было прокручивать в любом направлении на поверхности. Это для Android.

Я видел несколько вопросов / ответов, касающихся изображений в целом, но не бесшовных изображений.

Разница здесь, и вопрос, который у меня возникает, заключается в том, как обрабатывать прокрутку, когда в некоторых точках может отображаться 4 отдельных фрагмента изображения (когда пользователь прокручивает изображение по диагонали наполовину).

Довольно очевидно, как это сделать, когда вы хотите прокрутить изображение от края до края. Используйте мировое значение и прямоугольник для отображения своего рода «области просмотра» того места, где вы находитесь.

Что-то вроде:

Rect oRect1 = new Rect(dWorldX, dWorldY, dWorldX + canvas.getWidth(), dWorldY + canvas.getHeight());
Rect oRect2 = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

canvas.drawBitmap(largeBitmapTexture, oRect1, oRect2, new Paint());

Но когда растровое изображение непрерывно и непрерывно прокручивается, для меня это становится проблемой программирования. Я использую кучу операторов if? Есть ли более эффективный способ сделать что-то подобное?

править: Еще несколько мыслей. Одна сложность здесь в том, что мир (x, y) бесконечно велик. Из-за этого мне приходится использовать деления ширины и высоты экрана, чтобы отобразить то, что нужно отобразить. Первоначально я думал о том, чтобы использовать 4 итерации Rect, поскольку на экране никогда не было бы более 4 фрагментов изображения. Если это имеет смысл. Я думаю, я немного размышляю о том, как рассчитать.

edit: Вот код, который у меня сейчас есть, с предложениями от @glowcoder.

public void draw(Canvas canvas){
    int iImagesOverX=0;
    int iImagesOverY=0;

    iImagesOverX = (int)dX / mCloud.getIntrinsicWidth();
    iImagesOverY = (int)dY / mCloud.getIntrinsicHeight();
    mCloud.setBounds(iImagesOverX * (int)mCloud.getIntrinsicWidth() , iImagesOverY * (int)mCloud.getIntrinsicHeight() , (iImagesOverX * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  (iImagesOverY * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    Log.d(TAG, "bounds:"+ mCloud.getBounds());
    mCloud.draw(canvas);
    mCloud.setBounds((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth() , iImagesOverY * (int)mCloud.getIntrinsicHeight() , ((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  (iImagesOverY * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    mCloud.draw(canvas);
    mCloud.setBounds((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth() , (iImagesOverY + 1)* (int)mCloud.getIntrinsicHeight() , ((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  ((iImagesOverY + 1) * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    mCloud.draw(canvas);
    mCloud.setBounds((iImagesOverX) * (int)mCloud.getIntrinsicWidth() , (iImagesOverY + 1)* (int)mCloud.getIntrinsicHeight() , ((iImagesOverX) * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  ((iImagesOverY + 1) * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    mCloud.draw(canvas);
    Log.d(TAG, "imagesoverx:"+ iImagesOverX);
}

Мне пришлось добавить dX и dY к границам, чтобы заставить изображения двигаться. Однако после того, как вы направите одну «плитку» вправо, третья плитка не появится.

Так что это частично работает, но не совсем там, где мне нужно быть. Есть 4 панели, но только из-за 4 экземпляров границ и рисования. Мне нужно, чтобы эти 4 панели нарисовали там, где они должны быть, независимо от того, где мы находимся с x и y.

Ответы [ 3 ]

4 голосов
/ 09 сентября 2011

Основная идея заключается в том, что у вас есть бесконечная плоскость, в которой ваше изображение повторяется снова и снова во всех направлениях. И тогда у вас есть «окно просмотра», которое вы можете рассматривать как окно, ограничивающее ваш взгляд на бесконечную плоскость. Другими словами, область бесконечной плоскости, которая находится «под» областью просмотра, - это то, что фактически отображается на экране.

Таким образом, когда верхний левый угол вашего видового экрана находится в точке (0,0), вы видите часть бесконечной плоскости от (0,0) до (50,50) (при условии, что ваш видовой экран имеет 50 пикселей по ширине и высоте). ). Точно так же, если ваша область просмотра находится в (238 753), то вы видите часть бесконечной плоскости от (238, 753) до (288, 803).

Если ваши изображения 100х100, то ваша бесконечная плоскость будет выглядеть примерно так:

     (-100,-100)  (0,-100)      (100,-100)    (200,-100)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,0)    *(0,0)        *(100,0)      *(200,0)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,100)  *(0,100)      *(100,100)    *(200,100)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,200)  *(0,200)      *(100,200)    *(200,200)
    ******************************************

Теперь предположим, что в левом верхнем углу окна просмотра находится 75,75. Графически это будет выглядеть примерно так:

     (-100,-100)  (0,-100)      (100,-100)    (200,-100)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,0)    *(0,0)        *(100,0)      *(200,0)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *     (75,75) * (125,75)    *
    *            *          #######          *
    *(-100,100)  *(0,100)   #  *  #          *(200,100)
    ************************#*****#***********
    *            *          #  *  #          *
    *            *          #######          *
    *            *    (75,125) * (125,125)   *
    *            *             *             *
    *(-100,200)  *(0,200)      *(100,200)    *(200,200)
    ******************************************              

В этом случае ваш видовой экран имеет углы в (75,75), (75,125), (125,75) и (125,125). Таким образом, вы видите нижний правый угол изображения с (0,0), нижний левый угол изображения с (100,0) и т. Д.

Теперь предположим, что вы реализовали функцию, которая вычисляет, в какой сетке находится конкретная точка. В частности, она возвращает верхний левый угол сетки, в которой находится эта точка. Несколько примеров:

gridContainingPoint(0,0)      -> (0,0)
gridContainingPoint(50,50)    -> (0,0)
gridContainingPoint(100,100)  -> (100,100)
gridContainingPoint(123, -27) -> (100,-100)

Реализация этой функции довольно проста:

Point gridContainingPoint(Point pt) {
    int newX = ((int)Math.floor(pt.x/100f)) * 100;
    int newY = ((int)Math.floor(pt.y/100f)) * 100;
    return new Point(newX, newY);
}

Теперь, чтобы определить, какие изображения вам нужно нарисовать, вы вызываете метод gridContainingPoint() для каждого угла области просмотра. В этом примере вы получите:

    gridContainingPoint(75,75)   -> (0,0)
    gridContainingPoint(75,125)  -> (0,100)
    gridContainingPoint(125,75)  -> (100, 0)
    gridContainingPoint(125,125) -> (100, 100)

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

Прежде чем рисовать изображения, вы должны правильно установить область просмотра. По умолчанию, когда вы начинаете рисовать на холсте, область просмотра располагается в (0,0). Таким образом, вы увидите только те вещи, которые нарисованы на холсте, в области (0,0) x (50,50). Однако мы хотим, чтобы область просмотра была расположена в (75,75), чтобы мы могли видеть вещи в области (75,75) x (125,125). Для этого вы вызываете метод Canvas.translate(). Например, canvas.translate(-75,-75). Он должен быть отрицательным, поскольку он концептуально перемещает холст под областью просмотра, а не перемещает область просмотра.

Теперь, используя информацию из 4 вызовов к gridContainingPoint(), вы рисуете свое изображение в (0,0), в (0,100), в (100, 0) и в (100, 100). И все готово:)

Несколько вещей, которые следует иметь в виду - числа, используемые в этом примере, не будут фактическими числами, которые вы хотите использовать.

Во-первых, размер вашего видового экрана не будет 50x50, но это будет фактический размер вашего вида на момент его отрисовки. Вы можете получить это с View.getWidth() и View.getHeight().

Во-вторых, вам нужно настроить размер сетки и связанные с ней расчеты на основе размера вашего изображения - я сомневаюсь, что вы используете изображение размером 100x100.

И, наконец, имейте в виду, что когда вы вызываете метод gridContainingPoint() для каждого из углов области просмотра, он может возвращать одну и ту же сетку для нескольких углов. Например, если ваш видовой экран был вместо этого в (25,25), то он вернул бы изображение (0,0) для каждого угла, потому что (25,25), (25,75), (75,25) и ( 75,75) все в пределах изображения от (0,0) до (100,100). Так что это будет единственное изображение, которое вы должны нарисовать, чтобы полностью покрыть область просмотра.

0 голосов
/ 09 сентября 2011

Поскольку вы знаете, что ваше изображение значительно больше, чем холст, вы знаете, что за один раз на экране будет только один угол изображения.Это полезно.

Пусть ваша текущая позиция будет x, y, с началом координат 0,0.Пусть ваше изображение имеет размеры w, h

Подсчитайте, сколько полных изображений над вами от источника.Это просто x / w и y / h в целочисленном делении.Пусть результатом этого будут изображения i по горизонтали и изображения j по вертикали.

Всегда рисуйте свое изображение в (i * w, j * h) ((i + 1) * w, j * h) (i * w, (j + 1) * h) и ((i + 1) * w, (j + 1) * h).Рисование 4 изображений на холсте не будет большим ударом по производительности.Большая часть этого будет в конечном итоге с экрана и игнорируется.Затем вы можете продолжить рисовать свой экран как обычно.Это гарантирует, что у вас всегда будет полный фон, независимо от того, где лежит «шов» (Да, я знаю, что он без шва, но это просто означает, что мы не можем видеть границу. Это все еще там, конечно.)

0 голосов
/ 09 сентября 2011

Я думаю, что Google Map делает нечто похожее, когда вы перемещаетесь. Они разбивают свою КАРТУ на множество меньших тайлов (128х128 или 196х196). Если вы хотите более плавную прокрутку, вам нужно предварительно загрузить некоторые BITMAP заранее. Это все методы битовой виртуализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...