GIMP Script-Fu Первый Дан. Погружение в программирование графики

от автора

Первым делом в окне 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/


Комментарии

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

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