- PVSM.RU - https://www.pvsm.ru -
Конечно же, формат JPEG не поддерживает прозрачность, но сама идея использовать JPEG вместо PNG для прозрачных текстур будоражит умы довольно давно. Камрад PaulZi [1] не так давно предложил [2] использовать для HTML формат SVG, в котором хранится само изображение и маска. Jim Studt предлагает [3] использовать EXIF поля в JPEG и хранить там маски, а отображать на веб-странице с помощью Canvas.
Оба метода относительно сложны для использования, да и рассчитаны на веб, потому я остановился на самом простом варианте: хранить отдельно lossy JPEG для RGB и lossless маску в PNG, а совмещать их на этапе получения UIImage в программе. Сразу хочу сказать, что пишу на MonoTouch, потому код привожу на C#, хотя в ObjC это делается почти точно так же, с учетом синтаксиса.
Для разделения я использую консольные утилиты ImageMagik [4].
Эта команда отделяет альфа-канал:
convert file.png -channel Alpha -separate file.mask.png
Следующая команда создает JPEG файл, отбрасывая данные о прозрачности. Что характерно, некоторые другие утилиты (в т.ч. и Photoshop) при конверсии PNG файла в JPEG добавляют к нему некую одноцветную подложку и лишь затем сохраняют в RGB, что дает красивую, но неверную картинку с pre-multiplied alpha.
convert file.png -quality 90 -alpha off file.jpg
Качество полученного файла регурируется параметром quality 90. 90% качества для JPEG это больше, чем Apple ставит для скриншотов программ и фильмов. Думаю, каждый сможет подстроить под свой вкус это значение.
Маска получается в виде 8-битного Grayscale PNG без прозрачности. Такой формат отлично сжимается через optipng или через веб-сайт www.tinypng.org [5].
С JPEG ситуация интереснее. Можно было бы ограничиться только заданием качества, но недавно мне попалась замечательная утилита jpegrescan от Loren Merritt, одного из разработчиков ffmpeg и кодировщика x264 (на счет него же есть подозрения [6], что он является представителем инопланетного разума или кибернетического
Утилита использует необычный подход: подбирает разные коэффициенты для Progressive сжатия и выбирает наиболее оптимальные. Выигрышь получается от 5 до 15% с идентичным качеством картинки. Собственной страницы у утилиты нет, только топик [8] с обсуждением и сам perl-код: pastebin.com/f78dbc4bc [9]
Чтобы не вводить команды каждый раз вручную, я написал простенький скрипт на bash:
#!/bin/bash
basefile=${1##*/}
maskfile="${basefile%.*}.mask.png"
jpegfile="${basefile%.*}.jpg"
convert $1 -channel Alpha -separate $maskfile
convert $1 -quality 90 -alpha off $jpegfile
optipng $maskfile
jpegrescan $jpegfile $jpegfile
Вот результат работы скрипта:
В моем случае из файла в 1,8Мб получилось два файла на 380Кб и 35Кб.
Само склеивание делается очень просто — загружается две картинки в UIImage, затем создается на их основе CGImage методом WithMask (в ObjC это CGImageRef и initWithMask соответственно), а потом оборачивается в новый UIImage.
UIImage result;
using(UIImage uiimage = UIImage.FromFile(file))
using(UIImage mask = UIImage.FromFile(maskFile))
{
CGImage image = uiimage.CGImage;
image = image.WithMask(mask.CGImage);
result = UIImage.FromImage(image, uiimage.CurrentScale, uiimage.Orientation);
}
В реальном проекте я сделал чуть сложнее и проверяю наличие файла *.mask.png и если он отсутствует, то возвращаю обычный UIImage.FromFile().
Визуально игра не изменилась никак. Задержка загрузки и склеивания текстур на глаз не заметна, потому я и не стал ее замерять. Сам же проект уменьшился на 6 (!!) мегабайт, как в .ipa виде, так и в iTunes и на устройстве.
Скрин из игры в PNG. Никаких артефактов или проблем сжатия/прозрачности не видно.
Немного смущает удвоенное количество картинок в папке проекта, но это можно пережить. Изменения кода минимальные. Для графики интерфейса этот метод не идеален из-за необходимости вручную присваивать UIImage, а не загружать из NIB/XIB, но для собственных контролов или текстур подходит вполне. Даже если JPEG сохранять с 100% качеством, размер полученных файлов может быть меньше, чем изначального PNG без потерь качества.
P.S. ImageMagick и optipng ставятся из портов (MacPorts/Fink/Brew) и называются так же. Скрипт jpegrescan качается с pastebin [9] и использует порт jpeg
Автор: Nomad1
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/png/15863
Ссылки в тексте:
[1] PaulZi: http://habrahabr.ru/users/paulzi/
[2] предложил: http://habrahabr.ru/post/146804/
[3] предлагает: http://jim.studt.net/jpeg-alpha/
[4] ImageMagik: http://www.imagemagick.org/script/index.php
[5] www.tinypng.org: http://tinypng.org/
[6] есть подозрения: http://www.x264.nl/developers/Dark_Shikari/loren.html
[7] мозга: http://www.braintools.ru
[8] топик: http://news.ycombinator.com/item?id=803839
[9] pastebin.com/f78dbc4bc: http://pastebin.com/f78dbc4bc
Нажмите здесь для печати.