Tuesday, January 23, 2007

Screen shots with Gimp

I kept going with the gimp scripting. Yesterday I had planned on adding another script that would be quick and easy. Ha ha. No such luck.

When I take a screen shot and want to highlight certain areas, I add a layer on top, set the opacity to like 30-50, then use an eraser to uncover the part. Then I set the layer to "difference" and put the opacity at like 20-25.

Here's an example...

Does a pretty good job. Not a particularly pretty thing, but works. What looks a little better? Add some texture to the outline. I add a drop shadow, 2x, 2y, 3px radius. However, I only want it on the exposed area inside the outline. The drop shadow, however, darkens the whole thing...

To deal with this, take the original cutout layer, and paste it as a mask on the drop shadow layer. You need to invert the original layer first. Its complicated, however the output looks like the following...

The accent is minor, but I really like it. However, the steps involved to produce it are more than I'd like to do every time I want to do a screen shot. But hey, I just learned gimp scripting, so this should be easy? Right?

It is and it isn't. The problem I had was getting the layer into the mask of another layer. In theory I needed a channel object, then could plug that in as the mask. However, for the life of me, I couldn't figure out how to get a reference to a channel of the layer. I still don't know how. I spent (ie. wasted) much of yesterday working on it. Finally, instead of doing that, I just copy the layer, and paste it into the mask. I had tried this and couldn't get that to work either. A couple hours later I realized I need to invert the colors first, then it worked. Here's the interesting part of the code...

(gimp-invert inDrawable)
(gimp-layer-set-opacity inDrawable 100)

(set! shadowMask (car(gimp-layer-create-mask shadowLayer ADD-ALPHA-MASK)))
(gimp-layer-add-mask shadowLayer shadowMask)

(gimp-selection-all inImage)
(gimp-edit-copy inDrawable)
(set! floatingSel (car (gimp-edit-paste shadowMask FALSE)))
(gimp-floating-sel-anchor floatingSel)

(gimp-layer-remove-mask shadowLayer MASK-APPLY)

(gimp-invert inDrawable)
(gimp-layer-set-opacity inDrawable 20)
(gimp-layer-set-mode inDrawable DIFFERENCE-MODE)

I had to do a few other tricks. I'll post the full code at the end. One included a general function to get the layer below the selected layer. That kind of thing.

Anyway, using the plug in works as follows. Get a screen shot into gimp. Add a layer, all white. Set the opacity to whatever you want so you can see through. Use the eraser to cut out parts. (By the way, how to draw straight lines in gimp). Once that's done, just run the script. Make sure the cut out layer is selected. That's it. Make sure its white and selected. The script handles setting the opacity, putting it in "difference" mode, etc.

Put it together with my script from yesterday and you've got one fancy screen shot for web pages...


Full code...


(define (internal-getLayerBelow
inImage
inLayer
)

(set! layerArray (car(cdr(gimp-image-get-layers inImage))))
(internal-getLayerBelowRecurse
inLayer
layerArray
0
)
)

(define (internal-getLayerBelowRecurse
inLayer
layerArray
index
)

(set! currentLayer (aref layerArray index))

(if (= currentLayer inLayer)

(aref layerArray (+ index 1))

(internal-getLayerBelowRecurse
inLayer
layerArray
(+ index 1)
)
)
)


(define (script-fu-kev-style-cutout
inImage
inDrawable
)

(script-fu-drop-shadow
inImage
inDrawable
2
2
3
'(0 0 0)
80
0)

(set! shadowLayer (internal-getLayerBelow
inImage
inDrawable
)
)

(gimp-invert inDrawable)
(gimp-layer-set-opacity inDrawable 100)

(set! shadowMask (car(gimp-layer-create-mask shadowLayer ADD-ALPHA-MASK)))
(gimp-layer-add-mask shadowLayer shadowMask)

(gimp-selection-all inImage)
(gimp-edit-copy inDrawable)
(set! floatingSel (car (gimp-edit-paste shadowMask FALSE)))
(gimp-floating-sel-anchor floatingSel)

(gimp-layer-remove-mask shadowLayer MASK-APPLY)

(gimp-invert inDrawable)
(gimp-layer-set-opacity inDrawable 20)
(gimp-layer-set-mode inDrawable DIFFERENCE-MODE)

;(gimp-selection-all inImage)
;(gimp-edit-copy inDrawable)

;(set! floatingSel (car (gimp-edit-paste shadowMask FALSE)))
;(gimp-floating-sel-anchor floatingSel)


;(gimp-image-remove-layer inImage (internal-getLayerBelow
; inImage
; inDrawable
; )
;)
)

(script-fu-register
"script-fu-kev-style-cutout" ;func name
"Kev Styleize Cutout" ;menu label
"Make the cutout areas pretty" ;description
"Kevin Galligan" ;author
"copyright 2007, Kevin Galligan" ;copyright notice
"January 21, 2007" ;date created
"*"
SF-IMAGE "Input image" 0
SF-DRAWABLE "Input drawable" 0
)
(script-fu-menu-register "script-fu-kev-style-cutout" "/Script-Fu/Kev")

Sunday, January 21, 2007

Scripting Gimp

Last night I wrote a script-fu scheme script to automate some relatively simple stuff in Gimp. I'd put off trying to do this for a while, and even put up a request on guru.com to pay someone to do it.

You'd think I'd be able to do this fairly easily. In college we had to learn scheme for a class. I know gimp fairly well, and I've actually seen the handful of functions I wanted to call. It should just be a matter of getting a few things straight and that's it.

The problem is as follows. I hate scheme. Its confusing. I got the impression there was a lot of emporer's new clothes going on with it. There tended to be a higher quality and level of code with scheme projects, but I think that had a lot more to do with the people coding scheme rather than any advantage of the language itself. In fact, I find coding in scheme like swimming with a coat on. But maybe that's just me.

However, the only guru.com bid was for $200. The guy basically wanted to learn gimp's api on my dime. Personally, I was avoiding the exercise because of the time investment, but whatever. I'm a programming nerd. Once I got over the initial stuff, it wasn't so bad. On top of that, now I think I could do a lot quickly.

However, finding the right documentation quickly was difficult. Gimp has been around for a long time and several different scripting methods exist, so the documentation is fragmented and confusing. It turns out, the best place to start is on gimp's site (shocker).

http://docs.gimp.org/en/gimp-using-script-fu-tutorial.html

This will teach you a lot about scheme and writing scripts. Very, very, very useful.

I think the first question you have for yourself is which language should you use. Python, Perl, etc. seem like better options. However, there was some complication involved in the installation of those bindings. To be honest, I haven't figured out exactly what would be needed to use them. Because I was only calling a few simple functions, I decided to use scheme. I can deal with any language for a few functions.

For the person like me, who's been exposed to scheme before, I'll give the summary of the important things about the api that weren't clear before I started.
  • A "plugin" is defined by a main function. You can name the function whatever you want, but there's a naming scheme that gimp uses. You can read about it in the link above.
  • There are 2 types of functions. Those that operate globally (not a specific image). You can get the list of all images, or (more likely) create a new one. The other type is the image specific.
  • To write an image specific plugin, simply register an Image object as the first parameter and a Drawable object as the second. Gimp will provide those arguments.
  • All gimp functions return a list. ALL GIMP FUNCTIONS RETURN A LIST. Say it again. You need to wrap all returns in, at a minimum, '(car (gimp-some-funtion ...))'. This took me a little while.
  • One of the really cool things I didn't get was that those little dialogs that pop up for plugins aren't specifically coded. That's the part I thought would take a really long time. All you have to do is declare your variables. They show up in the dialog. I'm sure in certain situations this will be restrictive, but for the mast majority of situations, its much easier.
There's other stuff, but its redundant. For the most part, you can find it in the link above. I promise if you following along, you'll get it quick.

I'm including my code because I had a hard time finding simple examples on the web. All mine does is apply a border to an image, then apply a drop shadow. The idea is to dress up screen shots for documentation. Here's an example:

Its a screenshot of a little part of the gimp website. Functional, but hard to see what's really going on, where the shot starts and stops, etc. Well, it WOULD be if the screen background was white ;)

Here's the modified version:

Its a little easier to get your eyes around that one. The process? I applied a 1px gray border. Then I did a drop shadow. Pretty simple. However, the specific series of steps, while not terribly labor intensive, get annoying if you're doing a lot of them.

Here's the code...


(define (script-fu-kev-border-shadow
inImage
inDrawable
inBorderWidth
inBorderColor
inOffsetX
inOffsetY
inBlurRadius
inShadowColor
inShadowOpacity)

(let* ( (newLayer) )

(if (> (car (gimp-image-get-layers inImage)) 1)
(gimp-image-merge-visible-layers inImage 1)
)

(script-fu-addborder inImage (car (gimp-image-get-active-layer inImage)) inBorderWidth inBorderWidth inBorderColor 25)
(gimp-image-merge-down inImage (car (gimp-image-get-active-layer inImage)) 0)
(script-fu-drop-shadow
inImage
(car (gimp-image-get-active-layer inImage))
inOffsetX
inOffsetY
inBlurRadius
inShadowColor
inShadowOpacity
1)

(set! newLayer
(car
(gimp-layer-new
inImage
(car (gimp-image-width inImage))
(car (gimp-image-height inImage))
RGB-IMAGE
"layer 1"
100
NORMAL
)
)
)

(gimp-image-add-layer inImage newLayer 2)

(gimp-drawable-fill newLayer 2)

(gimp-image-merge-visible-layers inImage 1)
)
)

(script-fu-register
"script-fu-kev-border-shadow" ;func name
"Kev Border Shadow" ;menu label
"Convenience call to border and shadow" ;description
"Kevin Galligan" ;author
"copyright 2007, Kevin Galligan" ;copyright notice
"January 21, 2007" ;date created
"*"
SF-IMAGE "Input image" 0
SF-DRAWABLE "Input drawable" 0
SF-VALUE "Border Width:" "1" ;a string variable
SF-COLOR "Border Color:" '(102 102 102) ;color variable
SF-VALUE "Shadow Offset X:" "3" ;a string variable
SF-VALUE "Shadow Offset Y:" "3" ;a string variable
SF-VALUE "Shadow Blur Radius:" "5" ;a string variable
SF-COLOR "Shadow Color:" '(0 0 0) ;color variable
SF-VALUE "Shadow Opacity:" "80" ;a string variable
)
(script-fu-menu-register "script-fu-kev-border-shadow" "/Script-Fu/Kev")


More later.

Friday, January 12, 2007

IPhone 2012

I was thinking about the future of the IPhone. We'll assume they sort out the lawsuits.

The device I see coming out around 2012 is just about the coolest thing you've ever had. I'm guessing it'll look similar in size to the current version, although I have this strange feeling that the IPhone will be coming in two basic flavors.

IPhone Big: This is what they debuted recently. It'll be an ipod replacement, smart phone and media player. Will really wind up being a laptop replacement. Everybody seems to agree on that at this point.

IPhone Small: This will be primarily a phone. Smaller form and screen. Under the hood, some of the really sweet laptop replacement stuff may be there, but the physical size constraint will push out some of the interface fanciness.

Because of the split in form factor, I'm thinking there's a small possibility that Apple will attempt to put the little keyboard on the Big one. While not as elegant as the '07 design, the keyboard is pretty sweet. Here's what they may do. On one side have the main, full form display. On the back, have a partial size display, with a very slim factor (height wise) keyboard. The weight sensor should be able to guess which side you're looking at, and if not, when you hit a key it'll be pretty obvious. This will also allow for some interesting display options while you're talking, as you'll have the whole other side of the phone to display stuff on while talking (to annoy other train passengers or whatever. Maybe you can start a mobile ad service...)

Anyway, we're getting ahead of ourselves here. Smartphones will start to replace full sized machines as time goes on. This has been discussed a lot recently, and it seems like an obvious move. Coupled with a flash based drive of reasonable size, 50-100gb, the phone will serve as a computer itself. Here's what I'd like to see:

Wireless monitor connections. Hold the phone next to your (or any) monitor, press some confirmation on your phone, and you start broadcasting an image to the screen. This is nice for displaying on a real screen. I could see this working on a plane, in little airport booths, meetings, etc. Also, expand it out a bit, and you can imagine a little "work station" (not a new word/phrase, but its used in many different contexts with different meanings, so I put it in quotes). A small reciever/transmitter. Put the phone next to it, click the confirm, and you have access to keyboard, mouse, network connections (if you need something other than wireless), and monitor(s). You can do some work, then just grab and go.

These little work stations should also have another really awesome thing. The little node you communicate through should have a universal charger. You really need to connect nothing. When you get home, put the phone on that little stand, and you have keyboard, mouse, monitor, and its charging. Awesome.

Phone will have built in firewall services. I think its also imperative that the long term storage on a such a device should should encryption built in. If stolen, the data should not be readable without a key of some type. This is an absolute, and certainly not out of the realm of current possibility.

On the OS end, we absolutely need more transparent management. I like using windows, but lets be honest, even nerdly experts such as myself wind up with tons of gunk running after a while. I'm not even talking about all the spyware out there. Stuff like the "Install Anywhere" update process that completely hides itself, but occasionally pops up to tell me it wants to update itself. Apple annoys me to no end with the Quicktime install. I DON'T CARE IF IT TAKES LONGER TO SHOW A VIDEO!!! I don't want something resident in memory. I'm sure it won't happen, but on my wish list is better management for this kind of gunk. Come on, we're all grown up now. Time for something more professional.

Hmm, what else? Oh yeah. Smart card payment technology. Obviously.

Really weird. My roommate barged into my room to give me an article about the iPhone, and he's not a computer guy. Pretty funny. I was actually writing this when he did it. We talked, and I thought of something else. If the phone is going to replace my laptop, I need to be able to buy it and then choose the wireless service. No two year lock-ins, etc. Also, as part of service, I'm thinking some type of online backup.

This could merge with a google business idea I had way back when. Offer wireless service. In exchange, you get location based advertising. Like if I walk past a starbucks, a little deal might pop up on my screen. Walk past Best Buy. The phone knows I like metal and I have an Xbox, so it pops up some type of deal. It would have to be relatively un-annoying. Like google ads now. However, this type of coupling, advertising on the phone in exchange for service, might work. I'd be ok with it as long as it didn't get too detailed into my personal info.

Also, along with this, I was thinking Google might just come out with its own gadget. Something like the iPhone, that offered phone and smartphone/computer service, in exchange for location based always-on advertising. The economics would probably be way off to do it ALL for free, but if google said I could have a mini laptop and service for $300 and some moderately annoying ads, assuming the device and service were quality, sign me up. Sorry Steve.

By the way, I'm looking for a job right now.

Other stuff, like a mini projector would be nice, but I'm thinking the wireless monitor thing might make that less useful.

That's it for today.