Как засунуть 2 мб в 300 кб и немного уменьшить головную боль

Всем привет.

Этот пост провисел в черновиках два или три месяца. Я всё ждал, когда же я причешу скрипт, который я в нем упоминаю. В общем я пришел к выводу, что скрипт я причесывать не буду за ненадобностью, и так работает :-)

Я компилирую игры с помощью Flex SDK, использую растровую графику. Она, как известно занимает много места, особенно в формате PNG, т.к. он не поддерживает сжатие с потерями. Но этот формат является фактически единственным выбором, если нужны картинки с альфа-каналом. Мой выход из этой ситуации, и именно об этом этот пост, сохранить картинку как два JPEG файла, с цветовой информацией и с альфаканалом. Причем альфаканал можно сжать очень сильно — артефакты сжатия в альфаканале практически незаметны.

Кроме того, очень часто анимация попадает ко мне в виде кучи файлов с именами вида «0001.png», «0002.png» и так далее по числу кадров. Например Блендер выдает картинки именно так. Эмбедить все это в таком виде — просто морока, на каждый файл надо написать [embed] тэг, класс и что-нибудь еще. Гораздо проще и удобнее иметь один файл на анимацию и один раз его прописывать. Это к вопросу об уменьшении головной боли. И кстати и общий размер файлов также уменьшается, за счет заголовков, сжатия всего вместе, а не по отдельности и т.д.

В одном случае мне и правда удалось сжать 2 мб исходных файлов в 300 кб. А позже и того меньше, но это уже не задокументировано :-)

Как же это всё сделать?
По сути нужно сделать две вещи: разобрать исходные картинки на цвет и альфаканал, одновременно объединив их в одну единую картинку, и потом собрать их обратно во флешке.

Первую задачу я решил на Питоне. Никто не мешает сделать ее как угодно еще. Я лично планирую сделать редактор для новой игры на Adobe AIR и включить эту функциональность в него. Тем не менее расскажу пока про Питон. Для выполнения скрипта нужен сам Python 2.x и библотека PIL для соответствующей версии Python.

Скрипт:
import Image

path = "c:\\tmp\\"
number = 10

image = Image.open(path + "0001.png");
width = image.size[0]
height = image.size[1]
newWidth = width*number

newImage = Image.new("RGBA", (newWidth, height))

for i in range(number):
    image = Image.open(path + repr(i+1).zfill(4) + ".png")
    print i, image.size, image.mode
    box = (i*width, 0, (i+1)*width, height)
    newImage.paste(image, box)

newImage.save(path + "color.jpg", quality = 85)

box = 0, 0, newWidth, height
r,g,b,a = newImage.split()
a.save(path + "alpha.jpg", quality = 15)
Он берет файлы из указанной директории с именами типа 0001.png и так далее в указанном количестве. Считает, что все файлы одного размера в пикселях. И сохраняет результат в текущей директории. Не бейте меня за плохой стиль: этот скрипт работает — чего же боле? Конечно, если бы я писал этот скрипт сегодня, он был бы красивее.

Во флеше все тоже очень просто: (код слегка обновлен по результатам обсуждения)
public static function createFromJpegs(color:Class, alpha:Class):BitmapData
{
  var colorBmp:BitmapData = (new color).bitmapData
  var alphaBmp:BitmapData = (new alpha).bitmapData
  
  var bmp:BitmapData = new BitmapData(colorBmp.width, colorBmp.height);
  var rc:Rectangle = colorBmp.rect;
  var pt:Point = new Point();
  bmp.copyPixels(colorBmp, rc, pt);
  bmp.copyChannel(alphaBmp, rc, pt, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
  
  colorBmp.dispose();
  alphaBmp.dispose();
  
  return bmp;
}
[Embed(source = '../media/Foo/alpha.jpg')]private static var fooAlpha:Class;
[Embed(source = '../media/Foo/color.jpg')]private static var fooColor:Class;
public static var fooBmd:BitmapData = createFromJpegs(fooColor, fooAlpha);


Применение на свой страх и риск и под свою ответственность
Многие утверждают, что изображения портятся, и использовать их так невозможно и вообще нельзя. С этим сложно согласиться, но и полностью отрицать невозможно: действительно, формат JPEG — это формат сжатия с потерями, и они будут, эти потери. Оценить, подходят ли испорченные JPEG'ом изображения для вашей игры, можете только вы сами. Я лично считаю, что для моих игр — вполне подходят, ну а вы смотрите сами :-)
  • +8

Комментарии (46)

0
А чем вам не подходит Flash CS5 Jpeg он там с альфой :)
0
как swc библиотека
0
25000 рублей за jpeg с альфой??? Я пока не настолько успешный флеш-девелопер :-)
+3
да йой… делай как многии… ви знаете о чем я :)
0
не хочется :-)
0
есть еще пару вариантов
1. заливка спец цветом
2. засунуть в Jpeg Exif alpha канал тут больше инфи
blog.vidroid.com/save-game-levels-into-jpeg-image/
0
2bogimp: Тоже самое в Flash CS3 (Library->Bitmap Properties->Compression: Photo(JPEG)) и чуть ли не в Flash8

