Help:Zoomable images/de-tile.scm

From Wikimedia Commons, the free media repository
Jump to: navigation, search

This is a little script for the GIMP to automatically assemble tiles following a particular naming scheme into one single image. See Zoomable images. The script comes without warranties; use at your own risk.

The script adds a menu entry "De-tile" to the menu "Filters→Combine" in an image window in the GIMP.

The script assumes that all tiles are present within the same directory, and that they are named "<arbitrary prefix>-<column number>-<row number>.<same suffix for all tiles>".

To use this script, copy it into the "script" folder of the GIMP and then refresh the scripts or restart the GIMP. Load the top-left tile (named "x-0-0.jpg") and choose "Filters→Combine→De-tile". This will load all the other tiles, arrange them, and then combine them into one single image. Be prepared to wait a few minutes if there are many tiles; loading a few hundred files, even if they are small, takes time.

Technical note: this was just a quick hack. It is possible that it could be sped up by loading one tile, combining it with the rest of the already loaded image, and only then loading the next tile. This would create far less layers that existed at the same time.

Caveat: this script requires GIMP 2.4 or higher.

; de-tile.scm
; Combine tiles into one single image. Load the top-left tile and then
; run this script to get the whole image as one.
;
; Created by Wikimedia user "Lupo", September 2008, http://commons.wikimedia.org/wiki/User:Lupo
;
; Parts of this script have been modified from Hannu Hoffren's Combine script
; (combine-tiles.scm).
;
; The creator of this program is not resposible of any damage that might
; occur from using the program.
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License.
;
; De-Tile
; Assumes the top-left image is already loaded. Load all the other tiles
; as layers, constructing the filenames according to the pattern
; "prefix-col-row". Calculate the size of the combined image based on
; the widths and heights of the tiles in row and col 0. Resize the image,
; move all the layers to the right places, and merge them.
;
; Input: none. The script determines the size of the full image as well as the
; number of tiles in both horizontal and vertical direction automatically. The
; assumption is that all tiles are present in the same directory as the image
; already loaded, and that their names follow the convention "zoom_level-col-row".

(define (script-fu-de-tile img drawable)
  (let*
    (
      (filename (car (gimp-image-get-filename img)))
      (name     (car (gimp-image-get-name img)))
      (pathname (substring filename 0 (- (string-length filename) (string-length name))))
      (prefix   (car (strbreakup name "-")))
      (suffix   (car (cdr (strbreakup name "."))))
      (col      1)
      (row      0)
      (x        (car (gimp-image-width img)))
      (y        (car (gimp-image-height img)))
      ; Get all similary named files
      (files    (file-glob (string-append pathname prefix "-*-*." suffix) 0))
      (n-files  (car files))
      (filelist (cadr files))
      (tiles-x  0)
      (tiles-y  0)
    )
    ; We assume we already have the "0-0" file
    (gimp-image-undo-group-start img)
    (gimp-context-push)
    ; Calculate number of rows and colums from the filenames we've gotten
    (let*
      ((i 0) (cidx 0) (ridx 0) (name))
      (while (< i n-files)
        (begin
          (set! name (strbreakup
                       (car (strbreakup
                              (substring
                                (car filelist)
                                (string-length (string-append pathname prefix "-"))
                              )
                              "."
                            )
                        )
                        "-"
                      )
          )
          (set! cidx (string->number (car name) 10))
          (set! ridx (string->number (cadr name) 10))
          (if (> cidx tiles-x) (set! tiles-x cidx))
          (if (> ridx tiles-y) (set! tiles-y ridx))
          (set! i (+ i 1))
          (set! filelist (cdr filelist))
        )
      ) ; end while
    )
    (set! tiles-x (+ tiles-x 1))
    (set! tiles-y (+ tiles-y 1))
    ; Load files
    (while (< row tiles-y)
      (begin
        (while (< col tiles-x)
          (let*
            (
              (new-layer (car (gimp-file-load-layer
                                RUN-NONINTERACTIVE img
                                (string-append
                                  pathname
                                  prefix "-" (number->string col 10) "-" (number->string row 10) "." suffix)
                              )
                         )
              )
            )
            (gimp-image-add-layer img new-layer -1)
            (if (= row 0) (set! x (+ x (car (gimp-drawable-width new-layer)))))
            (if (= col 0) (set! y (+ y (car (gimp-drawable-height new-layer)))))
            (set! col (+ col 1))
          )
        )
        (set! col 0)
        (set! row (+ row 1))
      )
    )
    ; Now x and y define the new size of the full image
    (gimp-image-resize img x y 0 0)
    (set! col 0)
    (set! row 0)
    (set! x   0)
    (set! y   0)
    ; Now loop through layers, moving them to the right places
    (let*
      (
        (layers (gimp-image-get-layers img))
        (num-layers (car layers))
        (layer-array (cadr layers))
        (i (- num-layers 1))
      )
      (while (>= i 0)
        (let*
          (
            (thislayer (aref layer-array i))
            (thislayer-w (car (gimp-drawable-width thislayer)))
            (thislayer-h (car (gimp-drawable-height thislayer)))
          )
          (if (= (car (gimp-layer-is-floating-sel thislayer)) FALSE)
            (begin
              (gimp-layer-translate thislayer x y)
            )
          )
          (set! x (+ x thislayer-w))
          (set! col (+ col 1))
          (if (>= col tiles-x)
            (begin
              (set! x 0)
              (set! y (+ y thislayer-h))
              (set! col 0)
            )
          ) ; end if
          (set! i (- i 1))
        )
      ) ; end while
    )
    ; Combine all the layers into one
    (gimp-image-merge-visible-layers img EXPAND-AS-NECESSARY)
    (gimp-context-pop)
    (gimp-image-undo-group-end img)        
    (gimp-displays-flush)
  )
)      

(script-fu-register
  "script-fu-de-tile"
  "<Image>/Filters/Combine/De-Tile..."
  "Combine tiles into one single image"
  "Lupo"
  "Lupo"
  "September 2008"
  "*"
  SF-IMAGE "Image" 0
  SF-DRAWABLE "Drawable" 0
)