Масштабирование проблемы на Android - почему картина такая неуклюжая?

У меня нет 2D-графики и игровых возможностей. Я учил себя сотнями ошибок и десятков утраченных часов. Я начал с простой игры Dress Up. Я использовал Nexus 5x для разработки, где экран выглядел нормально. Когда я закончил одну веху, я попробовал игру на большом планшете Lenovo и миниатюрном телефоне Samsung Mini. Это выглядело ужасно.

введите описание изображения здесь

Оригинальный векторный PSD файл выглядит превосходно, экспорт PNG также не имеет проблем. Но когда я масштабирую картину, она ухабистая. Я знаю, что это растровое изображение. Но есть другая картина, которую я масштабирую в другом месте, и она выглядит нормально (обе фотографии 32 бит):

введите описание изображения здесь

Когда я играю в какую-либо игру из Google Play, у них никогда не бывает проблем с масштабированием. Как они реализованы? Используют ли они векторы? Есть ли какой-то трюк?

Я поместил все в папку assets, и хотя это заняло 1 МБ. Я декомпилировал некоторые apk, и у них нет набора вариантов для каждого разрешения. Хотя они хорошо масштабируют изображения.

Некоторые фрагменты исходного кода:

protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    this.w = w;
    this.h = h;
    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    drawCanvas = new Canvas(canvasBitmap);
}

protected void onDraw(Canvas canvas) {
    if (baseBitmap != null) {
        double scale = Math.min((double) w / (double) figure.getW(), (double) h / (double) figure.getH());
        figure.setScaleRatio(scale);
        int sw = (int) (scale * figure.getW());
        int x = ((w - sw) >> 1);
        figure.setX(x);

        paintDressPart(canvas, figure, figure.getMain());
        if (displayedParts != null) {
            for (DressPart dressPart : figure.getParts()) {
                if (displayedParts.contains(dressPart.getId())) {
                    paintDressPart(canvas, figure, dressPart);
                }
            }
        }
    }
}

private void paintDressPart(Canvas canvas, Figure figure, DressPart part) {
    double scale = figure.getScaleRatio();
    int sh = (int) (scale * part.getH());
    int sw = (int) (scale * part.getW());
    int sdx = (int) (scale * part.getDestX());
    int sdy = (int) (scale * part.getDestY());
    scaledRect.set(figure.getX() + sdx, sdy, figure.getX() + sw + sdx, sh + sdy);
    Rect partRect = part.getRect();
    canvas.drawBitmap(baseBitmap, partRect, scaledRect, canvasPaint);
}

И если вы хотите увидеть это самостоятельно, я подготовил полный проект на GitHub, чтобы вы могли скачать его и запустить сам:

https://github.com/literakl/DressUp

Обновление

Уменьшение масштаба также засасывает. Но благодаря замечанию Паавнна я понял, что Краска имеет значение. См. Разницу на картинке:

введите описание изображения здесь

Я запускал демонстрационное приложение на 3,2-дюймовом AVD-изображении. Изображение 278x786 масштабируется до 107x303.

Обновление октябрь

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

Я обнаружил, что mdpi работает лучше всего. У меня даже были снимки xxhdpi, и это выглядело хуже, чем mdpi. Я думаю, что даже на устройстве xxhdpi! Но это может быть проблемой картины, что она не была хорошо нарисована. Масштабирование ресурсов Android может сглаживать ребра на линиях.

Ответ 1

Я столкнулся с такой же проблемой.. это масштабирование не так гладко.. лучший стандартный метод

Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

Вы можете найти более оптимизированный размер-конвертер в Интернете.. но на самом деле лучшее решение, которое я закончил с помощью:

первый. Используйте векторный ресурс SVG.. это 100% работает с отличным качеством. просто убедитесь, что элементы SVG совместимы с Android (см. https://developer.android.com/studio/write/vector-asset-studio.html)

плюсы:

  • лучшее качество
  • эффективный размер
  • легко редактируется

минусы:

  • Поддерживаются не все элементы.

или

второй. обеспечивают изображение с высоким разрешением (одного достаточно в xxxhdpi)

плюсы:

  • хорошее качество

минусы:

  • может привести к проблемам с производительностью.

Я надеюсь, что это может помочь ".

Ответ 2

Используют ли они векторы?

SVG/Vector - это удобное решение, но векторное искусство требует чрезвычайно высокой точности, что делает его непригодным для многих стилей искусства. Легко использовать векторное искусство с базовыми формами, но может быть сложно добавить простая деталь с символическими стилями SVG, хотя она легко добавляется с помощью краски с помощью png,jpeg. Вы можете смотреть видеоролики с характеристиками производительности на SVG, где googlers предлагают SVG для дизайна значков. Большинство игр не используют его для богатых и сложных изображений.

Так что я не пойду на SVG специально, где мелкие детализация очень важны в играх.

Как они реализованы? Есть ли какой-то трюк?

  • Один из способов - просто опубликовать приложение с единой поддержкой регулярных активов (MDPI, HDPI), и если плотность экрана требует высокого разрешения, тогда отобразите диалог пользователю, спросив его, хочет ли он загрузить высокоуровневое приложение,.

  • У вас могут быть изображения с более высокой плотностью и масштабировать их во время выполнения на основе требований плотности телефона, используя DisplayMetrics. Иными словами, вы должны придерживаться того же соотношения сторон. Приготовьтесь к игре, сделанной для отображения 4: 3 выглядит отвратительным на 16: 9, но приспосабливая 16: 9 к 16:10, никто не заметил бы. читал больше.

Вы можете использовать этот способ, чтобы легко получить высоту и ширину экрана (, учитывая, что вы используете SurfaceView как наилучшую практику)

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenHeight = metrics.heightPixels;
int screenWidth = metrics.widthPixels;

Чтобы получить новую ширину-ширину при вращении:

surfaceChanged(SurfaceHolder holder, int format, int width, int height)

Этот метод дает вам ширину/высоту вашей поверхности, поэтому теперь вы можете использовать:

Bitmap.createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)

Из класса Bitmap, чтобы изменить размер ваших изображений пропорционально размеру поверхности, на которую вы рисуете. Вы можете изучить некоторые методы, чтобы найти подходящие коэффициенты масштабирования.

  1. OpenGL,kotlin может дать вам преимущество для создания богатого графического приложения. С их помощью вы можете контролировать производительность памяти и GPU, которая в основном является основой большинства игр, плюс вы можете использовать управление контролем более эффективно с GPU and native memory space плюс есть намного больше, о чем вы можете думать, это займет некоторое время, если вы не знакомы с ними, но как только вы его получите, они могут совершать множество магических трюков.

  2. Webp поможет вам уменьшить размер больших изображений до значительной разницы. Необходимо попробовать webp. (предложение, webp имеет небольшую проблему для альфа-канала изображения на некоторых устройствах, но слишком хорош для сжатия изображения)

Заключение: реализует лучшие практики с использованием OpenGL, SurfaceView, NDK и эффективно загружает изображения с использованием масштабирующих факторов ссылка docs и должен попробовать это тоже.

вы также можете взглянуть на 9patch изображения, где вы можете помещать ограничения, чтобы просто масштабировать часть изображения вместо целого. Это очень полезно.

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

Примечание. Ребята не стесняйтесь редактировать этот ответ с достоверной информацией, если хотите.