FE 'tkGooie' Utilities

'IMAGEtools' group

Rotate Image
in a 2nd
canvas

(FE = Freedom Environment)

The FE Rotate-image 'tkGooie' interface.

It reads a user-specified GIF or PNG file.

FE Home Page > FE Downloads Page >

FE 'tkGooies' Description Page >

FE 'tkGooies' 'IMAGEtools' Page >

This Rotate-Image-on-2nd-Canvas tkGooie utility Page

INTRODUCTION to
'rotateImage_on2ndCanvas'

In 2013, I happened to see the page titled 'Photo image rotation' at wiki.tcl.tk. That page was started by 'RS' (Richard Suchenwirth) back in 2002. He used a Tk canvas widget to hold a 'demo' image (a Tcl feather) and built a rotated image (at arbitrary angle) on a 2nd canvas, to the right of the 1st canvas.

In 2005, 'DKF' (Donal Fellows) presented another version of the rotate proc that's algorithmically equivalent to RS's but which was optimized for Tcl/Tk 8.4.

In any case, it has been about 8 years now, and no one has taken this 'demo' code and made something more like an 'end-user utility' out of it.

To me, it seemed it needed a file-browser-selector --- in place of a hard-coded demo image file. And I would like to have the option to lay down a background color behind the rotated image. Then I would be more tempted to use this kind of utility occasionally.

I had recently written 'ImageMagnets - a Tk GUI for image processing' --- for spot-bulge and spot-shrink around 'magnet points' on an image. In the process of coding the routine to bulge/shrink disks within an image, I found that mapping colors of pixels from a 'from' image to a 'to' image would not work nicely, because a pixel on the 'from' image would not map precisely on a pixel of the 'to' image. It was a bit of a conundrum, trying to figure out how to spread the color of the 'from' pixel across several 'to' pixels.

I found that it made sense to 'march' across the 'to' image and do an inverse mapping from the 'to' image to the 'from' image to find the pixel (or 2 or 3 or 4) to use (via a weighted average) to get the color of the 'to' pixel.

For example, if the 'to' mapping was the square of a normalized radius (of a pixel from a 'magnet point'), then the 'inverse' mapping was a square-root of the radius to the 'to' pixel.

In any case, I realized that I had the tools to make a 'preliminary stab' at an 'end-user utility' for rotating images --- images read from files supported by the Tk 'image create photo' command --- in particular, GIF and PNG files.

(Since I am using Tk 8.5, not 8.6, my testing was done with GIF files. But the code below will probably work with PNG files as well --- with the 8.6 'wish' interpreter.)

I started with code from some of my other Tk canvas scripts that had a file-browse-select feature and a background-color select feature, and ended up with the GUI seen in the following image.

This utility will also read in transparent GIF's, as seen in the following image.

If I wanted to make a transparent image from the image on the right, I would do a screen capture (with 'gnome-screenshot' on Linux) and take the captured PNG file into a text editor ('mtpaint' on Linux) to crop the image and then, when saving the file, choose to save it as either a PNG or a GIF, and choose the color to make transparent (which 'mtpaint' allows you to do).


