Библиотека функций к Script-fu
Итак, настоящая киллер функция по отображению изображений это gimp-item-transform-matrix
. Именно на её основе мы и будем строить все отображения изображений в нашем проекте.
Для использования функции GIMP gimp-item-transform-matrix
, осуществляющей линейное преобразования заданного отображаемого объекта, нам нужна структура в которой мы могли бы хранить матрицу преобразования. На основе этой структуры, мы построим абстракцию линейного двумерного преобразования на плоскости. Я посмотрел все эти преобразования и увидел, что матрица имеет всего 6 значимых полей, остальные два ноль и одно единица, поэтому наша структура будет хранить всего 6 полей.
подготовка
;;(define path-home "D:") ;;для виндовс (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 "struct2.scm")) (load (string-append path-lib "point.scm"))
(struct tr2d (m11 m12 m21 m22 dx dy))
Теперь мы можем определить операции преобразования. Первым делом определим операцию преобразования точки.
(define (p-tr2d p tr) (let ((x (p-x p)) (y (p-y p))) (p! (+ (* x (tr2d-m11 tr)) (* y (tr2d-m21 tr)) (tr2d-dx tr)) (+ (* x (tr2d-m12 tr)) (* y (tr2d-m22 tr)) (tr2d-dy tr)))))
Вообще говоря, если понимать под точкой, вектор, а преобразование — это матрица, то это умножение вектора на матрицу, с некоторыми особенностями, точка то у нас на плоскости имеет 2 координаты, а вектор умножается 3х значный, одна координата которого всегда 1, которая не храниться в составе точки.
Комбинация преобразований будет тоже двумерным преобразованием, умножение трёхмерных матриц, с особенностями используемой нами матричной модели преобразований, хранятся всего 6 элементов.
(define (comb-tr2d m n) (let ((m11 (tr2d-m11 m)) (m12 (tr2d-m12 m)) (m21 (tr2d-m21 m)) (m22 (tr2d-m22 m)) (mx (tr2d-dx m)) (my (tr2d-dy m)) (n11 (tr2d-m11 n)) (n12 (tr2d-m12 n)) (n21 (tr2d-m21 n)) (n22 (tr2d-m22 n)) (nx (tr2d-dx n)) (ny (tr2d-dy n))) (tr2d! (+ (* m11 n11) (* m12 n21)) (+ (* m11 n12) (* m12 n22)) (+ (* m21 n11) (* m22 n21)) (+ (* m21 n12) (* m22 n22)) (+ (* mx n11) (* my n21) nx) (+ (* mx n12) (* my n22) ny))))
Для отладки может понадобиться печать преобразования.
(define (prn-tr2d m) (display "[ ") (display (tr2d-m11 m)) (display " ") (display (tr2d-m12 m)) (display " 0 ]") (newline) (display "[ ") (display (tr2d-m21 m)) (display " ") (display (tr2d-m22 m)) (display " 0 ]") (newline) (display "[ ") (display (tr2d-dx m)) (display " ") (display (tr2d-dy m)) (display " 1 ]") (newline))
Теперь опишем несколько конструкторов создающих простые линейные преобразования.
;; преобразование вращения. (define (make-tr2d-rot alfa) (let* ((alfa-r (gr2rad alfa)) (cos-a (cos alfa-r)) (sin-a (sin alfa-r))) (tr2d! cos-a sin-a (- sin-a) cos-a 0 0))) ;;преобразование перемещения (define (make-tr2d-move x y) (tr2d! 1 0 0 1 x y)) ;;преобразование масштабирования (define (make-tr2d-scale sx sy) (tr2d! sx 0 0 sy 0 0)) ;;сдвиг вдоль оси х на заданный угол. (define (make-tr2d-shear-x angle) (tr2d! 1 (tan (gr2rad angle)) 0 1 0 0)) ;;сдвиг вдоль оси у на угол (define (make-tr2d-shear-y angle) (tr2d! 1 0 (tan (gr2rad angle)) 1 0 0)) ;;преобразование отражения относительно оси х (define (make-tr2d-reflect-x) (tr2d! 1 0 0 -1 0 0)) ;;преобразование отражения относительно оси y (define (make-tr2d-reflect-y) (tr2d! -1 0 0 1 0 0))
Теперь на основе построенной абстракции, можно написать функцию преобразования изображения, принимающую данное преобразование в качестве аргумента.
(define (draw-from-image-trans dest src tr) (let* ((dw2 (car (gimp-layer-new-from-drawable (car (gimp-image-get-active-layer src)) dest))) (m11 (tr2d-m11 tr)) (m12 (tr2d-m12 tr)) (m21 (tr2d-m21 tr)) (m22 (tr2d-m22 tr)) (mx (tr2d-dx tr)) (my (tr2d-dy tr))) (gimp-image-add-layer dest dw2 0) (gimp-item-transform-matrix dw2 m11 m21 mx m12 m22 my 0 0 1) (gimp-image-merge-visible-layers dest CLIP-TO-IMAGE) ))
Все эти преобразования определены в файле tr2d.scm
. Давайте выполним демонстрационный пример.
подготовка
;;догрузим необходимые библиотеки. (load (string-append path-lib "img.scm")) ;;(load (string-append path-lib "img2.6.scm")) ;;для гимп 2.6 (load (string-append path-lib "tr2d.scm")) (load (string-append path-lib "defun.scm")) ;;для загрузки draw-from-image-trans (load (string-append path-lib "brush.scm")) (load (string-append path-lib "fig.scm")) ;; или определите её через консоль. ;;(load (string-append path-lib "brush-2.6.scm")) ;;для гимп 2.6 ;;(load (string-append path-lib "fig2.6.scm")) ;; или определите её через консоль.
;;подготовимся к рисованию (define i1 (create-1-layer-img 640 480)) (define isource1 (car (file-png-load 1 (string-append path-work "i1.png") "i1"))) ;;определим две трансформации (define tr2d-pos1 (make-tr2d-move 100 100)) ;;перемещение в точку 100, 100 (define tr2d30 (make-tr2d-rot 30)) ;; поворот на 30 градусов. (draw-from-image-trans i1 isource1 tr2d30) ;;поворот в начале координат (draw-from-image-trans i1 isource1 (comb-tr2d tr2d30 tr2d-pos1 )) ;;поворот и перенос
Вот ещё пара примеров использования преобразований:
;;увеличим ширину в 2 раза, а высоту уменьшим на половину, повернем на 30 гр. и переместим. (draw-from-image-trans i1 isource1 (comb-tr2d (comb-tr2d (make-tr2d-scale 2 0.5) tr2d30) tr2d-pos1 )) ;;уменьшим ширину в 2 раза, а высоту увеличим в полтора, повернем на 45 гр. и переместим. (draw-from-image-trans i1 isource1 (comb-tr2d (comb-tr2d (make-tr2d-scale 0.5 1.5) (make-tr2d-rot 45)) (make-tr2d-move 200 100) ))
Получилась очень удачная функция заменяющая все ранее написанные функции отображения и преобразования изображений. Единственно, что мне не нравиться. это каскадные вызовы функции comb-tr2d
, давайте перепишем её, чтобы она принимала не произвольное количество аргументов, обрабатывая произвольную последовательность трансформаций.
;;эта функция есть в util.scm (define (fold f init lst) (do ((rez init (f rez (car l))) (l lst (cdr l))) ((null? l) rez))) (define (comb-tr2d . lst) (let ((tr (lambda (m n) (let ((m11 (tr2d-m11 m)) (m12 (tr2d-m12 m)) (m21 (tr2d-m21 m)) (m22 (tr2d-m22 m)) (mx (tr2d-dx m)) (my (tr2d-dy m)) (n11 (tr2d-m11 n)) (n12 (tr2d-m12 n)) (n21 (tr2d-m21 n)) (n22 (tr2d-m22 n)) (nx (tr2d-dx n)) (ny (tr2d-dy n))) (tr2d! (+ (* m11 n11) (* m12 n21)) (+ (* m11 n12) (* m12 n22)) (+ (* m21 n11) (* m22 n21)) (+ (* m21 n12) (* m22 n22)) (+ (* mx n11) (* my n21) nx) (+ (* mx n12) (* my n22) ny)))))) (fold tr (car lst) (cdr lst))))
Это практически таже самая функция, которая была ранее, только её тело перенесли в лямбду, и последовательно с помощю fold
применили ко всем преобразованиям из списка аргументов.
Для демонстрации работы нашей функции, давайте напишем функцию рисования серий изображений, расположенных по кругу.
(define (rot-img3 dest src x y sc-x sc-y shear-x shear-y num) (gimp-image-undo-group-start dest) (let ((a1 (floor (/ 360 num))) (a-cur 0) (tr-xy (make-tr2d-move x y)) (tr-pre (comb-tr2d (make-tr2d-scale sc-x sc-y) (make-tr2d-shear-y shear-x) (make-tr2d-shear-x shear-y)))) (while (< a-cur 360) (draw-from-image-trans i1 isource1 (comb-tr2d tr-pre (make-tr2d-rot a-cur) tr-xy)) (set! a-cur (+ a-cur a1)))) (gimp-image-undo-group-end dest) )
Обратите внимание, мы разделили создание трансформаций, те из них которые не меняются, мы создаём один раз и затем просто используем в окончательной комбинации трансформаций.
Выполним несколько примеров:
(rot-img3 i1 isource1 100 200 1 1 0 0 6) (rot-img3 i1 isource1 100 200 1 0.5 20 0 6) (rot-img3 i1 isource1 330 200 1.3 0.4 20 10 6) (rot-img3 i1 isource1 215 370 0.5 0.8 10 20 12) (rot-img3 i1 isource1 30 370 0.5 0.8 10 20 1) (rot-img3 i1 isource1 30 370 0.5 0.8 10 20 3) (rot-img3 i1 isource1 500 50 1 1 0 0 1)
Таким образом мы создали удобный инструмент для линейных преобразований на плоскости, используя который мы можем задать любое линейное преобразование, а функции, такие как gimp-item-transform-matrix
позволят преобразовать и отобразить любое заданное изображение.
На этом с преобразованиями мы заканчиваем и в следующей статье ещё немного поработаем с самим языком Script-fu.
ссылка на оригинал статьи https://habr.com/ru/articles/861000/
Добавить комментарий