Интересный подход.
Кстати, а эта операция не выделяет экстра память на копирование в битмапу? Точнее — очищает ли эта операция память после себя?

Еще был бы благодарен за статью с компрессией/декомпрессией XML :D
0
Выделяет, конечно. Я не заморачивался, т.к. проблем с поеданием памяти не встретил, по идее надо дописать вызов dispose в конце процедуры на colorBmp и alphaBmp. Но вообще и сборщик мусора должен убраться. Результирующая битмадата останется висеть конечно, но так и должно быть, она нужна. Если не нужна, ну надо и ее освободить после использования.

Про компрессию XML? А какие проблемы? ByteArray.compress/uncompress тебе в помощь. По идее если gzip сжать — то должно просто разжаться, но тут не знаю точно, надо кстати попробовать. В крайнем случае на air сделать компрессор.
0
Dispose обязательно. А вот результирующая BitmapData нужна далеко не всегда. Только если у тебя реально copyPixels рендерер. А если векторный — напиши сравнительное приложение на девелопе и на иде. В иде вытащи 2048*2048 jpg на сцену из библиотеки и сохрани. В девелопе возьми битмап дату из того же jpg в какую нибудь переменную внутри основного приложения. В теории в девелопе у тебя будет под 250Мб памяти, в иде несколько мб… Либо я не прав))
0
dispose тоже добавлю :-) обновлю пост к вечеру.

А как использовать векторный рендерер с битмапдатой я не понял? Чисто один раз нарисовать через beginBitmapFill и забыть? А зачем такое надо? А если у тебя bitmap на сцене, то он хранит ссылку на битмапдату и dispose делать нельзя.
0
Еще был бы благодарен за статью с компрессией/декомпрессией XML :D
А в сторону Flash XML Compressor не смотрел?
И еще есть старая библиотека для чтения-записи zip -AS3 Zip Library
+1
Что-то мне подсказывает, что можно colorBmp за раз скопировать с помощью copyPixels ;)
0
Можно :-) Так и сделаю.
0
Разве один clone() и один copyChannel() не быстрее будет?
0
clone не прокатит, потому что результирующая битмапдата должна быть RGBA, а т.к. исходная в RGB, то и клон так и останется в RGB.
0
все не как у людей ) тогда да, copyPixels()
+1
Плохо что игры потом выглядят коряво после таких оптимизаций… сжатие то с потерями.
Не все замечают правда, а китайцам так и вообще не привыкать, им и на пропорции пофиг зачастую :)
0
Для очень многих типов картинок сжатие без потерь просто не нужно. Да и игра размером 10-20 мегабайт никому тоже не нужна. Конечно, там где критично и заметно, надо png оставить или сжатие поменьше сделать.
+1
Скорости подрастают, да и трафик уже дешевый, так что 10-20M вполне нормально для игры.
Исключения разве что только смарты и планшеты + 3g, но там и flash толком не работает и нативные игры зачастую не меньше весят.
0
Я бы не согласился :-)
0
с каким пунктом? :)
0
С 10-20 мб для игры.

Вот здесь: www.newgrounds.com/portal/submit написано к примеру «File size is limited to 10 MB». А вот здесь www.kongregate.com/games/new — «SWF or unity3d file may not exceed 20MB».
Т.е. 10 мб еще куда ни шло, но 20 уже точно за рамками. И я видел порталы с ограничением в 5 мб.

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

Я лично считаю, что 5 мб — максимум. Хотя с текущим проектом скорее всего не уложусь, но уж постараюсь минимизировать превышение.
0
Этим надписям уже пару лет :)
0
И что? Они пока не исчезли. Будем ждать с релизом, пока не уберут?
0
Ну всегда есть варианты :)
И 20М уже вполне достаточно, думаю что в этом году все перейдут на такой лимит.
0
Рассчитаны они были из средней скорости в штатах на то время: 2.3Mbit/s (грубо 200кб в секунду), то-есть игра 10M грузится где-то 50 секунд. На 2010 год в штатах средняя ~3.9Mbit/s (по миру средняя 1.7).
Так что 10M вполне нормально, а до 20 уже можно начинать использовать. Если игра хорошая, порталы ее все равно возьмут.
Ну а для крупного проекта один фиг дозагрузку придется делать скорее всего.
+4
100%
Я в свое время возился с подобными оптимизациями достаточно тчательно и лучший вариант — это PNG! Иногда жипег дает некий прирост, но с его потерями ну его в одно место. В большинстве случаев сделать в PNG получается быстрее, качественнее и меньше в размере, чем жипег с альфой. Возится перебирать, где жипег даст припост при приемлимом качестве, занимает столько времени и ТУПОЙ работы, что крохи ужатия, которые выжимаются, того не стоят.

Для альфа-канала как раз качество очень чувствительно и альфа должна жаться без потерь. Я не представляю, что это должна быть за фигня, чтобы для нее были «артефакты сжатия в альфаканале практически незаметны». Жипег даже при 100% корежит картинку. Хочу видеть эту графику.
Мыло может сгодится на задники, но для задников можно вообще альфу не юзать в некоторых играх.

За статью, конечно, спасибо, очередные велосипеды (уже на питоне) ценителям доставляют, ну и не все же знали про такое. Кстати для склейки картинок (которые получаются от Блендера, к примеру) есть готовые тулзы. Я как любитель велосипедов и в качестве изучения языка писал свою, но если поискать, то можно их найти.
0
Мейби вместе с молехилом к нам придёт *.dds?
0
Для фото текстур само собой. Для обычной графики он ничего не даст, будут искажения.
0
Самый крутой тул складывания мелких картинок вместе, что я пробовал — Nvidia Texture Atlas Tools. Но очень часто проще самому под конкретный случай быстро скрипт накидать, чем пытаться найти, изучить и заставить делать то, что нужно какой-то готовый тул.
Насчет качества графики, я думаю, сильно зависит от характера картинки. Вот моя первая попытка на флеше: www.flashgamelicense.com/view_game.php?from=dev&game_id=8056 тут вся графика так пожата. Задники естественно без альфаканала. В текущем проекте (не хочу пока публично показывать) тоже артефактов не заметно, хотя графика совсем другого типа — фишки типа как для матч-3. Причем непожатая графика в png уже сейчас занимает 15 мб, а это еще далеко не всё. Игра пока в 3 мб укладывается, но дай бог с готовой в хотя бы 5 мб уложиться.
0
Посмотрел. Не увидел ничего невероятного в плане сжатия.
Зато артефакты пожатого альфаканала заметно: на светлом фоне (планеты) у объектов темные каемки.
Ну и мыло самих объектов — на астероидах не заметно, на главном корабле иногда бросается.
0
А я и не обещал ничего невероятного :-)
0
способ хороший, жмет действительно лучше чем PNG с JPEG сжатием в дефолтном редакторе. Альфу можно жать послабже, тем более она итак greyscale.

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

Только импортить все же лучше через SWC, а не эмбеддом
0
Почему лучше SWC и чем его делать?
Аналогичный тул можно сделать на самом флеше. Вообще без проблем. Только компилировать как air, чтоб с файлами работать. Для проекта на флеше — самое логичное.
0
да, чем SWC лучше эмбеда? И там и там используются уже скомпилированные классы.
+2
В SWC картинка экспортируется как битмапдата, эмбедом встраивается как битмапа, т.е. если нужны только битмапдаты, не создается лишних объектов.
0
… и писанины поменьше
0
спс. А SWC не весь линкуется к итоговой флешке? А то мне кажется, что эмбед гибче в этом плане — линкуем только выбранное. Писанины не особо больше с норм ИДЕ: кликнул, добавить на ресурсе, добавил строчку кода.
0
Во флеш-девелопе это указывает в настройках, как в билдере не знаю.
Правой кнопкой по *.swc и выбираешь Options
0
В ФД, по дефолту, линкуются только используемые классы. Писанина пропорциональна количеству эмбедов, как минимум нужно имя класса вбивать для каждого, для SWC — один раз подключил, и имеешь доступ из любой точки кода.
0
В SWC сохраняются пэкэджи, то есть, иерархия и структура ресурсов проекта. Что, теоретически, может помочь в разработке.

Вкупе с плагином, назначающим пэкеджи автоматом в зависимости от того, в какой папке лежит сырец, может быть полезно.
0
Пробовал такое.
На краях артефакты образуются :(
0
отож! и я ж про то!
0
Господа, чтобы удовлетворить вашу безжалостную атаку на мой топик, я его немного дописал :-) Наслаждайтесь!
0
Ты неправильно воспринимаешь критику.
Мы даем полную картину в отличие от твоей восторженной, от изобретения очередного велосипеда. Разумный человек должен сказать спасибо, а не огрызаться, как маленький ребенок, который привык к постоянному одобрению родителей.
И многие не «считают», а сжатие альфаканала действительно приводит к таким артефактам:
— нарушение начального контура (на тонких объектах очень заметно)
— темная каемка по всему контуру или местами.

У тебя же ничего про видимое ухудшение картинки не написано было в посте и были даны фантастические цифры сжатия (из 2Мб до 300Кб) в 6 раз. В частных случаях это возможно, но в большинстве — коэффициенты пониже.
В указанной игре все косяки незаменты из за ее темного фона и динамики, в статике (ГУИ, битмап-шрифты) это будет резать глаз уже посильнее.
0
Я не огрызался :-) Жаль, что так выглядит, я не хотел.
И я же и дописал в топик, что картинка ухудшается.

Вообще, ИМХО вполне очевидная вещь. Картинка портится, но иногда, и часто, вполне терпимо, учитывая уменьшение размера.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.