Месяц: Март 2011

  • Уменьшение изображения на Java

    Довольно рутинная операция. Есть несколько способов.
    Возьмём исходное изображение:

    1. Самый простой.

    Первый на stackoverflow.
    Суть метода очень проста – создаем BufferedImage меньшего размера, затем прорисовываем в него исходное изображение.
    Далее сохраняем обычным ImageIO.write()

            BufferedImage scaled = new BufferedImage(scaledWidth, scaledHeight,
                    BufferedImage.TYPE_INT_RGB);
            Graphics2D g = scaled.createGraphics();
            g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
            g.dispose();
    
            ImageIO.write(scaled, "JPEG", new File("1.jpg"));

    Результат:

    Недостатки:
    – Алгоритм сжатия хоть и быстрый, но результат неприятный.
    – Реализация сохранения JPEG ImageIO по-умолчанию не радует.

    2. Улучшаем качество JPEG-картинки.

    Для этого делаем так:

       /* код приведён только в учебных целях! не использовать в продакшн */
       public static void saveAsJPEG(BufferedImage scaledImage, OutputStream out) throws IOException {
    
            ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(scaledImage);
            Iterator iter = ImageIO.getImageWriters(type, "JPEG");
            // берем первый попавшийся для JPEG
            ImageWriter writer = (ImageWriter) iter.next();
            ImageOutputStream ios = ImageIO.createImageOutputStream(out);
            writer.setOutput(ios);
    
            JPEGImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
            iwparam.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
            // Вот здесь выкручиваем качество на максимум!
            iwparam.setCompressionQuality(1.0F);
            IIOImage iioimage = new IIOImage(scaledImage, null, null);
            writer.write(null, iioimage, iwparam);
            ios.flush();
            writer.dispose();
            ios.close();
        }

    Результат:

    Недостатки:
    – Сильно вырастает размер файла. Это известный баг. Подробности здесь: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5028259.
    – Алгоритм уменьшения картинки всё равно оставляет желать лучшего.

    3. Используем хинты.

    У Graphics2D есть так называемые RenderingHints, которые позволяют улучшить качество относительно настроек по умолчанию:

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    g.setRenderingHint(RenderingHints.KEY_RENDERING,
    RenderingHints.VALUE_RENDER_QUALITY);
    

    Результат:

    Недостатки:
    Качество стало лучше, но не сильно.

    4. Уменьшаем постепенно.

    Суть алгоритма – сжимать не сразу, а в несколько итераций.
    Код можно посмотреть здесь: http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html (очень позновательная и интересная статья, подробно расписано про уменьшение размера картинок).

    Результат:

    5. Метод из JDK 1.1.

    Впринципе также можно воспользоваться методом из JDK 1.1:
    Image.getScaledInstance(width,height, Image.SCALE_SMOOTH)

    Image scaled = originalImage.getScaledInstance(scaledWidth,
                    scaledHeight, Image.SCALE_SMOOTH);
    g.drawImage(scaled, 0, 0, null);
    g.dispose();

    Результат:

    Недостатки:
    Реализация метода может различаться на конкретных платформах.

  • Простые хаки и Greasemonkey


    Недавно обратил внимание на то, что шрифты на сайте java.net какие-то неприятные – мутные и плохо читаются. Может они всегда были такими, а я стал чуть-более придирчивым, а может быть стали такими после мега_перезагрузки_сайта.
    На самом деле не столь важно почему, важно понять как подправить…

    Могу предложить два способа из разряда “быстрых хаков”.

    Способ 1. Простой и скучный (и только для только для пользователей Firefox-а).
    Нужно в файле: C:\Documents and Settings\\Application Data\Mozilla\Firefox\Profiles\\chrome\userContent.css
    добавить настройку для конкретного домена:

    @-moz-document domain(java.net) {
      body {
        font-family: Arial,Helvetica,sans-serif !important; 
      }
    }
    

    Способ 2. Простые хитрости Greasemonkey.

    Greasemonkey (замасленная обезьяна, “механик”) – удобная тулза для различного рода кастомизации, переделавание и допиливания страничек. Один из моих любимых инструментов для подобного рода экспериментов.
    Если кратко, используя Greasemonkey вы сможете запускать свои скрипты на JavaScript-е у себя в браузере для определенных сайтов.

    Приведу несколько очень полезных ссылок:

    Кстати Greasemonkey сейчас работает в Google Chrome “из коробки”. Для установки вы просто драг-анд-дропаете в окно браузера ваш файлик (что-то типа sitefix.user.js) и он будет воспринят как обычное расширение.
    Для Firefox, как и раньше нужно поставить плагин https://addons.mozilla.org/ru/firefox/addon/greasemonkey/.

    Примерный скрипт для подмены шрифтов выглядит следующим образом:

    // ==UserScript==
    // @name           FontFix
    // @namespace      programmisty
    // @include        http://*.java.net/*
    // ==/UserScript==
    var head = document.getElementsByTagName("head")[0];
    var style = document.createElement("style");
    style.setAttribute("type", 'text/css');
    style.innerHTML = " body { font-family: arial,helvetica,sans-serif} ";
    head.appendChild(style); 
    

  • Удобный небезопасный кастинг

    Удобный автоматический кастинг

    Вещь не новая, но для тех, кто начинал программировать на Java в 90-ые и не очень привык к Generics-ам, это может быть интересно.

    Пример использования. Вам нужен метод, который возвращает объекты разных классов. Это могут быть обычные объекты – String, Integer, List, а могут быть и какие-то “свои” сложные бизнес-объекты вроде – ABController, XYZComponent, QWERTYHelper, FooManager (название зависит от возраста и предпочтений программиста).

    Так писали некоторые до Java 5.

        Helper helper = ....; // Какой-то вспомогательный класс, который хранит всякую ерунду
    
        String  path = (String) helper.get("storage.path");  // да, я знаю, хардкодить константы плохо. 
        Integer maxSize  = (Integer) helper.get("max.size"); 
        MyAwesomeDocumentManager manager = (MyAwesomeDocumentManager) helper.get("manager");

    Сейчас можно писать попроще. Конечно это сахар, но код выглядит полегче:
    (далее…)