* Tested in GIMP 2.99.14 *

Copies all visible to a new image and scales that, preserving transparency. The script will detect a layer called “crop” and uses the mask of that layer as a way to non-destructively crop the copy. It can also use the active selection as a cropping guide. The new image is named after the original with a “_scaled” postfix.

The plug-in should appear in the Image menu.

To download simple-scale.scm
…follow the link, right click the page, Save as simple-scale.scm, in a folder called simple-scale, in a GIMP plug-ins location. In Linux, set the file to be executable.

#!/usr/bin/env gimp-script-fu-interpreter-3.0

(define (script-fu-simple-scale img drawables scaleX scaleY pix pX pY crop)
  (let*
    (
      (width 0)(height 0)(cropMsk 0)(cropBx 0)(opac 0)
      (dstImg 0)(dstL 0)(fileInfo (get-image-file-info img))(safeName "")
      (fileNoExt (vector-ref fileInfo 2))(ScAdj 0)(brkTok DIR-SEPARATOR)
      (filePath (vector-ref fileInfo 3))(fileBase (vector-ref fileInfo 1))
      (mode INTERPOLATION-CUBIC) ; LINEAR ; CUBIC ; NOHALO ; LOHALO ; NONE
    )

    (gimp-context-push)

    ; look for a cropping layer and create a selection from its mask
    (set! cropBx (car(gimp-image-get-layer-by-name img "crop")))
    (if (> cropBx 0 ) (set! cropMsk (car(gimp-layer-get-mask cropBx))))

    (when (and (< cropMsk 0 ) (= crop 1))
      (gimp-message "make a masked layer that frames the image called \"crop\"")
    )

    (when (> cropMsk 0 )
      (when (> crop 0)
        (gimp-image-select-item img CHANNEL-OP-REPLACE cropMsk)
        (gimp-selection-invert img )
      )
      (when (= crop 0)
        (set! opac (car (gimp-layer-get-opacity cropBx)))
        (gimp-layer-set-opacity cropBx 0)
      )
    )

    ; copy visible in selected area
    (gimp-edit-copy-visible img)

    ; restore opacity if crop box was ignored
    (if (> opac 0) (gimp-layer-set-opacity cropBx opac))
    (gimp-selection-none img )

    (set! dstImg (car (gimp-edit-paste-as-new-image)))
    (set! width (car (gimp-image-get-width dstImg)))
    (set! height (car (gimp-image-get-height dstImg)))

    ; adjust scale factors for rounded-up resolution
    (set! ScAdj (percent-to-resolution scaleX scaleY width height))
    (set! width (car ScAdj))
    (set! height (cadr ScAdj))
    (if (> pix 0)(set! width pX))(if (> pix 0)(set! height pY))

    (gimp-context-set-interpolation mode)
    (if (< width 1)(set! width 1))(if (< height 1)(set! height))
    (gimp-image-scale dstImg width height)

    ; if the file has a save name, give it a new safe name
    (when (= (length (strbreakup fileNoExt "_scaled")) 1 )
      (set! safeName (string-append filePath brkTok fileNoExt "_scaled.xcf"))
      (gimp-image-set-file dstImg safeName)
    )

    ; if the file already has a safe name, don't repeat
    (when (> (length (strbreakup fileNoExt "_scaled")) 1 )
      (set! safeName (string-append filePath brkTok fileNoExt ".xcf"))
      (gimp-image-set-file dstImg safeName)
    )

    (set! dstL (vector-ref (cadr(gimp-image-get-selected-layers dstImg))0))
    (gimp-item-set-name dstL "scaled-copy")
    (gimp-layer-set-composite-space dstL LAYER-COLOR-SPACE-RGB-PERCEPTUAL)
    (gimp-edit-copy-visible dstImg)
    (gimp-image-clean-all dstImg)
    (gimp-display-new dstImg)
    (gimp-context-pop)

  )
)

(define debug #f)

(script-fu-register-filter "script-fu-simple-scale"
 "Simple Scale"
 "Creates a scaled copy of all visible in a new image"
 "Mark Sweeney"
 "Under GNU GENERAL PUBLIC LICENSE Version 3"
 "2023"
 "*"
 SF-ONE-OR-MORE-DRAWABLE
 SF-ADJUSTMENT "Scale X %" (list 50 1 10000 1 10 0 SF-SPINNER)
 SF-ADJUSTMENT "Scale Y %" (list 50 1 10000 1 10 0 SF-SPINNER)
 SF-TOGGLE     "By Pixel"             FALSE
 SF-ADJUSTMENT "Pixel Width" (list 512 1 10000 1 10 0 SF-SPINNER)
 SF-ADJUSTMENT "Pixel Height" (list 512 1 10000 1 10 0 SF-SPINNER)
 SF-TOGGLE     "Use Crop Layer"             TRUE
)
(script-fu-menu-register "script-fu-simple-scale" "<Image>/Image")



; copyright 2023, Mark Sweeney, Under GNU GENERAL PUBLIC LICENSE Version 3

; utility functions
(define (boolean->string bool) (if bool "#t" "#f"))

(define (exit msg)
  (gimp-message-set-handler 0)
  (gimp-message (string-append " >>> " msg " <<<"))
  (gimp-message-set-handler 2)
  (quit)
)

(define (here x)(gimp-message(string-append " >>> " (number->string x) " <<<")))


; finds the full file name, base name, stripped name, and path of a given image
; returns a vector list ("/here/myfile.xcf" "myfile.xcf" "myfile" "/here")
(define (get-image-file-info img)
  (let*
    (
      (fNme "")(fBse "")(fwEx "")(fPth "")(usr "")(strL "")
      (brkTok DIR-SEPARATOR)
    )

    (if (equal? "/" brkTok)(set! usr(getenv"HOME"))(set! usr(getenv"HOMEPATH")))

    (when (> (car (gimp-image-id-is-valid img)) 0)
      (when (not(equal? (car(gimp-image-get-file img)) ""))
        (set! fNme (car(gimp-image-get-file img)))
        (set! fBse (car (reverse (strbreakup fNme brkTok))))
        (set! fwEx (car (strbreakup fBse ".")))
        (set! fPth (reverse (cdr(reverse (strbreakup fNme brkTok)))))
        (set! fPth (unbreakupstr fPth brkTok))
      )

      (when (equal? (car(gimp-image-get-file img)) "")
        (set! fNme (string-append usr brkTok "Untitled.xcf"))
        (set! fBse (car (reverse (strbreakup fNme brkTok))))
        (set! fwEx (car (strbreakup fBse ".")))
        (set! fPth usr)
      )
    )

    (vector fNme fBse fwEx fPth)
  )
)


; given a 1-100 scale, and the current dimensions, it returns the new size
; (1-100, 1-100, current width, current height)
(define (percent-to-resolution scaleX scaleY width height)
  (let*
    (
      (scaleX (/ scaleX 100.0))
      (scaleY (/ scaleY 100.0))
      (width (round (* width scaleX)))
      (height (round (* height scaleY)))
    )

    (list width height)
  )
)



<
Previous Post
Precise Scale
>
Next Post
Proxy