455 lines
No EOL
16 KiB
Scheme
455 lines
No EOL
16 KiB
Scheme
; Test concepts/model of Selection and SelectionMask
|
|
;
|
|
; Explains methods, coordinate systems, rects/bounds
|
|
|
|
|
|
; Disambiguate word "selection"
|
|
|
|
; A user of the GUI can "select" an area using a selection Tool.
|
|
; The act with a tool and the area the tool shows might be selected
|
|
; is distinct from the actual Selection object.
|
|
; Also, the methods of the API are similar but distinct from user actions.
|
|
|
|
|
|
; Disambiguate Image and Canvas
|
|
;
|
|
; The Image is not the set of pixels in all drawables;
|
|
; some drawables can be off the Canvas.
|
|
; The Canvas is the rectangle that composes and is seen.
|
|
; The size of Image is the size of the Canvas, not the size that encloses all drawables.
|
|
|
|
; Selection
|
|
;
|
|
; Is a set of pixels.
|
|
; The set can be empty, aka Selection is None.
|
|
;
|
|
; The pixels in the set are a subset of the pixels covered by SelectionMask:
|
|
; the pixels in SelectionMask that are non-zero.
|
|
;
|
|
; Methods to change the Selection usually change the SelectionMask.
|
|
; Some methods change the Selection by shape.
|
|
; e.g. gimp-image-select-rectangle see GISR
|
|
; Some methods make the Selection None and All.
|
|
|
|
|
|
; SelectionMask
|
|
;
|
|
; Is-a Channel, which is-a Drawable.
|
|
; A Drawable is rectangular.
|
|
;
|
|
; Each image has its own SelectionMask.
|
|
; The SelectionMask always exists for an image.
|
|
; The coordinates of the SelectionMask are in the Image coordinate system.
|
|
; There are no methods for offsetting a channel (setting its UL origin).
|
|
; The SelectionMask is the always same size as the Image.
|
|
;
|
|
; The depth of the SelectionMask is the same as other channels (RGB)
|
|
; For example, when the image is 16-bit, the SelectionMask is 16-bit.
|
|
|
|
|
|
|
|
; gimp-image-get-selection
|
|
;
|
|
; GIGS-1 gimp-image-get-selection returns a Channel
|
|
; GIGS-2 the SelectionMask is same size as Image, when new image
|
|
; GIGS-3 the SelectionMask does not cover parts of layers outside the image
|
|
; GIGS-4 the SelectionMask grows to size of a resized enlarged image
|
|
; GIGS-5 gimp-item-id-is-selection is True for the SelectionMask
|
|
|
|
; gimp-image-select-rectangle
|
|
|
|
; GISR-1 selecting a shape outside the image changes the Selection to None
|
|
; GISR-2 selecting a shape outside the image does not change the size of SelectionMask
|
|
|
|
|
|
; gimp-drawable-mask-intersect
|
|
;
|
|
; Computes the intersection of the SelectionMask drawable with the given drawable.
|
|
; The returned bounds are the intersection OR the bounds of the drawable.
|
|
; The returned boolean with strange meaning...
|
|
; GDMI-1 The boolean is TRUE when Selection is not None and it intersects the drawable.
|
|
; GDMI-2 The boolean is TRUE when Selection is None.
|
|
; GDMI-3 The boolean is FALSE when Selection is not None and it does not intersect drawable.
|
|
; GDMI-4 When Selection is None, bounds of intersection is size of layer,
|
|
; but all corresponding values of the SelectionMask are zero.
|
|
; GDMI-5 When Selection is not None, but not covers layer, bounds of intersection is size of intersection.
|
|
; GDMI-6 When Selection is not None, but covers layer, bounds of intersection is size of layer,
|
|
; and corresponding values of the SelectionMask are non-zero.
|
|
|
|
|
|
; gimp-selection-value
|
|
;
|
|
; Returns the value of the SelectionMask at the given coordinates.
|
|
;
|
|
; GSV-1 When the coordinates are outside the bounds of the image (canvas)
|
|
; the value is zero.
|
|
|
|
|
|
; gimp-drawable-mask-bounds
|
|
;
|
|
; GDMB
|
|
; NOT TESTED, generally low usefulness
|
|
|
|
|
|
; gimp-selection-bounds
|
|
;
|
|
; Is a method on Selection
|
|
;
|
|
; Returns:
|
|
; Boolean whether the SelectionMask has any non-zero values, i.e. is-a Selection
|
|
; UL and LR coordinates of a bounding rect of the Selection, in image coordinates,
|
|
; OR the bounding rect of the Image !!!
|
|
;
|
|
; GSB-1 The bounds of the Selection on a new image without layers is the size of the image.
|
|
; GSB-2 A new image without layers has no selection, returned boolean is False.
|
|
|
|
; The rect need not cover the origin (of the image i.e. canvas).
|
|
;
|
|
; When the boolean is True, the rect covers all selected pixels inside the image i.e. canvas.
|
|
; That rect in the SelectionMask has some values not zero (somewhat selected).
|
|
; That rect in the SelectionMask may have some values of 0 (not selected).
|
|
; The rect is tight around selected pixels.
|
|
; It has no slices that are entirely zero (not selected.)
|
|
|
|
; When the boolean is False, the rect has no meaning.
|
|
; The Rect covers the Image (Canvas.)
|
|
; When the boolean is False, gimp-selection-is-empty returns True.
|
|
|
|
|
|
; gimp-drawable-get-offsets
|
|
;
|
|
; GDGO-1 a drawable can be created with negative offsets from image
|
|
; It can be totally off the image (canvas)
|
|
|
|
|
|
|
|
|
|
(script-fu-use-v3)
|
|
|
|
|
|
; Testing methods.
|
|
; More abstract than Gimp calls.
|
|
|
|
; assert the SelectionMask is given width
|
|
; Requires testSelectionMask is defined globally
|
|
(define (testSelectionMaskWidth width)
|
|
(assert (equal? (gimp-drawable-get-width testSelectionMask)
|
|
width)))
|
|
|
|
; Assert the Selection is empty.
|
|
; Requires testImage is defined globally.
|
|
; This tests that selection-is-empty if and only if the boolean
|
|
; result of gimp-selection-bounds is #f.
|
|
; !!! The bounds returned by gimp-selection-bounds are another matter
|
|
; and may be non-zero when Selection is empty.
|
|
(define (testSelectionIsEmpty)
|
|
(assert `(gimp-selection-is-empty ,testImage))
|
|
(assert `(not (car (gimp-selection-bounds ,testImage)))))
|
|
|
|
; Assert the bounds of the Selection
|
|
; Requires testImage is defined globally.
|
|
; Abstracts away that the bounds are a separate return value of gimp-selection-bounds.
|
|
; bounds is-a list of four numbers.
|
|
; FIXME this throws "Unbound variable" for the backtick `, so we use explicit quasiquote.
|
|
(define (testSelectionBounds bounds)
|
|
(assert (quasiquote (equal? (cdr (gimp-selection-bounds ,testImage))
|
|
bounds))))
|
|
|
|
|
|
; Image has-as CoordinateSystem
|
|
; Origin of the CoordinateSystem is not exposed in the API
|
|
|
|
(define testImage (gimp-image-new 11 12 RGB))
|
|
|
|
; New image has the width given at creation.
|
|
; The image has no layers; this is independent of layers.
|
|
(assert `(= (gimp-image-get-width ,testImage)
|
|
11))
|
|
|
|
|
|
(test! "SelectionMask on new image")
|
|
|
|
; New image has a SelectionMask.
|
|
; Note the API calls it a "selection" !!!
|
|
(define testSelectionMask (gimp-image-get-selection testImage))
|
|
|
|
(test! "GIGS-1 gimp-image-get-selection returns a Channel")
|
|
(assert `(gimp-item-id-is-channel ,testSelectionMask))
|
|
(test! "GIGS-5 gimp-item-id-is-selection is True for the SelectionMask")
|
|
(assert `(gimp-item-id-is-selection ,testSelectionMask))
|
|
|
|
; But a new image has no layers (not tested.)
|
|
|
|
; A new image has a SelectionMask but no Selection.
|
|
; These are distinct: the Selection is created by a user or script separately.
|
|
|
|
; In v3 binding is a boolean.
|
|
; (In v2 binding, is numeric 0 or 1)
|
|
(test! "GSB-2 A new image without layers has no selection, returned boolean is False.")
|
|
(assert `(not (car (gimp-selection-bounds ,testImage))))
|
|
|
|
(test! "GSB-1 Bounds of selection are the image size, when no selection.")
|
|
; Cdr (the rest of) gimp-selection-bounds are four values of a Bounds tuple.
|
|
; A SelectionMask has the same coordinate system as the Image (same origin.)
|
|
; SelectionMask for new image is same size (bounds) as Image.
|
|
; It is NOT zero width.
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 11 12)))
|
|
;(testSelectionBounds (list 0 0 11 12))
|
|
|
|
(test! "GIGS-2 the SelectionMask is same size as Image, when new image")
|
|
; The offsets are 0,0
|
|
(assert (equal? (gimp-drawable-get-offsets testSelectionMask)
|
|
'(0 0)))
|
|
; and the width is same as image width
|
|
(testSelectionMaskWidth 11)
|
|
|
|
|
|
|
|
(test! "selection-none")
|
|
(assert `(gimp-selection-none ,testImage))
|
|
|
|
(testSelectionIsEmpty)
|
|
|
|
; but does not change the bounds of the Selection
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 11 12)))
|
|
|
|
|
|
|
|
(test! "select-all")
|
|
(assert `(gimp-selection-all ,testImage))
|
|
|
|
; after select-all, selection-bounds indicates selection created
|
|
(assert `(car (gimp-selection-bounds ,testImage)))
|
|
; Remember, the image has no layers, so the selection-bounds are nearly useless
|
|
; for operations.
|
|
|
|
; and the bounds are same as image
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 11 12)))
|
|
|
|
; and now is-empty is false
|
|
(assert `(not (gimp-selection-is-empty ,testImage)))
|
|
; but the "selection" is meaningless, no drawing op will have effect
|
|
; since there are no layers in the image.
|
|
|
|
|
|
; State: image has no layers, and Selection==All
|
|
|
|
(test! "select rect outside canvas to upper left, on image with no layers")
|
|
; This rect is outside the canvas
|
|
(gimp-image-select-rectangle testImage CHANNEL-OP-REPLACE -1 -1 1 1)
|
|
|
|
; Since outside the canvas, this does not change the SelectionMask size
|
|
(test! "GISR-2 selecting a shape outside the image does not change the size of SelectionMask")
|
|
(testSelectionMaskWidth 11)
|
|
|
|
; But it does replace the SelectionMask values and makes the Selection empty
|
|
(test! "GISR-1 selecting a shape outside the image changes the Selection to None")
|
|
(testSelectionIsEmpty)
|
|
; Since Selection is empty, bounds of Selection are same (but meaningless)
|
|
|
|
|
|
(test! "Add layer larger than canvas")
|
|
; Adding a layer larger than the current canvas
|
|
; does not change the size of the Image
|
|
; nor change the Selection
|
|
; nor change the SelectionMask
|
|
(define testLayer1 (testing:layer-new-inserted testImage))
|
|
; The layer is 21,22 and is inserted in image
|
|
|
|
; The image size has not grown.
|
|
(assert `(= (gimp-image-get-width ,testImage)
|
|
11))
|
|
|
|
; The Selection has not grown; it does not cover the layer.
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 11 12)))
|
|
|
|
(test! "GIGS-3 the SelectionMask does not cover parts of layers outside the image")
|
|
; The layer is width 21 but the SelectionMask is only 11
|
|
(testSelectionMaskWidth 11)
|
|
|
|
|
|
(testSelectionIsEmpty)
|
|
|
|
(assert `(gimp-selection-none ,testImage))
|
|
(test! "GDMI-2 When Selection is None, GDMI boolean is TRUE")
|
|
(assert `(car (gimp-drawable-mask-intersect ,testLayer1)))
|
|
|
|
(test! "GDMI-4 When Selection is None, intersection bounds are size of layer.")
|
|
; In coordinate system of layer.
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer1))
|
|
'(0 0 21 22)))
|
|
|
|
(assert `(gimp-selection-all ,testImage))
|
|
; Selection is All, but the SelectionMask is size of canvas, not the larger layer.
|
|
|
|
; select all has NOT grown the Selection to size of layer;
|
|
; the Selection still only covers the canvas peephole.
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 11 12)))
|
|
|
|
(test! "GSV-1 value of the selection mask is 'not selected' i.e. 0 when out of bounds of image")
|
|
; in the LR quadrant of the layer, but outside the image (canvas.)
|
|
(assert `(= (gimp-selection-value ,testImage 11 12)
|
|
0))
|
|
|
|
(test! "GDMI-1 When is a Selection and intersects layer, GDMI boolean is TRUE")
|
|
(assert `(car (gimp-drawable-mask-intersect ,testLayer1)))
|
|
|
|
(test! "GDMI-5 When Selection is not None, but not covers layer, bounds of intersection is size of intersection")
|
|
; In coordinate system of layer.
|
|
; !!! Layer is width 21 but SelectionMask is width 11
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer1))
|
|
'(0 0 11 12)))
|
|
|
|
|
|
(test! "Resize image (canvas) to layers")
|
|
(assert `(gimp-image-resize-to-layers ,testImage))
|
|
|
|
; The Selection has NOT grown.
|
|
; The Selection is still coordinates of the former canvas size,
|
|
; which is smaller than the layer size.
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 11 12)))
|
|
|
|
(test! "GIGS-4 the SelectionMask grows to size of a resized enlarged image")
|
|
(testSelectionMaskWidth 21)
|
|
|
|
|
|
|
|
(test! "Select all on image with one layer.")
|
|
; select all succeeds
|
|
(assert `(gimp-selection-all ,testImage))
|
|
|
|
; The Selection has grown
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 21 22)))
|
|
|
|
|
|
|
|
(test! "Intersection of the Selection with the layer")
|
|
; Intersection is not empty (#t)
|
|
(assert `(car (gimp-drawable-mask-intersect ,testLayer1)))
|
|
|
|
(test! "GDMI-6 When Selection is not None, but covers layer, bounds of intersection is size of layer.")
|
|
; Intersection rect is entire layer
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer1))
|
|
'(0 0 21 22)))
|
|
|
|
|
|
; the value of the selection mask is "entirely selected" i.e. 255
|
|
; in the LR quadrant of the layer, now inside the image (canvas.)
|
|
(assert `(= (gimp-selection-value ,testImage 11 12)
|
|
255))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(test! "Add layer left and above the canvas")
|
|
; L2
|
|
; +
|
|
; | L1 |
|
|
(define testLayer2 (testing:layer-new-inserted testImage))
|
|
(test! "GDGO-1 a drawable can be created with negative offsets from image")
|
|
(gimp-layer-set-offsets testLayer2 -21 -22)
|
|
|
|
; The Selection has NOT grown
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 21 22)))
|
|
; Neither the SelectionMask nor the Image is grown to cover the layer off canvas.
|
|
; TODO
|
|
|
|
(test! "Resize image (canvas) to upper left, to TWO layers")
|
|
; + |
|
|
; | L2 |
|
|
; | L1 |
|
|
(assert `(gimp-image-resize-to-layers ,testImage))
|
|
|
|
(test! "The Selection HAS NOT grown")
|
|
; The origin of the Image has changed;
|
|
; and the UL LR of the selection bounds.
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(21 22 42 44)))
|
|
|
|
(test! "The SelectionMask is larger")
|
|
; The SelectionMask extends from image origin to cover testLayer1,
|
|
; which is entirely selected.
|
|
(testSelectionMaskWidth 42)
|
|
;(assert (equal? (gimp-drawable-get-width testSelectionMask) 42))
|
|
|
|
(test! "The SelectionMask still intersects testLayer1")
|
|
; Intersection is not empty (#t)
|
|
(assert `(car (gimp-drawable-mask-intersect ,testLayer1)))
|
|
|
|
(test! "The intersection has same UL coordinates, in the frame of the Drawable")
|
|
; Intersection is width of layer
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer1))
|
|
'(0 0 21 22)))
|
|
; Adding offsets of the layer to the intersection UL coordinates
|
|
; will give coordinates of the intersection UL in the image (canvas)
|
|
|
|
; Selection does not intersect Layer2.
|
|
; (testLayer1 is selected, but not testLayer2. The SelectionMask covers both.)
|
|
(test! "GDMI-3 The boolean is FALSE when Selection is not None and it does not intersect drawable.")
|
|
(assert `(not (car (gimp-drawable-mask-intersect ,testLayer2))))
|
|
|
|
|
|
(test! "Select all of image with TWO layers")
|
|
(assert `(gimp-selection-all ,testImage))
|
|
|
|
(test! "The Selection has grown")
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 42 44)))
|
|
|
|
(test! "The SelectionMask has not grown")
|
|
(assert `(equal? (cdr (gimp-selection-bounds ,testImage))
|
|
'(0 0 42 44)))
|
|
|
|
(test! "The intersection with testLayer1 is the same")
|
|
; it is still in coordinates of the Drawable.
|
|
; testLayer1 UL is at 21,22 of the image and its dimensions are 21,22
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer1))
|
|
'(0 0 21 22)))
|
|
|
|
(test! "The intersection with testLayer2 also is in coordinates of the Drawable")
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer2))
|
|
'(0 0 21 22)))
|
|
|
|
|
|
|
|
(test! "Select a rect outside the image (canvas) to the upper left")
|
|
(gimp-image-select-rectangle testImage CHANNEL-OP-REPLACE -1 -1 1 1)
|
|
|
|
(test! "GISR-2 selecting a shape outside the image does not change size of SelectionMask")
|
|
(testSelectionMaskWidth 42)
|
|
|
|
(test! "GISR-1 selecting a shape outside the image changes the Selection to None")
|
|
; It sets the SelectionMask to all zeroes and makes the Selection empty.
|
|
; When a user drags out the same shape, the shape is visible in the GUI,
|
|
; but it is not the selection ()
|
|
(testSelectionIsEmpty)
|
|
|
|
; Since Selection is None, intersection of SelectionMask with drawable is
|
|
; still the size of the drawables, in their coordinate systems.
|
|
(test! "GDMI-4 When Selection is None, bounds of intersection is size of layer")
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer1))
|
|
'(0 0 21 22)))
|
|
(assert `(equal? (cdr (gimp-drawable-mask-intersect ,testLayer2))
|
|
'(0 0 21 22)))
|
|
|
|
|
|
; TODO test shrinking an image to size of a smaller layer.
|
|
; expect SelectionMask shrinks
|
|
|
|
; TODO test resizing image to cover layer to LR of image
|
|
; expect SelectionMask grows to image size
|
|
|
|
; TODO test bit-depth of selection mask
|
|
; TODO test valus of selection mask in range 0-255
|
|
|
|
(gimp-display-new testImage) |