REBOL [Title: "Thumbnail Maker"] ; Create a little GUI to allow the user to adjust image settings: view center-face layout [ text "Resize input images to this height:" height: field "200" text "Create output mosaic of this width:" width: field "600" text "Space between thumbnails:" padding-size: field "30" text "Color between thumbnails:" btn "Select color" [background-color: request-color/color white] text "Thumbnails will be displayed in this order:" the-images: area across btn "Select images" [ ; Select some files: some-images: request-file/title trim/lines {Hold down the [CTRL] key to select multiple images:} "" ; Error check: if some-images = none [return] ; Show the selected files in the area widget above: foreach single-image some-images [ append the-images/text single-image append the-images/text "^/" ] show the-images ] ; This button creates the output thumbnail mosaic: btn "Create Thumbnail Mosaic" center [ ; Set sizing variables to the values entered in the GUI: y-size: to-integer height/text mosaic-size: to-integer width/text padding: to-integer padding-size/text ; Set the background color (white if none selected): if error? try [background-color: to-tuple background-color][ background-color: white ] ; The list of images that will be resized is stored in a block ; labeled "images". The "parse" function is covered later in ; this tutorial. The following code separates each line item in ; the text area above, and returns a block of all the items: images: copy parse/all the-images/text "^/" ; Error check: if empty? images [alert "No images selected." break] ; The output image will be created from a "view layout" GUI block. ; That block will be labeled "mosaic" and will contain all the ; resized image data and layout formatting needed to create the ; thumbnail image sheet. We'll start building that block by ; including the background color, spacing, and "across" words ; needed to layout the GUI. Because the block contains some ; variables, we'll use the "compose" function to evaluate them ; (treat them as if they'd been typed in explicitly): mosaic: compose [ backcolor (background-color) space (padding) across ] ; Next, we'll use a foreach loop to go through the list of images, ; read and resize each image, and add the resized image data to ; the mosaic block. The variable "picture" will be used to refer ; to each image as the loop progresses through each item in the ; list: foreach picture images [ ; Give the user some feedback with a litte message: flash rejoin ["Resizing " picture "..."] ; Read the image data, and assign it the variable label ; "original": original: load to-file picture ; After the data is done loading, erase the message above: unview ; We can refer to the size of the original image using the ; format "orginal/size". That returns width and height ; values in the form of an XxY pair. To refer to the height ; (Y) value only, we can use the format "original/size/2" ; (the second element in the pair). If the height of the ; original image is larger than the "y-size" variable set at ; the beginning of the program, we'll resize the image so ; it fits that height, and append the resized image data to ; the "mosaic" block. Otherwise, we'll simply append the ; orginal image to the block. We're also going to include ; the "image" word, because the "mosaic" block needs to ; include all the functions and data needed to create a view ; layout GUI window: ; If the original image is taller than the prescribed height: either original/size/2 > y-size [ ; Figure a percentage amount the width needs to be ; resized: new-x-factor: y-size / original/size/2 ; Calculate the width of the new image size, and assign ; that value to the variable "new-image-size": new-x-size: round original/size/1 * new-x-factor ; Create the resized image by using the "layout" function ; (as in "view layout"). Specify a new size for the ; image by rejoining the "new-x-size" variable above with ; the "y-size" value specified earlier, and convert that ; value to a pair. Create a new image from that layout ; using the "to-image" function, and assign it to the ; variable "new-image": new-image: to-image layout/tight [ image original to-pair rejoin [new-x-size "x" y-size] ] ; Next, append the resized image data to the "mosaic" ; block. We'll compose the block because we want the ; new-image data to be included as if it was typed in ; explicitly. The word "image" also needs to be included ; because that's needed to show an image in a view layout ; block: append mosaic compose [image (new-image)] ][ ; Here's the second part of the "either" condition above. ; If the height of the original is less than the "y-size" ; variable, simply append the original image to the ; "mosaic" block: append mosaic compose [image (original)] ] ; As the current foreach loop stands, each resized image is ; simply added to the "mosaic" layout from left to right. We ; need to check the size of the "mosaic" layout every time we ; add an image. If the layout is wider than the width we set ; at the beginning of the program (the "mosaic-size" ; variable), we need to insert a "return" word into the ; "mosaic" GUI layout block: ; Create a temporary layout of the "mosaic" block: current-layout: layout/tight mosaic ; If the width of the current layout is larger than the ; prescribed width, insert the "return" word BEFORE the ; current resized image. A tick mark is put onto the 'return ; word so that the actual unevaluated text "return" is ; appended to the mosaic block. "back back tail" puts the ; "return" word in the correct place in the layout block: if current-layout/size/1 > mosaic-size [ insert back back tail mosaic 'return ] ] ; Prompt the user for a file name to save the final "mosaic" ; layout image: filename: to-file request-file/file/save "mosaic.png" ; Create an image from the final "mosaic" layout block, and save ; that image to the file name above: save/png filename (to-image layout mosaic) ; Show the user the saved image: view/new layout [image load filename] ] ]