Difference between revisions of "User:Pedro Oval/GIMP Layers to SL Animated Texture"

From Second Life Wiki
Jump to navigation Jump to search
m (Add note that Unoptimize fixes the issue.)
(Update to 1.1pre2)
Line 1: Line 1:
== Development version ==
== Development version ==
<scheme>
<scheme>
; Layers to SL Texture Animation, version 1.1pre1.
; Layers to SL Texture Animation, version 1.1pre2.
;
;
; Written by Pedro Oval, 2010-12-04.
; Written by Pedro Oval, 2010-12-04.
Line 8: Line 8:


(define (script-fu-layers-to-sl-anim curimg curdrawable xframes yframes)
(define (script-fu-layers-to-sl-anim curimg curdrawable xframes yframes)
  ; Read the layer IDs and the total number of layers from the original image.
   (define layerset (gimp-image-get-layers curimg))
   (define layerset (gimp-image-get-layers curimg))
   (define numframes (car layerset))
   (define numframes (car layerset))
   (set! layerset (cadr layerset))
   (set! layerset (cadr layerset))
   (define framexsize (car (gimp-image-width curimg)))
 
   (define frameysize (car (gimp-image-height curimg)))
  ; The frame size X and Y is the size of the original image.
   (define framesizex (car (gimp-image-width curimg)))
   (define framesizey (car (gimp-image-height curimg)))
 
  ; Get the image type; indexed images need special treatment.
   (define imgtype (car (gimp-image-base-type curimg)))
   (define imgtype (car (gimp-image-base-type curimg)))


  ; Check if the given number of horizontal x vertical frames matches
  ; the number of layers in the original image. If yes, proceed; if not,
  ; show an error message.
   (if (= numframes (* xframes yframes))
   (if (= numframes (* xframes yframes))
     (begin
     (begin
       (define img (car (gimp-image-new (* framexsize xframes) (* frameysize yframes) imgtype)))
      ; New image
       (define img (car (gimp-image-new (* framesizex xframes) (* framesizey yframes) imgtype)))
      ; We don't touch the original image, so we start a new undo group
      ; for the new image instead.
       (gimp-image-undo-group-start img)
       (gimp-image-undo-group-start img)
       (if (= imgtype INDEXED)
       (if (= imgtype INDEXED)
         (begin
         (begin
          ; Indexed images need the palette to be copied to the new image.
           (define palette (gimp-image-get-colormap curimg))
           (define palette (gimp-image-get-colormap curimg))
           (gimp-image-set-colormap img (car palette) (cadr palette))))
           (gimp-image-set-colormap img (car palette) (cadr palette))))


      ; Loop betwen 0 and numframes - 1.
       (define frame 0)
       (define frame 0)
       (while (< frame numframes)
       (while (< frame numframes)
        ; For each layer/frame:
         (begin
         (begin
           (define newlayer (car (gimp-layer-new-from-drawable (vector-ref layerset (- numframes frame 1)) img)))
 
           (gimp-image-add-layer img newlayer frame)
          ; Show progress as the current frame relative to the number of frames.
           (gimp-progress-update (/ frame numframes))
 
          ; Read the individual layer ID, starting from the bottom.
          (define oldlayer (vector-ref layerset (- numframes frame 1)))
 
          ; Create new layer as a copy of this one and add it to the image.
          (define newlayer (car (gimp-layer-new-from-drawable oldlayer img)))
           (gimp-image-add-layer img newlayer -1)
 
          ; Add alpha if the layer doesn't have it.
          (if (= (car (gimp-drawable-has-alpha newlayer)) FALSE)
            (gimp-layer-add-alpha newlayer))
 
          ; Calculate the offsets and crop size.
          (define offsets (gimp-drawable-offsets oldlayer))
          (define newsizex (car (gimp-drawable-width oldlayer)))
          (define newsizey (car (gimp-drawable-height oldlayer)))
          (define offsetx (car offsets))
          (define offsety (cadr offsets))
          (if (< offsetx 0)
            (begin
              (set! newsizex (+ newsizex offsetx))
              (set! offsetx 0)))
          (if (< offsety 0)
            (begin
              (set! newsizey (+ newsizey offsety))
              (set! offsety 0)))
          (if (> (+ offsetx newsizex) framesizex)
            (set! newsizex (- framesizex offsetx)))
          (if (> (+ offsety newsizey) framesizey)
            (set! newsizey (- framesizey offsety)))
 
          ; Calculate frame position X and Y.
           (define framey (truncate (/ frame xframes)))
           (define framey (truncate (/ frame xframes)))
           (define framex (- frame (* framey xframes)))
           (define framex (- frame (* framey xframes)))
           (gimp-layer-set-offsets newlayer (* framex framexsize) (* framey frameysize))
          (set! framex (* framex framesizex))
          (set! framey (* framey framesizey))
 
          ; Resize and place the layer.
          (gimp-layer-resize newlayer
                            newsizex
                            newsizey
                            (- (car offsets) offsetx)
                            (- (cadr offsets) offsety))
           (gimp-layer-set-offsets newlayer (+ framex offsetx) (+ framey offsety))
 
          ; Force it visible.
          (gimp-drawable-set-visible newlayer TRUE)
 
          ; All done. Next loop iteration.
           (set! frame (+ frame 1))))
           (set! frame (+ frame 1))))
       (gimp-image-merge-visible-layers img CLIP-TO-IMAGE)
 
       ; Progress 100%
      (gimp-progress-update 1.0)
 
      ; Merge visible layers (all are visible now).
      (define mergedlayer (car (gimp-image-merge-visible-layers img CLIP-TO-IMAGE)))
 
      ; Make the new layer the size of the image.
      (gimp-layer-resize-to-image-size mergedlayer)
 
      ; Convert the image to RGB if it is not already.
       (if (not (= imgtype RGB))
       (if (not (= imgtype RGB))
         (gimp-image-convert-rgb img))
         (gimp-image-convert-rgb img))
      ; All done - close the undo group and add a display for the new image.
       (gimp-image-undo-group-end img)
       (gimp-image-undo-group-end img)
       (gimp-display-new img))
       (gimp-display-new img))
    ; If the frame counts didn't match...
     (gimp-message "Error: Number of layers doesn't match horizontal x vertical frames")))
     (gimp-message "Error: Number of layers doesn't match horizontal x vertical frames")))


