Login [Register]
Don't have an account? Register now to chat, post, use our tools, and much more.
So as many of you know, although I haven't formally announced it yet, I am working on a book about calculators (further details coming soon) and it will of course have tons of screenshots in it. The best way to take screenshots is with my favorite emulators, but unfortunately, the format of the screenshots they take is not acceptable for a book. For one, the default resolution is 72x72 pixel per inch, while I need 300 pixels per inch. Of course, once the resolution is changed, they're miniature, so I need to scale them up, remote the screen-colored yellowish color, and add a border. Today, because I'm dumb, I spent a few hours figuring out how to script that up in GIMP; below you can see the results of my hard work. I do recommend giving it a try if you have any tasks of your own in GIMP that you do a lot and need to automate, especially if you either know or want to know LISP/Lush/Scheme.


Code:
(define (manning-TI-SS img)
   (gimp-image-undo-disable img)

   (let* ((numlayers (car (gimp-image-get-layers img)))
         (layerlist (cadr (gimp-image-get-layers img)))
        (count 0))

      ;delete the non-bottom layers
      (set! count (- numlayers 1))
      (while (> count 0)
         (set! count (- count 1))
         (gimp-image-remove-layer img (aref layerlist count))
      )
      (gimp-convert-rgb img)
      (gimp-threshold (aref (cadr (gimp-image-get-layers img)) 0) 128 255)
      (gimp-image-set-resolution img 300 300)
      (gimp-image-scale-full img 576 384 INTERPOLATION-NONE)
      (gimp-image-resize img 582 390 3 3)
      (let* ((mylayer (car (gimp-layer-new img 582 390 RGBA-IMAGE "border" 100 NORMAL-MODE)))
            (mylayer2 (car (gimp-layer-new img 582 390 RGBA-IMAGE "border" 100 NORMAL-MODE))))
         (gimp-image-add-layer img mylayer -1)
         (gimp-palette-set-default-colors)
         (gimp-image-set-active-layer img mylayer)
         (gimp-selection-all img)
         (gimp-selection-shrink img 2)
         (gimp-selection-invert img)
         (gimp-bucket-fill mylayer FG-BUCKET-FILL NORMAL-MODE 100 0 0 0 0)
         (gimp-image-add-layer img mylayer2 0)
         (gimp-image-lower-layer-to-bottom img mylayer2)
         (gimp-image-set-active-layer img mylayer2)
         (gimp-selection-all img)
         (gimp-bucket-fill mylayer2 BG-BUCKET-FILL NORMAL-MODE 100 0 0 0 0)
         (gimp-selection-clear img)
      )
      
      (gimp-image-undo-enable img)
      (gimp-image-flatten img)
      (gimp-displays-flush)
   )
)

; Finally register our script with script-fu.
(script-fu-register "manning-TI-SS"
                    "Manning TI Screenshot"
                    "Processes a raw .gif screenshot for inclusion in my book."
                    "Kerm Martian <email retconned>"
                    "Christopher Mitchell"
                    "2011-11-15"
                    ""
               SF-IMAGE    "Image"         0)
(script-fu-menu-register "manning-TI-SS" "<Toolbox>/Xtns/Script-Fu")
I despise being the one to bring PhotoShop into this, but I can have photoshop record my actions for future use. No scripting required on my part.

On point though, looks like a lot of work went into it. Do you have any examples to show us? I'm assuming the script-fu-register stuff goes under the script info, and not to the image itself?
That's correct. The language is actually Scheme, a LISP-like language, as you can see with all those parentheses. The scripts you make show up under the Filter menu, and since you can do all sorts of math and other data manipulation, I feel like it might be more powerful than recording and playing back commands. Of course, for this particular job, that would indeed be enough; someone should make something like that for GIMP, if they haven't already.
I can't help but point out...
Code:
(let* ((numlayers (car (gimp-image-get-layers img)))
         (layerlist (cadr (gimp-image-get-layers img)))
        (count 0))
(set! count (- numlayers 1))
      (while (> count 0)
         (set! count (- count 1))
         (gimp-image-remove-layer img (aref layerlist count))
      )
Would be more Scheme-ish (ie, tail end recursion abusing) like:
Code:
(let ((numlayers (car (gimp-image-get-layers img)))
      (layerlist (cadr (gimp-image-get-layers img))))
  (let f ((count (- numlayers 1)))
       (when (> count 0)
         (gimp-image-remove-layer img (aref layerlist count))
           (f (- count 1))))
Well done, though! I didn't even know that GIMP used Scheme in its Script-Fu Smile
Can you explain how that works? I didn't know you could put anything between the let and the ((list of local) (variables to use)) in that scope. Thanks, nor did I until I went experimenting with it!
Hm, interesting. I've never played with Gimp's script-fu facility. For something this simple (scale, remove color, add border) I usually stick with ImageMagick and/or Netpbm. You should be able to do everything you need with less than 10 options (maybe even 5) to mogrify.

Plus, the default filter for enlarging images is "Mitchell". Smile

http://www.imagemagick.org/www/command-line-options.html#filter

Code:
(let f ((n 1)) 
  (unless (= n 10)
     (display n)
          (f (- n 1))))

;;; Is the same as

(let ((f (lambda (n)
             (unless (= n 10)
                  (display n)
         (f (- n 1))))))
  (f 1))

It's just an alternative way of defining lambdas. I believe the term for it is called a "named let". (Also, FWIW, that code above just prints 1 through 9)
That's quite nifty to know, Catherine; thank you for that. I'll have to experiment with a few more scripts to try that out. Smile Perhaps something to do a bit of optimization after enlarging PindurTI's animated GIFs. Christop, that's neat. I was happy to find that I could use the None interpolation for scaling; for a while I couldn't seem to find the command for that, and it is of course key for scaling up calculator screenshots. I'll take a look at Netbpm, and I have indeed played with ImageMagick before. I'm not sure why I didn't consider it for this.
KermMartian wrote:
The best way to take screenshots is with my favorite emulators, but unfortunately, the format of the screenshots they take is not acceptable for a book. For one, the default resolution is 72x72 pixel per inch, while I need 300 pixels per inch.

If you want to get pedantic about it (I don't mind being pedantic Smile), the 96x64 screenshots should be considered to be about 48 pixels per inch (or 96 pixels per inch if the screenshots are double-sized). The TI-83 screen is about 2" wide and has 96 pixels across, so that's 48 dpi. Therefore you would have to scale the image by 625% to keep it actual size at 300 dpi. Of course, you'd have to adjust that scale factor if you want to make the printed images either smaller or larger than actual size.
Aha, but you missed the fact that the screenshots WabbitEmu gives me are already 192x128, hence why I chose 300% instead of around 600%. Smile I was thinking on the same page as you. If I was converting PindurTI screenshots, which come out at 1:1 size, I would indeed need >=600% scaling.
KermMartian wrote:
Aha, but you missed the fact that the screenshots WabbitEmu gives me are already 192x128, hence why I chose 300% instead of around 600%. Smile I was thinking on the same page as you. If I was converting PindurTI screenshots, which come out at 1:1 size, I would indeed need >=600% scaling.

Ahaha, but I did consider the possibility that screenshots are 192x128. See?
christop wrote:
(or 96 pixels per inch if the screenshots are double-sized).

Laughing

How big do you plan to make the images in the book, anyway? Are they going to be more or less actual size?

Are you planning to include any animated screenshots in the book, perhaps with lenticular printing? Very Happy
Went through a tutorial and, after about an hour or an hour and a half, I have a script that mirrors the left to the right. The code:
Code:
(script-fu-register "script-fu-mirror"
          "Mirror"
          "Mirrors the left->right on layer"
          "_player1537"
          "Copyright is lame"
          "2011/11/19"
          ""
          SF-IMAGE "Input Image" 0
          SF-DRAWABLE "Input Drawable" 0)
(define duplicate-layer
  (lambda (image layer)
    (let ((dup-layer (car (gimp-layer-copy layer 1))))
      (gimp-image-add-layer image dup-layer 0)
      dup-layer)))
(define script-fu-mirror
  (lambda (img drawable)
    (gimp-undo-push-group-start img)
    (gimp-context-push)
    (let ((new-layer (duplicate-layer img drawable))
     (width (car (gimp-drawable-width drawable)))
     (height (car (gimp-drawable-height drawable))))
      (gimp-flip new-layer 0)
      (gimp-rect-select img (/ width 2) 0 (/ width 2) height REPLACE 0 0)
      (gimp-layer-add-alpha drawable)
      (gimp-edit-clear drawable)
      (gimp-selection-invert img)
      (gimp-edit-clear new-layer)
      (let f ((n (car (gimp-image-get-layer-position img drawable))))
   (when (> n 0)
         (gimp-image-lower-layer img new-layer)
         (f (- n 1))))
      (gimp-image-merge-down img drawable 0)
      (gimp-selection-none img)
      (gimp-displays-flush)
      (gimp-image-undo-group-end img)
      (gimp-context-pop))))
(script-fu-menu-register "script-fu-mirror" "<Image>/Xtns/Script-Fu")

Also, I am so deeply saddened by the Script-Fu tutorial :/ They say that the only looping mechanism is (while), when there is (do) also. Not to mention, they never once touch on tail end recursion. And, they try to abstract that you can use just (let) and (define). For the latter, they used the (define (name args)) syntax, so people don't know that they are really defining a lambda (define name (lambda args)). And, in the first few parts of the tutorial, they decided to use the pascalCasedFunctionNames style, and changed later on, rather than using the hyphen-separated-naming-convention. Also, they used the

Code:
(let*
  (
    (name 0)
    (blah 0)
  )
)
syntax instead of
Code:
(let* ((name 0) (blah 0)))
Plus, of course, using (let*) instead of (let), but I assume that was so they didn't have to explain the difference to people who don't want to learn Scheme. (let*) lets you use names later on in the declarations, whereas (let) doesn't let you use variables defined earlier in the expression. Meh.
_player1537 wrote:
Also, they used the
Code:
(let*
  (
    (name 0)
    (blah 0)
  )
)
syntax instead of
Code:
(let* ((name 0) (blah 0)))
Err, that's not a syntactical change at all, that's just a difference in whitespacing convention. The actual code is completely identical from the point of view of the interpreter...
s/syntax/styling/ Smile I just dislike having random closed parenthesis lying around because any good editor should match your parentheses for you, and it takes up less of the screen. Not to mention, a good editor would also be able to line you up with your other parentheses, so that if you have
Code:
(let ((a 2)
      (b 2))
  (display (+ a b)))
it will make sure that the (a 2) and (b 2) lines are indented to the same place as well as putting (display) 1 space after where the open parenthesis for (let) is (so that it lines up with the e).
_player1537 wrote:
s/syntax/styling/ Smile I just dislike having random closed parenthesis lying around because any good editor should match your parentheses for you, and it takes up less of the screen. Not to mention, a good editor would also be able to line you up with your other parentheses, so that if you have
Code:
(let ((a 2)
      (b 2))
  (display (+ a b)))
it will make sure that the (a 2) and (b 2) lines are indented to the same place as well as putting (display) 1 space after where the open parenthesis for (let) is (so that it lines up with the e).


My IDE does generate the close tags and parenthesis/(curly) brackets on the same line automatically, but I choose to put them on new lines for visual aide and to quickly find missing closing tags if I happen to delete one.
_player, funnily enough, this argument is quite relevant to me other than my Script-Fu experiments, as for my Machine Learning class this semester I'm writing a lot of Lush, another Lisp-like language. I've been tending to write my function-ending and loop-ending parentheses on their own lines, the way I'd put closing braces in C. I of course keep command-ending parentheses on the line with their command, as that would be a bit too ridiculous to separate those out. The only exception is commands for which the arguments themselves are huge.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are GMT - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement