photo

https://www.tcl-lang.org/man/tcl8.6/TkCmd/photo.htm

A photo image is the means that Tk uses to display full color images. Tk comes with support for GIF, PPM, and PGM formats, as well as a format to hook in code for other formats. The Img extension provides additional full color image formats.

Photo image rotation - Image scaling


DKF: Although Tk can't load images with rich alpha channels (i.e. non-trivial transparency effects like you get with some PNG images) it can display them from 8.4 onwards.

LV: Is the distinction between load and display that you make here a difference between minimally displaying the content versus displaying the content with the transparency effects?

DKF: No. Load is the process of getting the image data created in memory from a file, and display is the putting of the image on the screen (including all transparency effects, if they're present).

LV: Okay (it might be good to add that distinction to the man page). So, how does Tk 8.4 and on display PNG files if it can't load them? Or do you mean that Tk 8.4 can now load PNG images - just not the advanced ones?

wdb: Is there any way known to "synthesize" memory-internal images with true alpha channel?

DKF: Not yet (at the Tcl level); didn't have time to work on it. Assistance welcome.


LV I see a recent update to the Changes in Tcl/Tk 8.6 page says that Tk 8.6 is going to support LZW compressed files. Does this mean that the patents for LZW have finally expired?

DKF: According to Wikipedia[L1 ], it expired in 2003. We're now 5–6 years later, we're supposed to be able to make use of the algorithm now, that's the whole point of the way patents work. LV Of course - I just wanted to be certain that things had expired. I had a memory of articles back in 2003 that indicated there was still some sort of controversy over the patents and whether they really expired at that time.

GraemeP: IBM also had a patent on the algorithm that expired in August 2008, so its less than 3 years later (see GNU page about GIF images [L2 ] and Cygwin mailing list [L3 ])

LV The article you mention here, from gnu.org, seems to indicate that the IBM claims expired in 2006, not 2008. If that is the case, then things should be fine.


AMG: Is it possible to generate a photo image with transparency in Tcl script? It's certainly possible to generate a photo via the [$photo put] command, but I don't know how to specify that a particular pixel be transparent.

jima 2012-02-10

I have this poor proc to turn all pixels of a given color (given by the color of a particular hand picked pixel) into transparent:

 proc SetTransparent {
  img xP yP
 } {
  set transparent [.photo get $xP $yP]

  set h [image height $img]
  set w [image width $img]
 
  for {set x 0} {$x < $w} {incr x} {
   for {set y 0} {$y < $h} {incr y} {
    if {[$img get $x $y] == $transparent} {
     $img transparency set $x $y 1
    }
   }
  }
 }

It surely can be improved to suit your needs.

AMG: Thanks. It's a shame that transparency has to be set one pixel at a time. Also it would be nice if script could set an arbitrary alpha, not just 100% transparent and 100% opaque.

Peterdh: Here's an example showing the use of the photo 'copy' command. It assumes you have a .tiff image named IMG_0426 in the current directory.

package require Img

set p [image create photo -file IMG_0426.tiff ]

# Use these statements if you want to view the original image
# label .l1 -image $p
# pack .l

set p2 [image create photo]

$p2 copy $p -zoom 2

# Use these statements to view the new image
# label .l2 -image $p2
# pack .l2

# This writes 'test.tiff' which is a zoom x2 copy
# of IMG_0426.tiff.
$p2 write test.tiff

AMG: Let me highlight just how flexible the Tk_PhotoImageBlock structure is. In addition to the most common image format (red, green, blue, alpha values are interleaved, then are stored in memory left-to-right, then rows go from top to bottom), this structure allows for all sorts of other arrangements by changing the pixelSize, pitch, and offset fields. Negative pixelSize gives right-to-left. Negative pitch gives bottom-to-top. Swapping pixelSize and pitch transposes. offset other than {0,1,2,3} gives images divided into separate red, green, blue, and alpha planes.

Challenge: store the image starting with the blue values from the first column, next the green values, next the red values, next the alpha, beginning at the bottom left corner and proceeding right. I believe this would be achieved as follows:

block.width = ...;
block.height = ...;
block.pixelPtr = base + block.height - 1; /* top left pixel */
block.pitch = -1;                         /* downward stride */
block.pixelSize = 4 * block.height;       /* rightward stride */
block.offset[0] = 2 * block.height;       /* red */
block.offset[1] = 1 * block.height;       /* green */
block.offset[2] = 0 * block.height;       /* blue */
block.offset[3] = 3 * block.height;       /* alpha */

If the image appears as follows:

(r0,g0,b0,a0) (r1,g1,b1,a1) (r2,g2,b2,a2)
(r3,g3,b3,a3) (r4,g4,b4,a4) (r5,g5,b5,a5)
(r6,g6,b6,a6) (r7,g7,b7,a7) (r8,g8,b8,a8)

Then it would be stored as follows:

 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
b6 b3 b0 g6 g3 g0 r6 r3 r0 a6 a3 a0 b7 b4 b1 g7 g4 g1 r7 r4 r1 a7 a4 a1 b8 b5 b2 g8 g5 g2 r8 r5 r2 a8 a5 a2

With a Tk_PhotoImageBlock structure like so:

block.width = 3;
block.height = 3;
block.pixelPtr = base + 2; /* top left pixel */
block.pitch = -1;          /* downward stride */
block.pixelSize = 12;      /* rightward stride */
block.offset[0] = 6;       /* red */
block.offset[1] = 3;       /* green */
block.offset[2] = 0;       /* blue */
block.offset[3] = 9;       /* alpha */

The address for channel c (0 = red, 1 = green, 2 = blue, 3 = alpha) of the pixel at (x, y) is:

block.pixelPtr + block.pitch * y + block.pixelSize * x + block.offset[c]

Therefore, the pixel channel byte addresses (relative to base) are:

(2-0+12*0+6,2-0+12*0+3,2-0+12*0+0,2-0+12*0+9) (2-0+12*1+6,2-0+12*1+3,2-0+12*1+0,2-0+12*1+9) (2-0+12*2+6,2-0+12*2+3,2-0+12*2+0,2-0+12*2+9)
(2-1+12*0+6,2-1+12*0+3,2-1+12*0+0,2-0+12*0+9) (2-1+12*1+6,2-1+12*1+3,2-1+12*1+0,2-1+12*1+9) (2-1+12*2+6,2-1+12*2+3,2-1+12*2+0,2-1+12*2+9)
(2-2+12*0+6,2-2+12*0+3,2-2+12*0+0,2-2+12*0+9) (2-2+12*1+6,2-2+12*1+3,2-2+12*1+0,2-2+12*1+9) (2-2+12*2+6,2-2+12*2+3,2-2+12*2+0,2-2+12*2+9)

Or:

( 8, 5, 2,11) (20,17,14,23) (32,29,26,35)
( 7, 4, 1,10) (19,16,13,22) (31,28,25,34)
( 6, 3, 0, 9) (18,15,12,21) (30,27,24,33)