Line 50: Line 126:
                     SF-DRAWABLE    "Drawable"    0
                     SF-DRAWABLE    "Drawable"    0
                     SF-ADJUSTMENT  _"# of horizontal frames" '(2 1 1024 1 10 0 1)
                     SF-ADJUSTMENT  _"# of horizontal frames" '(2 1 1024 1 10 0 1)
                     SF-ADJUSTMENT  _"# of vertical frames" '(2 1 1024 1 10 0 1))
                     SF-ADJUSTMENT  _"# of vertical frames"   '(2 1 1024 1 10 0 1))
</scheme>
</scheme>
New in this version with respect to the stable one:
* Add progress indicator.
* Clip each layer to its rectangle.
* Respect the original offsets of layers.
* Do the merge down in the correct order.
* Add alpha to the layers that lack it.
* Set the final layer's size to the image's size.
* Extensively commented.


== TODO list ==
== TODO list ==


* Add option to generate and show the script that will animate the texture with the given parameters. It has the problem that we don't ask for the final frame count, so the real from/to values can be different to the ones that the script can output, which may confuse some users. It will have a checkbox, "Generate script", initially checked, but uncheckable to avoid the annoyance of a dialog at the end.
* Add option to generate and show the script that will animate the texture with the given parameters. It has the problem that we don't ask for the final frame count, so the real from/to values can be different to the ones that the script can output, which may confuse some users. It will have a checkbox, "Generate script", initially checked, but uncheckable to avoid the annoyance of a dialog at the end.
* Perhaps change the strategy to use gimp-image-duplicate to simplify the process. The drawback is that only visible layers get merged down, so either the user should ensure that all layers are visible, or the program should do that.
* Perhaps change the strategy to use gimp-image-duplicate to simplify the process.
* Add note somewhere in the documentation that it assumes that the frames have the size of the original image, even if GIMP layers can exceed that or be smaller. NOTE: For now, Unoptimize does the fixes already in the original image.
* To alleviate that last issue, it would be wise to clip each layer to the rectangle it's assumed to be on, if it's bigger. Using the relative position of each layer in the original image would be wise too.
* Add the opposite conversion: given an animated texture, split it into layers.
* Add the opposite conversion: given an animated texture, split it into layers.
* Automatically remove alpha from the final image if not needed.

Revision as of 09:59, 6 December 2010

Development version

<scheme>

Layers to SL Texture Animation, version 1.1pre2.
Written by Pedro Oval, 2010-12-04.
Donated to the public domain.
Thanks to Digital Dharma for the suggestions.

(define (script-fu-layers-to-sl-anim curimg curdrawable xframes yframes)

 ; Read the layer IDs and the total number of layers from the original image.
 (define layerset (gimp-image-get-layers curimg))
 (define numframes (car layerset))
 (set! layerset (cadr layerset))
 ; The frame size X and Y is the size of the original image.
 (define framesizex (car (gimp-image-width curimg)))
 (define framesizey (car (gimp-image-height curimg)))
 ; Get the image type; indexed images need special treatment.
 (define imgtype (car (gimp-image-base-type curimg)))
 ; Check if the given number of horizontal x vertical frames matches
 ; the number of layers in the original image. If yes, proceed; if not,
 ; show an error message.
 (if (= numframes (* xframes yframes))
   (begin
     ; New image
     (define img (car (gimp-image-new (* framesizex xframes) (* framesizey yframes) imgtype)))
     ; We don't touch the original image, so we start a new undo group
     ; for the new image instead.
     (gimp-image-undo-group-start img)
     (if (= imgtype INDEXED)
       (begin
         ; Indexed images need the palette to be copied to the new image.
         (define palette (gimp-image-get-colormap curimg))
         (gimp-image-set-colormap img (car palette) (cadr palette))))
     ; Loop betwen 0 and numframes - 1.
     (define frame 0)
     (while (< frame numframes)
       ; For each layer/frame:
       (begin
         ; Show progress as the current frame relative to the number of frames.
         (gimp-progress-update (/ frame numframes))
         ; Read the individual layer ID, starting from the bottom.
         (define oldlayer (vector-ref layerset (- numframes frame 1)))
         ; Create new layer as a copy of this one and add it to the image.
         (define newlayer (car (gimp-layer-new-from-drawable oldlayer img)))
         (gimp-image-add-layer img newlayer -1)
         ; Add alpha if the layer doesn't have it.
         (if (= (car (gimp-drawable-has-alpha newlayer)) FALSE)
           (gimp-layer-add-alpha newlayer))
         ; Calculate the offsets and crop size.
         (define offsets (gimp-drawable-offsets oldlayer))
         (define newsizex (car (gimp-drawable-width oldlayer)))
         (define newsizey (car (gimp-drawable-height oldlayer)))
         (define offsetx (car offsets))
         (define offsety (cadr offsets))
         (if (< offsetx 0)
           (begin
             (set! newsizex (+ newsizex offsetx))
             (set! offsetx 0)))
         (if (< offsety 0)
           (begin
             (set! newsizey (+ newsizey offsety))
             (set! offsety 0)))
         (if (> (+ offsetx newsizex) framesizex)
           (set! newsizex (- framesizex offsetx)))
         (if (> (+ offsety newsizey) framesizey)
           (set! newsizey (- framesizey offsety)))
         ; Calculate frame position X and Y.
         (define framey (truncate (/ frame xframes)))
         (define framex (- frame (* framey xframes)))
         (set! framex (* framex framesizex))
         (set! framey (* framey framesizey))
         ; Resize and place the layer.
         (gimp-layer-resize newlayer
                            newsizex
                            newsizey
                            (- (car offsets) offsetx)
                            (- (cadr offsets) offsety))
         (gimp-layer-set-offsets newlayer (+ framex offsetx) (+ framey offsety))
         ; Force it visible.
         (gimp-drawable-set-visible newlayer TRUE)
         ; All done. Next loop iteration.
         (set! frame (+ frame 1))))
     ; Progress 100%
     (gimp-progress-update 1.0)
     ; Merge visible layers (all are visible now).
     (define mergedlayer (car (gimp-image-merge-visible-layers img CLIP-TO-IMAGE)))
     ; Make the new layer the size of the image.
     (gimp-layer-resize-to-image-size mergedlayer)
     ; Convert the image to RGB if it is not already.
     (if (not (= imgtype RGB))
       (gimp-image-convert-rgb img))
     ; All done - close the undo group and add a display for the new image.
     (gimp-image-undo-group-end img)
     (gimp-display-new img))
   ; If the frame counts didn't match...
   (gimp-message "Error: Number of layers doesn't match horizontal x vertical frames")))

(script-fu-register "script-fu-layers-to-sl-anim"

                   _"<Image>/Script-Fu/SecondLife/Frames to texture..."
                   _"Convert a set of layers (frames) to a texture suitable for llSetTextureAnim."
                   "Pedro Oval"
                   _"Public Domain"
                   "2010-12-04"
                   "RGB*, GRAY*, INDEXED*"
                   SF-IMAGE        "Image"        0
                   SF-DRAWABLE     "Drawable"     0
                   SF-ADJUSTMENT   _"# of horizontal frames" '(2 1 1024 1 10 0 1)
                   SF-ADJUSTMENT   _"# of vertical frames"   '(2 1 1024 1 10 0 1))

</scheme>

New in this version with respect to the stable one:

  • Add progress indicator.
  • Clip each layer to its rectangle.
  • Respect the original offsets of layers.
  • Do the merge down in the correct order.
  • Add alpha to the layers that lack it.
  • Set the final layer's size to the image's size.
  • Extensively commented.

TODO list

  • Add option to generate and show the script that will animate the texture with the given parameters. It has the problem that we don't ask for the final frame count, so the real from/to values can be different to the ones that the script can output, which may confuse some users. It will have a checkbox, "Generate script", initially checked, but uncheckable to avoid the annoyance of a dialog at the end.
  • Perhaps change the strategy to use gimp-image-duplicate to simplify the process.
  • Add the opposite conversion: given an animated texture, split it into layers.
  • Automatically remove alpha from the final image if not needed.