Подтвердить что ты не робот

Загрузка изображений в формате bpg: совместимые, простые и эффективные методы

Недавно произошел некоторый шум в отношении формата изображения Fabrice Bellard BPG (http://bellard.org/bpg/), который (на основе демонстраций, представленных на его сайте ) обеспечивает лучшее сжатие, чем jpeg, webp и некоторые другие. Декодирование изображения выполняется в браузере с помощью JS, что означает, что он может быть использован немедленно, не дожидаясь принятия браузера. В целом это кажется хорошей идеей, и торговля некоторым временем процессора для более быстрой загрузки часто является эффективным компромиссом.

Метод используемый здесь для замены изображений, - это, на window.load, итерации по document.images, найти любой, где src атрибут содержит URL-адрес, заканчивающийся на ".bpg", и замените его на холст.

Это, безусловно, не единственный способ подойти к проблеме, и я вижу некоторые стороны этой техники, которые включают: а) холсты не имеют точно таких же правил компоновки, как изображения - например, установка атрибута width на нем означает что-то другое на img vs canvas, b) также кажется, что по крайней мере в Chrome, как масштабирование выполняется для изображений, которые уменьшены по сравнению с холстами, отличается.

Лучшее решение идеально соответствовало бы этим требованиям:

  • Попытка не дублировать данные изображения в памяти больше, чем необходимо (а также не излишне использовать большее количество CPU, чем это необходимо), декодирование в JS уже требует многого по сравнению с обработкой собственных изображений)
  • Совместимость браузера настолько же эффективна
  • Используйте теги <img> вместо <canvas> (не требование, но, похоже, будет лучше)
  • Обеспечьте простой способ не только обрабатывать изображения при загрузке документа, но и изображения, которые позже добавляются в документ (например, в ответ на активность пользователя).
  • Все еще будет прост в использовании (существующая техника на Bellard.org, безусловно, легко интегрируется)
  • EDIT: использование веб-работников для декодирования изображения без блокировки страницы также потенциально может быть хорошим способом.

Некоторые релевантные инструменты, которые приходят на ум, включают данные: и blob: urls.

У кого-нибудь есть примеры рабочего кода, который загружает BPG, используя такие "лучшие" методы? (То, как у Фабриса это в его примерах неплохо, и, конечно, подходы имеют компромиссы, но я думаю, что может быть что-то лучше для обобщенного использования.)

4b9b3361

Ответ 1

BPG выглядит многообещающим. Если вы хотите в любое время обнаружить добавление элементов <img>, из любого источника, вы можете использовать MutationObservers. Если вы не знаете о них, они асинхронны, регистрируют подмножество всех мутаций DOM в документе и позволяют обратному вызову обрабатывать эти мутации одновременно, а не синхронно, как с DOM Events. Поэтому, если вы создаете или изменяете источник большого количества изображений в script, наблюдатель будет ждать завершения вашего script, а затем обрабатывать все новые изображения за один раз (отсюда и циклы в обратных вызовах).

Следующее предполагает, что у вас есть функция с именем doSomethingToDecode(img) (извините, что я не собираюсь помогать с этим сейчас), которая заменяет src img (с вероятностью) сгенерированным PNG. Он может сделать это асинхронно, это не проблема. Кроме того, вам не нужно останавливать просмотр изображения при замене его src, пока сгенерированное замещение не заканчивается на ".bpg".

Этот первый наблюдатель будет реагировать, когда вы добавляете <img> потомков в любом месте (и других элементов, но игнорируете их); к сожалению, его невозможно оптимизировать в общем случае. Он должен перебирать список мутаций, а затем список новых узлов для каждой мутации, отсюда и те вложенные петли for. Но

imgObs=new MutationObserver(
    function(mutations){
        for(var i=0, m; m=mutations[i]; i++) if(m.addedNodes.length)
            for(var j=0, img; img=m.addedNodes[j]; j++) if(img.localName=="img"){
                if(img.src && /\.bpg$/.test(img.src))
                    doSomethingToDecode(img)
                srcObs.observe(img, srcOptions)
            }
    }
)

Этот другой наблюдатель реагирует, когда вы изменяете атрибут src элемента и должны наблюдать только элементы <img> (для максимальной производительности он не имеет отказов в случае, если вы заставляете его наблюдать за другими элементами, так что нет).

srcObs=new MutationObserver(
    function(mutations){
        for(var i=0, m; m=mutations[i]; i++){
            var img=m.target
            if(img.src && /\.bpg$/.test(img.src))
                doSomethingToDecode(img)
        }
    }
)

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

var srcOptions={childList:false, attributes:true, attributeFilter:["src"]}

Вы также можете сделать наблюдателя, который реагирует на удаление элементов <img>, чтобы остановить их наблюдение, и освободить любые связанные с декодированием ресурсы, но, надеюсь, браузер, по крайней мере, достаточно умный, чтобы прекратить наблюдение за элементами, которые должны быть мусором (не проверено!).

Запустите это после загрузки HTML (не ждите всю страницу с изображениями и CSS и т.д.). Примечание: для этого используется коллекция DOM уровня 0 document.images. Очень старая школа, но она делает именно то, что мы хотим очень эффективно и лаконично, так почему, черт возьми, не?

Итак, сначала вы декодируете существующий <img> с источниками bpg и наблюдаете за ними для src изменений позже:

for(var i=0, img; img=document.images[i]; i++){
    if(img.src && /\.bpg$/.test(img.src))
        doSomethingToDecode(img)
    srcObs.observe(img, srcOptions)
}

Затем это говорит первому наблюдателю реагировать на вещи во всем документе <body>; к сожалению, нет параметра tagNameFilter, чтобы изначально отфильтровывать мутации not ^ t20 > childList.

imgObs.observe(document.body, {subtree:true, childList:true, attributes:false})

Ответ 2

Добавьте обработчик ошибок к изображениям. В Chrome 40 он запускается, когда изображение отсутствует или когда браузер не знает, как его отобразить:

<script>
var errors = [];
function fallback(elt) {
    if(errors[elt.src]) return;
    // you should also extract the extension in order to test lena.bpg, lena.png, lena.jpg and not lena.bpg.png, lena.bpg.png.jpg
    errors[elt.src] = true;
    console.error('could not load image, falling back');
    elt.src = elt.src + '.png';
    // stop trying
    elt.onerror= function(){};
}
</script>
<img src="test.html" onerror="fallback(this);" width=300 height=300 />

Плюсы: отсутствие использования памяти/процессора на клиентской части. Минусы: необходимо предоставить альтернативные изображения на сервере.

Я считаю, что вы могли бы объявить глобальный прослушиватель ошибок во всех тегах img, чтобы это работало и для будущих изображений.