Как сохранить / преобразовать 24-битный PNG в 8-битный PNG?

Я создаю изображения с использованием ImageNew (и связанных с ним) в Railo, который использует JAI под обложками.

Когда я сохраняю изображение, я получаю 24-битный PNG, но мне нужен только 8-битный. (Простое повторное сохранение файла с 8-битным графическим редактором приводит к получению от четверти до половины числа байтов.)

ImageWrite не предлагает никакой функциональности, касающейся глубины в PNG-битах, и я не могу найти никаких подробностей о том, как это сделать с помощью самого JAI (получение ошибки DNS для http://jai-core.dev.java.net/)

Обновить:

Используя Quantize ImageFilter, я могу уменьшить количество цветов до 256 - это значительно уменьшает размер файла (но все же не так, как при обработке вручную), но все равно приводит к 24-битному / непалетированному файлу PNG. К сожалению, это также удаляет прозрачность, которую мне нужно сохранить (или, по крайней мере, повторно применить).

Если я возьму файл, который он производит, и запустю его через OptiPNG (оптимизатор PNG без потерь), он произведет индексированный 8-битный файл, срезает довольно много байтов и дает приемлемые размеры файлов.

Итак, оставшийся шаг головоломки: как повторно применить прозрачность после того, как ImageFilter удалил ее (или, лучше, предотвратить ее удаление).

Я думаю, мне нужен какой-то способ сделать Image.replace('white','transparent') в качестве решения на основе Railo/Java или кроссплатформенного инструмента командной строки.

3 ответа

Решение

Итак, у меня есть рабочее решение, которое дает приемлемые результаты - конечные файлы немного меньше, чем мой первоначальный ручной процесс, но визуально неразличимы.

Решение не такое кроссплатформенное, как хотелось бы (нужно найти / собрать бинарный файл Linux для OptiPNG), но это все еще достаточно хорошее решение.

Как предложил Ли в комментариях к вопросу, я использовал Quantizer для уменьшения цвета, затем с помощью MapColorsFilter исправил тот факт, что квантователь нарушает прозрачность, и, наконец, с помощью OptiPNG сжал полученный файл до разумного размера.

Вот соответствующий код:

<cfscript>
    var Filename = './filename.png'
    var MyImage = NewImage(Filename)

    ImageFilter(MyImage,'quantize',{numColors:256,dither:false})

    // ImageFilter(MyImage,'MapColors',{oldColor:'white',newColor:'ffffff00'})
    var TransImage = ImageMapColors(MyImage,'white','ffffff00')

    ImageWrite( TransImage , Filename )
</cfscript>

<cfexecute
    name      = "#Variables.OptiPngExecutable#"
    arguments = "-o9 #Filename#"
    timeout   = 30
/>

В настоящее время есть ошибка в ImageFilter Railo для фильтра MapColors, поэтому мне пришлось получить доступ к фильтру напрямую, вот код, который я использовал для обхода этого:

<cffunction name="ImageMapColors" output=false >
    <cfargument name="Image" rtype="Image" required />
    <cfargument name="Old"   type="String" required />
    <cfargument name="New"   type="String" required />

    <cfset var ObjKey = 'ColorReplacer_#Arguments.Old#_#Arguments.New#' />

    <cfif NOT StructKeyExists(Variables,ObjKey)>
        <cfset var Old = createObject('java','railo.commons.color.ColorCaster').toColor(Arguments.Old) />
        <cfset var New = createObject('java','railo.commons.color.ColorCaster').toColor(Arguments.New) />
        <cfset Variables[ObjKey] = createObject("java","railo.runtime.img.filter.MapColorsFilter")
            .init(Old.getRGB(),New.getRGB()) />
    </cfif>

    <cfreturn ImageNew( Variables[ObjKey].filter(ImageGetBufferedImage(Arguments.Image),{}) ) />
</cffunction>

в сочетании с cfexecute должен удовлетворить ваши потребности.

Вы пытались использовать промежуточный шаг, сохраняя сначала в формате JPEG, используя атрибут "качество" ImageWrite()потом сохранение как PNG? Просто мысль. Угадывать использование .3 или же .4 как значение может работать?

Другие вопросы по тегам