Первым делом в окне script-fu я научился определять процедуры и запускать их:
(define (square x) (* x x)) (square 5)
Далее я принялся изучать процедуры уже самого gimp, доступные из плагина script-fu(т.е для tinyscheme). Отличным помощником в этом вопросе является меню окна консоли Посмотреть...
. Оно позволяет найти описание текущей(набранной в окне ввода) процедуры, или же найти любую другую процедуру в огромном списке, к которому можно применить фильтр.
Это именно функции script-fu
экспортируемые в тинисхему, описанным выше способом, что бы тинисхема получила возможность работать с гимп объектами. Гимп объекты как правило представлены дескрипторами. Значительное количество функций, как правило имеющих в имени plug-in или script-fu имеют параметр run-mode
, определяющий в каком режиме будет работать функция, допускается работа в интерактивном и неинтерактивном режиме. Не все функции, даже принимающие этот параметр, его поддерживают, как например plug-in-gfig
, работающая только в интерактивном режиме.
Первыми освоенными процедурами, работающими уже непосредственно с ГИМП, были процедуры с помощью которых я намеревался создать новое изображение. Что ж давайте повторим эти шаги:
Скачайте Архив библиотеки
подготовка к работе:
(define path-home (getenv "HOME")) (define path-lib (string-append path-home "/work/gimp/lib/")) (define path-work (string-append path-home "/work/gimp/")) (load (string-append path-lib "util.scm")) (load (string-append path-lib "img.scm"))
Создание изображения в GIMP 2.10:
(define i1 (car (gimp-image-new 640 480 RGB))) ;;создаём изображение i1 ;;1 (define l1 (car (gimp-layer-new i1 640 480 RGB "layer 1" 100 LAYER-MODE-NORMAL-LEGACY))) ;;создаем слой l1 ;;2 (gimp-image-undo-disable i1) (gimp-image-add-layer i1 l1 0) ;;добавляем слой в изображение (gimp-palette-set-background '(255 127 0)) ;;устанавливаем цвет фона изображения (gimp-edit-fill l1 FILL-BACKGROUND) ;;заполняем цветом фона слой (gimp-display-new i1) ;;и вот только на этой функции появилось изображение в окне!!! (gimp-image-undo-enable i1) ;;появилось изображение в меню окна отмены действий.
Теперь с созданным нами изображением можно как то работать средствами GIMP, эту последовательность действия я в последствии объединю в функцию создания изображения create-1-layer-img
. Надо отметить, что приведённые команды подходят именно для версии 2.10, а вот команды для версии 2.6:
2.6 Виндовс
(define path-home "D:") (define path-lib (string-append path-home "/work/gimp/lib/")) (define path-work (string-append path-home "/work/gimp/")) (load (string-append path-lib "util.scm")) (load (string-append path-lib "img2.6.scm")) (define i1 (car (gimp-image-new 640 480 RGB))) ;;создаём изображение i1 ;;1 (define l1 (car (gimp-layer-new i1 640 480 RGB "layer 1" 100 NORMAL))) ;;создаем слой l1 ;;2 (gimp-image-undo-disable i1) (gimp-image-add-layer i1 l1 0) ;;добавляем слой в изображение (gimp-palette-set-background '(255 127 0)) ;;устанавливаем цвет фона изображения (gimp-edit-fill l1 BACKGROUND-FILL) ;;заполняем цветом фона слой (gimp-display-new i1) ;;и вот только на этой функции появилось изображение в окне!!! (gimp-image-undo-enable i1) ;;появилось изображение в меню окна отмены действий.
отличаются они только названиями констант. Мелочь, но не приятно. Но гимп меняется, развивается, и старые имена констант, уточняются и изменяются.
Далее я попытался наладить взаимодействие с script-fu сервером из emacs, скачал пакет к емаксу, но его функциональность меня не удовлетворила, поэтому я оставил эти попытки и принял простой способ взаимодействия с консолью, основанный на копировании и вставке кода через буфер обмена. Немного не удобно, зато универсально, будет работать в любом редакторе.
Загрузка различных типов графических файлов
Большая группа функций в скрипт-фу предназначены для загрузки и сохранения файлов различных графических форматов. Как правило все они носят название подобные file-pcx-load
, file-pcx-save
.
Но я для загрузки выбрал функцию загруки файлов gimp-file-load
позволяющую загружать файлы любых форматов и получать на выходе дескриптор изображения. В то же время в скрип-фу есть функция gimp-file-load-layer(gimp-file-load-layers)
позволяющая загружать изображения как уровень уже существующего изображения. Итак загружаем файл с изображением:
подготовка
;;определим пути по которым лежать библиотеки и данные (define path-home (getenv "HOME")) (define path-lib (string-append path-home "/work/gimp/lib/")) (define path-work (string-append path-home "/work/gimp/"))
;;обёртка над функцией загрузки изображения (define (img-load f-name) (car (gimp-file-load 1 f-name f-name))) (define ii2 (img-load (string-append path-work "t1.png")))
О том, что мы загрузили изображение, нам скажет только значение переменной ii2, во внешнем виде гимпа при этом ничего не измениться и изображения мы не увидим. Хотя если внимательно порыться во вкладке изображения, там можно будет увидеть, что загружено изображение t1.png. Чтобы его отобразить, нужно создать дисплей(display).
;; загруженное изображение можно отобразить в новом окне (gimp-display-new ii2)
Добавление одного изображения в другое изображение
Как правило загружать изображение стоит в двух случаях, либо мы хотим его изменить, либо мы хотим его(или его часть) где нибудь использовать. Сейчас нам надо добавить загруженное изображение в другое изображение. В принципе это сделать достаточно просто.
(define i1 (create-1-layer-img 640 480)) (define ii2 (img-load (string-append path-work "t1.png"))) (define ll2 (car (gimp-layer-new-from-drawable (car (gimp-image-get-active-layer ii2)) i1))) (gimp-image-add-layer i1 ll2 0)
Результат представлен на рисунке:
Как видно изображение было добавлено в качестве слоя, с именем «Копия Фон», уровень на который мы его добавили 0(третий параметр gimp-image-add-layer
), т.е на самый верх стека слоёв.
В принцпе есть путь проще. Это функция позволяющая загружать одно изображение в качестве слоя для уже имеющегося изображения.
(define (img-load-as-layer img f-name) (car (gimp-file-load-layer 1 img f-name))) (define i1 (create-1-layer-img 640 480)) (define l2 (img-load-as-layer i1 (string-append path-work "t1.png"))) (gimp-image-add-layer i1 l2 0)
Результат тот же самый, только имя нового слоя будет «t1.png». Этот метод гораздо эффективнее предыдущего, но при этом надо точно знать, что данной изображение нам нужно только как уровень к данному изображению. Ну вообщем есть варианты работы, выбирайте когда какой удобнее использовать.
Манипулирование слоями.
В принцпе мы добавили новый слой, но он у нас стоит в начале координат изображения. Что же делать когда нам надо, ммм.. к примеру подвинуть этот слой, в какое то необходимое нам место?
Сделать это очень просто функцией gimp-layer-set-offsets. Например:
(gimp-layer-set-offsets l2 20 20) ;это л2, а не 12
Оп. и слой отпрыгнул от начала координат, на указанное смещение.
Давайте отмасштабируем этот слой, т.е придадим ему новые размеры
(gimp-layer-scale l2 200 400 TRUE) ;;на месте осталась середина слоя ;;или так, врните изображение на место использовав историю действий (gimp-layer-scale l2 200 400 FALSE) ;;на месте остался левый верхний угол слоя. ;;получить размер слоя (gimp-drawable-height l2) ;;(400) (gimp-drawable-width l2) ;;(200) ;; получить позицию (gimp-drawable-offsets l2) ;;(40 40)
Опс!!! Обратите внимание что смещение при маштабировании ИЗМЕНИЛОСЬ!!! Но если бы слой стоял в нулевой позиции, смещение не поменялось бы.
Вспомогательные функции для работы с изображениями и слоями.
(gimp-image-list) ;;список текущих изображений в GIMP (gimp-image-get-layers 2) ;;список слоёв заданного изображения
Функция позволяющая объединять слои:
(gimp-image-merge-visible-layers 2 EXPAND-AS-NECESSARY)
Работа с выделениями в гимп.
Выделение(selection) в GIMP это набор контуров на специальном слое изображения. Даже не так, это специальный слой, имеющий два состояния выделено или не выделено. Любые операции по выделению, его отмене, объединению выделений, их перемещению и т.п. меняют состояния этого слоя. Наносят на него «пятна» выделения, или убирают их оттуда. Всё что попадает в эти «пятна», является областью с которой гимп будет работать, обрабатывая операции над выделениями. Большинство функций работы с выделениями можно найти в просмотрщике процедур задав фильтр: select
;;создаем первое изображение(цвет выбран в ручную - зелёный (gimp-context-set-background '(0 255 0 )) (define i1 (create-1-layer-img 640 480)) ;; можно выделить всё и вычесть из выделения прямоугольник ;;(gimp-selection-all i1) ;;(gimp-rect-select i1 50 75 20 20 CHANNEL-OP-SUBTRACT TRUE 0) ;;можно сбросить выделение и создать новое прямоугольное выделение (gimp-selection-none i1) (gimp-rect-select i1 50 75 20 20 CHANNEL-OP-ADD TRUE 0) ;;создаём воторе изображени - цвет красный (gimp-context-set-background '(256 0 0 )) (define i2 (create-1-layer-img 640 480)) ;;создаём слой из выделения в изображении i1 (define ls1 (car (gimp-selection-float (car (gimp-image-get-active-drawable i1)) 0 0))) ;;5 ;;создаём из слоя ls1 слой совместимый с изображением i2 (define ll2 (car (gimp-layer-new-from-drawable ls1 i2))) ;;прикрепляем слой ll2 к изображению i2 (gimp-image-add-layer i2 ll2 0)
Скрина нет, но обычный зелёный четырёхугольник на красном фоне, неинтересно.
Из приведённого кода видно, что мы работаем с выделением без дескриптора, а берём его из изображения. Таким образом выделения могут задаваться как программно, так и в ручную, что позволяет их использовать как часть интерактивного взаимодействия с обычным пользователем в скриптах.
Создав изображение i1
с заполнением цветом фона(зелёным), я сделал несколько выделений в ручную, используя операцию объединения выделений(нажимая Shift). Затем добавил выделение маленькой прямоугольной области внутри имеющегося прямоугольника. Далее я создал новое изображение i2
заполненное цветом фона(красным). После этого из активной отображаемой области изображения i1
полученной с помощью gimp-image-get-active-drawable
, создал слой ls1
, отображающий выделенные области изображения i1
. На основе этого слоя, создал слой для изображения i2
с помощью функции gimp-layer-new-from-drawable
. И уже этот слой поместил в изображение i2
.
Давайте продемонстрирую ещё один пример работы с выделением. На этот раз выделение останется на месте, просто мы изменим его цвет.
(gimp-context-set-foreground '(255 0 0)) (define i1 (create-1-layer-img 640 480)) ;;создаём изображение ;;создаём сложное выделение (gimp-image-select-ellipse i1 CHANNEL-OP-ADD 50 20 200 100) ;;основная область выделения (gimp-image-select-ellipse i1 CHANNEL-OP-SUBTRACT (- 150 70) 60 20 20) ;;вычтем регион из выделения (gimp-image-select-ellipse i1 CHANNEL-OP-SUBTRACT (+ 150 50) 60 20 20) ;;и ещё один вычтем (gimp-context-set-foreground '(0 255 255)) ;;установим цвет кисти переднего плана ;;заполним выделенный регион активной области рисунка установленным цветом (gimp-drawable-edit-fill (car (gimp-image-get-active-drawable i1)) FILL-FOREGROUND)
Рисуем линию.
Операции с изображениями, слоями и выделениями это конечно хорошо, но нам бы для начала научиться рисовать точки и линии. В гимпе есть операция манипулирования изображением под названием set-pixel, точнее gimp-drawable-set-pixel
и gimp-drawable-get-pixel
. Но изменение изображения по пиксельно, лишит нас основного богатства рисования в GIMP, а именно кисти. Поэтому, наряду с возможностью попиксельного манипулирования изображением в гимпе есть возможноть рисования кистью, а именно функция: gimp-paintbrush-default
. Данная функция на вход требует массив координат точек и может рисовать выбранной заранее кистью ломаные линии состоящих из произвольного количества сегментов.
(define (draw-line-def img x0 y0 x1 y1) (let* ((points (cons-array 4 'double)) (dw (car (gimp-image-get-active-drawable img)))) (aset points 0 x0) (aset points 1 y0) (aset points 2 x1) (aset points 3 y1) (gimp-paintbrush-default dw 4 points))) (define i1 (create-1-layer-img 640 480)) ;;создаём изображение ;что бы не создавать множество резервных копий, для отката изменений, можно блокировать ;;эту возможность (gimp-image-undo-disable i1) ;; в ручную выбираем кисть и устанавливаем цвет переднего плана. ;; рисуем линии (draw-line-def i1 20 20 520 20) (draw-line-def i1 520 20 520 460) (draw-line-def i1 520 460 20 460) (draw-line-def i1 20 460 20 20) ;;а потом включить (gimp-image-undo-enable i1)
Смотрим, что получилось:
Признаться я и сам не ожидал получить такую красоту. Я выбрал только кисть Hardness 025. Но после полученного результата, я открыл её параметры и увидел, что гимп настроен брать параметры цвета из градиента(Динамика рисования — Color From Gradient, Повтор — Треугольная волна, Градиент — Full saturation), который я ранее устанавливал для каких то своих экспериментов.
Что ж давайте нарисуем прямоугольник поскучнее. В ручную установим кисть: (Динамика рисования — Basic Simple)
(draw-line-def i1 50 50 420 50) (draw-line-def i1 420 50 420 360) (draw-line-def i1 420 360 50 360) (draw-line-def i1 50 360 50 50)
Рисуем окружность.
На основе написанной функции рисования линии, легко написать функцию рисования окружности, состоящей из заданного количества прямых.
(define i1 (create-1-layer-img 640 480)) ;;создаём изображение ;;определяем функцию рисования окружности из заданного количества опорных точек (define (draw-circle-n img x y radius num-points) (let* ((angle-delta (/ (* 2 *pi*) num-points)) (points (cons-array (* 2 (+ num-points 1)) 'double)) (i 0) (dw (car (gimp-image-get-active-drawable img)))) (while (<= i num-points) (let ((angle (* i angle-delta))) (aset points (* 2 i) (+ x (* (sin angle) radius))) (aset points (+ (* 2 i) 1) (+ y (* (cos angle) radius))) (set! i (+ i 1)))) (gimp-paintbrush-default dw (* 2 (+ num-points 1)) points) points)) ;;установив тип кисти, цвет, размер кисти, вызываем функцию рисования окружности (draw-circle-n i1 100 100 50 24) (draw-circle-n i1 100 100 50 3) (draw-circle-n i1 100 100 50 4)
Дополнительная точка в массиве нужна для замыкания контура. В результате запуска получаем треугольник, ромб и окружность.
Не совсем удобно постоянно указывать количество опорных точек для рисования окружности. К сожалению тинисхема не поддерживает передачу параметров по умолчанию, но к этому вопросу мы ещё вернемся в дальнейшем. Предлагаю пока в качестве «костыля» использовать …
макрос!
(define-macro (draw-circle . param) ;;(prn param) ;;отладочка! (let* ((i (car param)) (x (cadr param)) (y (caddr param)) (r (cadddr param)) (l (length param))) (case l ((4) ;;(prn "4 param") `(draw-circle-n ,i ,x ,y ,r 32)) ((5) ;;(prn "5 param") `(draw-circle-n ,i ,x ,y ,r ,(car (cddddr param))))))) (draw-circle i1 150 200 60) (draw-circle i1 350 200 100 8)
Это работает(хотя можно было вместо макроса написать обычную функцию), но создавать такой «костыль» для каждой функции, для которой возможно использование аргументов по умолчанию, крайне не удобно, и это пожалуй недостаток языка. Впрочем как мы увидим в дальнейшем, вполне исправимый! 😉
Ещё один недостаток написанных функций состоит в том, что данные в них не сгруппированы в абстракции, есть отдельные координаты, но они не сформированы в точки, и абстракции точки очень не хватает, для дальнейшего построения функций. В очень плохом стиле определена функция рисования окружности, в которой происходит расчёт координат контура и его рисование, с такими «окружностями» не возможно работать, рисование и расчёт объединены в одном месте, это надо обязательно исправлять. И обо всём этом мы поговорим в следующей статье.
ссылка на оригинал статьи https://habr.com/ru/articles/857918/
Добавить комментарий