Играем в сапера в фотошопе

от автора

По роду своей деятельности мне периодически приходится автоматизировать свою работу в фотошопе. Точнее я мог бы этого не делать, но природная лень не оставляет шансов в борьбе с рутиной, как говориться «лучше час потерять, зато потом за 5 минут долететь». Все бы наверное так и оставалось на уровне отдельных разрозненных скриптов если бы не пост от enotus. Благодаря ему я узнал, что к фотошопу (как впрочем и другим продуктам от Adobe) можно писать расширения на HTML+JS. И пошло, поехало.

Как-то так сложилось, что изучение всего нового я обычно начинаю с написания простенькой игрушки на этом самом новом. Для фотошопа я выбрал Сапера. В этом примере я бы хотел рассказать о создании интерфейса расширения, взаимодействии с фотошопом и обработкой событий. Так что кому все еще интересно, прошу подкат.

Как уже описано в статье моего предшественника нам понадобится Brackets с установленным расширением ну и собственно сам Photoshop.

Также рекомендую сразу запастись документацией со страницы Adobe: наиболее вероятно что потребуется Photoshop CC JavaScript Reference и возможно Photoshop CC Scripting Guide. Они не имеют прямого отношения к написанию расширений, но потребуются для взаимодействия с фотошопом.

Приступаем. Создадим проект в Brackets при помощи расширения:

У нас появится папка с полным набором файлов для работы расширения:

В папку CSS я закинул дополнительно файл для светлой темы topcoat т.к. предпочитаю работать со светлым интерфейсом, но это не обязательно. Кстати о Topcoat, скачать и почитать документацию можно тут.

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

В папке js нас интересует main.js, собственно основной файл нашего проекта. Но прежде чем переходить к нему нам нужно создать интерфейс нашего расширения. Это делается с помощью HTML в файле index.html.

И остался еще один важный файл о котором я не упомянул: hostscript.jsx. JSX скрипт это как раз то что использовали уже довольно давно для автоматизации различных процессов в фотошопе и других проектах от Adobe. У них даже есть специальная утилитка ExtendScript Toolkit, она тоже может пригодится, например для отладки скриптов jsx.

index.html

В этом файле тоже уже все подготовлено, подключена темная тема topcoat, добавлены все необходимые скрипты. Собственно все что требуется, это заполнить div#content:

    <div id="content">                  <div>             Поле:<br>             <input type="text" id="w" class="topcoat-text-input" placeholder="width" value="6"> x <input type="text" id="h" class="topcoat-text-input" placeholder="height" value="6"><br>             Количество мин:<br>             <input type="text" id="m" class="topcoat-text-input" placeholder="width" value="8"><br>             <button id="btn_start" class="topcoat-button--large hostFontSize">New game</button>         </div>         <hr>         <div>                         <h2>Осталось открыть: <span id="res">-</span></h2>             <h3><span id="timer">0</span> sec.</h3>             <button id="btn_check" class="topcoat-button--large hostFontSize" disabled>Check cell</button>                        </div>            <div>                         <button id="btn_mark" class="topcoat-button--large hostFontSize" disabled>Mark</button> <button id="btn_unmark" class="topcoat-button--large hostFontSize" disabled>UnMark</button>             </div>              </div> 

В результате мы получим следующую картину:

Можно переходить к JS.

main.js

По сути тут уже есть пример самого простого использования:

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */ /*global $, window, location, CSInterface, SystemPath, themeManager*/  (function () {     'use strict';     var csInterface = new CSInterface();              function init() {                         themeManager.init();                         $("#btn_test").click(function () {             csInterface.evalScript('sayHello()');         });     }              init(); }()); 

Самая главная тут строчка это csInterface.evalScript(‘sayHello()’);, именно таким образом происходит взаимодействие с приложением. Дело в том, что механизм расширений универсальный и не зависит от приложения в котором выполняется, но по этой же причине ни чего не знает от функционале — ему все равно фотошоп это иллюстратор. Именно для этого и используются скрипты JSX. А sayHello() лишь один из методов реализованных в hostscript.jsx.

Но, нам же нужно не только передавать данные в jsx, но и получать результаты. Поэтому можно использовать callBack функцию, в том числе анонимную:

