Skype / [Из песочницы] Улучшаем картинки в чате Skype

в 15:24, , рубрики: html, php, pixelart, skype, метки: , , ,

Skype / [Из песочницы] Улучшаем картинки в чате Skype

Предисловие

17 января мне, как и многим другим, прислали ссылку на статью zhovner про картинки в чате скайпа — http://habrahabr.ru/blogs/skype/136395/, и понеслось!
Идея прикольная, мы тут же начали перекидываться картинками, сгенерированными сервисом img4skype.com, но оказалось что у многих они отображаются растянуто.
Просмотрев фрагмент кода zhovner мы быстро обнаружили что это легко можно исправить, убрав один пробельный символ.
Нужно всего лишь исправить такой код:
$out .= '███';

на вот такой:
$out .= '██';

Поэтому и была быстро сделана локальная версия сервиса, позволяющая выбирать количество пробельных символов на пиксель — 2 или 3.
* Однако два пробельных символа вместо трех все же меняют размер картинки (вместо 1:1 получается 4:3), поэтому позже мы добавили сжатие оригинальной картинки (1:1 => 3:4, после искажения 4:3 опять получаем 1:1) если выбрано 2 пробела на пиксель.
Оптимизация

1. Объединение похожих цветов

Идея, лежащая на поверхности и озвученная самим zhovner — это объединение похожих цветов.
Если подряд идут два пикселя красного цвета то генерируется такой код:
██████

Вполне очевидно что это можно упростить:
██████

Так мы сэкономим место в сообщении, занимаемое кодом , что освободит нам 29 символов, а это 9 или 14 пикселей (в зависимости от количества пробелов на пиксель).
Это очень важно, потому что размер картинки ограничен максимальной длиной сообщения в Skype.
Эта оптимизация уже реализована несколькими читателями, поэтому не вижу смысла еще раз приводить ее код целиком.
Опишу подробнее только сравнение цветов.
Экспериментальным путем мы пришли к следующей функции:
// compare colors
function compare_clr( $c1, $c2, $max_dif, $img_px_qs )
{
$r1 = ( $c1 >> 16 ) & 0xFF;
$g1 = ( $c1 >> 8 ) & 0xFF;
$b1 = $c1 & 0xFF;

$r2 = ( $c2 >> 16 ) & 0xFF;
$g2 = ( $c2 >> 8 ) & 0xFF;
$b2 = $c2 & 0xFF;

$r_dif = abs( $r1 - $r2 );
$g_dif = abs( $g1 - $g2 );
$b_dif = abs( $b1 - $b2 );

$k = 1;

if( $img_px_qs == 1 ) // max quality
{
$def_k = 0.65;

// check difference in color channels
$dr = ( $r_dif > 0 ) ? $def_k : 0;
$dg = ( $g_dif > 0 ) ? $def_k : 0;
$db = ( $b_dif > 0 ) ? $def_k : 0;

$k = $dr + $dg + $db;

if( $k $max_dif ) || ( $g_dif*$k > $max_dif ) || ( $b_dif*$k > $max_dif ) )
{
$rv = false;
}

return $rv;
}

Первые два параметра — цвета, 3-й параметр — порог сравнения, максимальная разница между каналами цвета (0-255), 4-й параметр — максимальное качество или максимальный размер.
Если сравнивать между собой цвета целиком, не делая разницы между каналами (r, g, b), то качество картинки оставляет желать лучшего. Картинка очень быстро «мажется».
Поэтому мы сравниваем отдельные каналы. Если разница цветов хотя бы по одному каналу превышает порог — цвета считаются разными.
Формула с коэффициентами дает лучшее качество и лучше всего убирает смазывание. Она лучше подходит для сложных многоцветных картинок (фото).
Простое сравнение дает больший размер, но на сложных картинках смазывание появляется даже на маленьких порогах сравнения. Зато эта формула позволяет генерировать бОльшие картинки для простых смайлов и черно-белых троллфейсов.
2. Основной цвет

Еще одна важная оптимизация, так же лежащая на поверхности, это фоновый цвет.Цитата:

«Еще можно не окрашивать черный пиксель, потому что он и так черный.»

Черный пиксель на самом деле не черный, а дефолтный. Его цвет задается первым тегом в коде, который у img4skype не имеет цвета и потому дефолтный (почти черный):

...

Если добавить первому тегу font цвет то он будет дефолтным цветом для всей картинки:

...

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

imagecopyresampled( $newimg, $img, 0, 0, 0, 0, $neww, $newh, $imgw, $imgh );

// find most popular color
$c_arr = array();

for( $j = 0; $j < $newh; $j++ )
{
for( $i = 0; $i $val )
{
if( $val > $max_cnt )
{
$max_cnt = $val;
$def_clr = $key;
}
}

3. Максимальный размер

Дальше больше, довольно быстро обнаружилось что народу хочется поиграться с размерами и менять константу 800 вот в этой формуле:

$newh = floor(sqrt(800 / $ratio));

Некоторые картинки можно сделать больше, код для них помещается в скайп если заменить 800 на 1000 или 1200, а некоторые не помещаются даже при 800.
Поэтому следующим шагом делаем эту константу переменной, которую пользователь может выбирать.
Но это полумера, потому что теперь приходится кропотливо возиться с параметрами чтобы сделать максимально возможную по размеру и качеству картинку.
Поэтому делаем сразу генерацию максимально возможной картинки. Методом деления отрезка пополам генерируем код до тех пор пока не получим результат с максимальным значением константы в формуле выше и максимально возможной длиной, помещающейся в сообщение Skype.
Теперь пользователи должны заботиться только о качестве, которое может оценить только глаз человека.
Все остальное мы делаем за них.

P.S.

Обнаружив что img4skype.com не развивается (не добавляются даже реализованные идеи с готовым кодом), мы решили создать собственный аналог — skypeimg.com. Здесь реализовано все то о чем написано в этой статье.
Все функции «проверены временем» — они работают на нашем сервисе с 20.01.2012.
Вот пару примеров работы нашего генератора (скриншоты из скайпа в масштабе 1:1):
Хотелось бы сказать спасибо zhovner за оригинальную идею, и пожелать в будущем до конца реализовывать простой и сам собой напрашивающийся функционал, чтобы не провоцировать появление конкурентов!


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js