One application that I had in mind for this rotate utility is to rotate text labels 90 degrees for use in y-axis labeling of a plot (even though I can rotate images with 'mtpaint' --- but where's the Tcl fun in that).

I made the following black-text-on-white-background with A GUI for making 'Title Blocks' ... with text, fonts, colors, images. I used 'gnome-screenshot' to capture the image in a PNG file, and I used 'mtpaint' to crop the image and save it in a GIF file.

Then I read the GIF file into this rotate-image GUI, set the background to white, and clicked on the 'Rotate' button.

In fact, I had in mind that one could take a whole set of alphanumeric characters (like the following taken from YAFSG - Yet Another Font Selector GUI) and rotate them. Then capture the image and break it up into separate little image files for each rotated character.

I could do the rotation with 'mtpaint', as well as the cropping to individual characters --- but, again, I wanted to get Tk into the act. Besides I have a lot more control with a Tk script. I could add various kinds of image processing ---including 'gamma correction' and 'transparency effects' and 'blur effects' and so on --- with many possibilities for controlling and guiding the outcome.


DESCRIPTION OF THE CODE

Below, I provide the Tk script code for this 'rotate TkPhoto image' utility.

I follow my usual 'canonical' structure for Tk code for this Tk script:



  0) Set general window & widget parms (win-name, win-position,
     win-color-scheme, fonts, widget-geometry-parms, win-size-control,
     text-array-for-labels-etc).

  1a) Define ALL frames (and sub-frames, if any).
  1b) Pack   ALL frames and sub-frames.

  2) Define & pack all widgets in the frames, frame by frame.
              Within each frame, define ALL the widgets.
              Then pack the widgets.

  3) Define keyboard and mouse/touchpad/touch-sensitive-screen action
     BINDINGS, if needed.

  4) Define PROCS, if needed.

  5) Additional GUI initialization (typically with one or more of
     the procs), if needed.


This Tk coding structure is discussed in more detail on the page A Canonical Structure for Tk Code --- and variations.

This structure makes it easy for me to find code sections --- while generating and testing a Tk script, and when looking for code snippets to include in other scripts (code re-use).

I call your attention to step-zero. One new thing that I have started doing recently is using a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future.


Experimenting with the GUI

As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on all of the 'pack' commands for the frames and widgets.

That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I re-size the main window.

I think that I have used a nice choice of the 'pack' parameters. The labels and buttons and the scale stay fixed in size and relative-location as the window is re-sized --- while the two 'canvases' expand/contract to accommodate the image size.

Furthermore, I used the '-scrollregion' parameter for the canvases, so that the images can exceed the current canvas sizes. The images can be scrolled up-and-down and side-to-side --- if the images exceed the canvas sizes.

You can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various frames and widgets --- to get the widget behavior that you want.

---

In addition, you might want to change the fonts used for the various GUI widgets. For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families.

In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing.

I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well.


Some features in the code

That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing.

You can look at the top of the PROCS section of the code to see a list of the procs used in this script, along with brief descriptions of how they are called and what they do.

The main proc is the 'rotate_image' proc. I have provided many comments (at the top of the script and in that proc) that describe the mathematics (and considerations) that are being used to perform the rotation (pixel mapping) function.


Comments in the Code

It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this.

Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch videos of people 'twerking' --- and catching on fire in the process --- by falling onto lighted candles.


The CODE

Here is a link to CODE for the script 'rotate_image_on2ndCanvas.tk'.


A LITTLE MORE ON MARCHING ACROSS THE 'TO' IMAGE

I refer to the image on the left as img1 and the one on the right as img2. Rather than 'marching across img1' and coloring pixels of img2, we 'MARCH ACROSS IMG2' and use the INVERSE of the above rotation transformation (i.e. angle -A instead of A) to get a location on img1 corresponding to the current pixel on img2. Then we use the color of one or more pixels around the IMG1-PIXEL-LOCATION to get the color for the img2 pixel.

I ended up using 3 pixels rather than 4 --- as follows. Say (i,j) is the integer location of the pixel on img2 whose color we want. We do a rotation of (i,j) and it gives us not-necessarily integer coordinates, (x,y) say.

Let m = int(x) and n = int(y). Then I considered the 3 points (m,n) and (m+1,n) and (m,n+1).

Let dx = x - m. Let dy = y - n. Then dx and dy are fractional numbers between 0.0 and 1.0.

Then I got the weighted average of the 2 RGB colors at (m,n) and (m+1,n).

RGBcolor1 =
( RGBcolor(m,n) * (1 - dy) ) + ( RGBcolor(m+1,n) * dy )

Also the weighted average of the 2 RGB colors at (m,n) and (m,n+1).

RGBcolor2 =
( RGBcolor(m,n) * (1 - dx) ) + ( RGBcolor(m,n+1) * dx )

Then I set the color of the pixel at (i,j) of img2 to

(RGBcolor1 + RGBcolor2) / 2

This technique essentially 'anti-aliases' the img2 pixel, whenever the img2-pixel does not map back precisely to a single pixel on img1.

This technique seems to work well, so I do not think I need to throw the img1 color at pixel (m+1,n+1) into the mix.


SOME POTENTIAL ENHANCEMENTS:

Some other features could be added to this Tk script:

** Handle multiples of 90 separately
Like you can see in the 'DKF' code at page 'Photo image rotation' of wiki.tcl.tk, cases like +90, -90, +180, -180, +270 could be handled relatively easily and much more efficiently (computationally speaking).

It is on my things-to-do list to do that. In fact, by handling these cases separately one can avoid some cases of 'jaggies' being introduced by round-off errors or whatever.

In any case, I was pleasantly surprised that the rotations, of the images shown above, were generally done within a couple of seconds in most cases --- often in less than a second. So the draw-times do not make this code a pain to use.


** Build rotated pixels of img2 by lists of hexcolors
For simplicity in the beginning, I just colored the img2 pixels pixel-by-pixel --- although I color the background using 'row-lists' of the solid color.

I intend to go back someday and make the building of img2 proceed row-by-row --- or even by one large list of lists-of-row-colors.

But I was pleasantly surprised that, as noted above, the rotates proceeded quite rapidly.


** More image processing features
As noted near the top of this page, there are options that one could supply in the area of 'transparency' and 'gamma correction' and 'blur' (or 'anti-aliasing options'). But I will wait on those kinds of enhancements to see if I actually use this utility much.


** GIF or PNG Write
One could provide an option to output a GIF or PNG file, via a 'WriteImg' button. But I am quite happy with the quality of screen captures that I get by using the 'gnome-screenshot' utility on Linux.


My main satisfaction with this code is that it is available now in case I find situations where I do not have a utility available (at reasonable cost) to perform some image processing function involving rotation. With this code, I have a basis from which to build what I need.

In fact, I could someday add this rotation capability (code) to A GUI for making 'Title Blocks' ... with text, fonts, colors, images and avoid the screencapture and image editor steps that I went through above. But that would add a lot more complication to that 'Title Blocks' code.

There is a lot to be said in favor of the lots-of-little-utilities approach.


IN CONCLUSION

There's a lot to like about a utility that is 'free freedom' --- that is, no-cost and open-source so that you can modify/enhance/fix it without having to wait for someone else to do it for you (which may be never).

I hope to provide more free image-processing scripts that can be used to perform handy operations on images --- or build new images. As I have said on at least one other code-donation page on this wiki ...

A BIG THANK YOU to Ousterhout for starting Tcl-Tk, and a BIG THANK YOU to the Tcl-Tk developers and maintainers who have kept the simply MAH-velous 'wish' interpreter going.

Bottom of this page for
Rotate Image - on a 2nd canvas
--- a utility in the FE 'tkGooies' system,
in the 'IMAGEtools' group.

To return to a previously visited web page location, click on the Back button of your web browser a sufficient number of times. OR, use the History-list option of your web browser.
OR ...

< Go to Top of Page, above. >

Page history:

The code was created in 2013 --- and posted 2013 Sep 08 at http://wiki.tcl.tk/38709.

This FE web page was created 2014 May 07 --- as a backup and alternative to the wiki.tcl.tk page.

This page was changed 2015 Oct 05.
(Small changes.)

Page was changed 2019 Feb 24.
(Added css and javascript to try to handle text-size for smartphones, esp. in portrait orientation.)

Page was changed 2019 Jun 13.
(Specified image widths in percents to size the images according to width of the browser window.)


NOTE:
The code here MAY become more 'up-to-date' than the code originally posted on the Tcler's Wiki ---
wiki.tcl-lang.org --- formerly wiki.tcl.tk.