csInterface.evalScript('checkCell()', function(result){                 if(result<=0)                 {                     stopGame();                 }                 else                 {                     $("#res").html(result); //вывод количества закрытых клеток без мин                 }             }); 

Вот примерно так я проверяю клетки на мины: result содержит количество клеток которые еще нужно открыть для победы. Мина соответствует -1. Если вы обратили внимание среди скриптов сразу подключен jQuery. Так что взаимодействовать с интерфейсом проще простого.

Единственный момент требующий отдельного упоминания в рамках main.js это реакция на события происходящие в приложении за пределами нашего расширения. С одной стороны тут все просто, с другой хуже чем хотелось бы. Первым делом с попытался поймать клик мышкой в документе — не тут то было, такого события просто нет. Как выяснилось позже события относятся скорее ко всему приложению, а не к документу и имеют характер «использован фильтр», «отменено последнее действие» и тому подобное. Помимо этого у всех событий есть 4-х буквенный идентификатор и длинный цифровой и в нашем случае нужен именно длинный цифровой, а в документации указаны лишь короткие коды. Благо у каждого приложения есть метод конвертации одного идентификатор в другой, но он работает в рамках jsx-скрипта.

В общем я не буду сейчас вас грузить деталями, воспользуемся примером из документации:

    function registerPhotoshopEvent(in_eventId) {         var event = new CSEvent("com.adobe.PhotoshopRegisterEvent", "APPLICATION");         event.extensionId = csInterface.getExtensionID();         event.appId = csInterface.getApplicationID();         event.data = in_eventId         csInterface.dispatchEvent(event);     }      csInterface.addEventListener("PhotoshopCallback" , function(event) {         csInterface.evalScript("return (app.documents.length > 0)", stopGame)     });      var closeEventid = "1131180832"; //идентификатор события закрытия документа     registerPhotoshopEvent(closeEventid); 

hostscript.jsx

Как я уже писал ранее, это основной способ взаимодействия с приложением, в данном случае с Photoshop CC. Вот тут то и пригодится скаченная в самом начале Photoshop CC JavaScript Reference. Там содержится описание свойств и методов различных объектов фотошопа.

Я приведу здесь лишь пару примеров. Начнем с создания документа:

var startRulerUnits = app.preferences.rulerUnits; // сохраняем текущие единицы измерения  preferences.rulerUnits = Units.PIXELS; //Заменяем единицы измерения на пикселы      //создаем документ mainDoc = app.documents.add(width,  height, dpi, "Game", NewDocumentMode.RGB); 

Вы прекрасно знаете, что размеры в фотошопе можно задавать в разных системах измерения. Соответственно и результат измерения вы будете получать в тех-же единицах. Поэтому за этим нужно внимательно следить. Чтобы не получить неприятный сюрприз лучше подстраховаться и перевести единицы измерения к нужному значению.

Обращаю внимание, что есть исключения. Например, выделенная область всегда задается и измеряется только в PIXELS. А например все векторные объекты меряются исключительно в POINTS.

Для конвертации единиц измерения я использовал небольшой метод подсмотренный где-то на просторах интернета:

function convertValue(value, from, to) {     output = new UnitValue(value, from);         return output.as(to); } 

Но и тут вас ждет сюрприз. Конвертация из мм и дюймов в пикселы всегда происходит из расчета 72 точки на дюйм и если у вас другое разрешение изображения, следует произвести пересчет самостоятельно.

Ну и пожалуй последний пример. Очень многие вещи которые мы привыкли в фотошопе делать как одно действие программно нужно реализовывать множеством команд. Например для склеивания слоев разными способами (склеить все, только видимые, связанные) нужно реализовывать самостоятельно. Из доступных есть только вариант склеить с предыдущим слоем, а дальше уже ваше дело (проверять видимость, связанность, менять местами слои). Похожая ситуация с выделением области. Вы не можете выделить круг или столбец в один пиксел. Все что вам предлагают засунуть массив координат точек, а уж какая это будет фигура лишь от вас зависит:

function DrawBomb(layer, x, y, colorHEX) {     var startRulerUnits = app.preferences.rulerUnits; // сохраняем единицы измерения      preferences.rulerUnits = Units.PIXELS; //Заменяем единицы измерения на пикселы          mainDoc.activeLayer = layer;     mainDoc.selection.select([         [(x+0.2)*cellSize+padding, (y+0.2)*cellSize+padding],         [(x+0.8)*cellSize+padding, (y+0.2)*cellSize+padding],         [(x+0.8)*cellSize+padding, (y+0.8)*cellSize+padding],         [(x+0.2)*cellSize+padding, (y+0.8)*cellSize+padding],         [(x+0.2)*cellSize+padding, (y+0.2)*cellSize+padding]     ]);     var color = new SolidColor;     color.rgb.hexValue = colorHEX;     mainDoc.selection.fill(color);     mainDoc.selection.deselect();         preferences.rulerUnits = startRulerUnits; //восстанавливаем единицы измерения } 

Можно и поиграть

Ну вот, интерфейс нарисован, обработчики в js написаны, логика в jsx реализована, можно и поиграть.
Не забываем включить Debug Mode, чтобы мы увидели свое не подписанное расширение в фотошопе.

Теперь можно запускать Photoshop и активировать наше расширение:

Ну теперь можно и поиграть. Правда из-за того, что клики по картинке не отлавливаются, нужно двигать красный прямоугольник и нажимать кнопки действий на панели расширения.

Ну и видео процесса:

Напоследок скажу пару слов про подписывание ваших расширений.
Не забываейте удалять все скрытые файлы перед подписыванием.
В Windows путь к папке с расширением нужно начинать с точки, примерно так:
ZXPSignCmd.exe -sign ./ru.mobak.habrasample ru.mobak.habrasample.zxp cert.p12 pass

И прошу, не судите строго мой код, все таки я не программист.

Файлы

Исходники расширения можно скачать тут.

Готовое расширение которое можно установить через Adobe Extension Manager лежит тут. Подписано самодельным сертификатом, так что матюкается маленько.

ссылка на оригинал статьи http://habrahabr.ru/post/253337/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *