gh-118225: Support more options for copying images in Tkinter (GH-118228)

* Add the PhotoImage method copy_replace() to copy a region
  from one image to other image, possibly with pixel zooming and/or
  subsampling.
* Add from_coords parameter to PhotoImage methods copy(), zoom() and subsample().
* Add zoom and subsample parameters to PhotoImage method copy().
This commit is contained in:
Serhiy Storchaka 2024-05-06 17:33:15 +03:00 committed by GitHub
parent 09871c9223
commit 1b639a04ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 271 additions and 20 deletions

View file

@ -4278,33 +4278,112 @@ class PhotoImage(Image):
def __getitem__(self, key):
return self.tk.call(self.name, 'cget', '-' + key)
# XXX copy -from, -to, ...?
def copy(self):
"""Return a new PhotoImage with the same image as this widget."""
def copy(self, *, from_coords=None, zoom=None, subsample=None):
"""Return a new PhotoImage with the same image as this widget.
The FROM_COORDS option specifies a rectangular sub-region of the
source image to be copied. It must be a tuple or a list of 1 to 4
integers (x1, y1, x2, y2). (x1, y1) and (x2, y2) specify diagonally
opposite corners of the rectangle. If x2 and y2 are not specified,
the default value is the bottom-right corner of the source image.
The pixels copied will include the left and top edges of the
specified rectangle but not the bottom or right edges. If the
FROM_COORDS option is not given, the default is the whole source
image.
If SUBSAMPLE or ZOOM are specified, the image is transformed as in
the subsample() or zoom() methods. The value must be a single
integer or a pair of integers.
"""
destImage = PhotoImage(master=self.tk)
self.tk.call(destImage, 'copy', self.name)
destImage.copy_replace(self, from_coords=from_coords,
zoom=zoom, subsample=subsample)
return destImage
def zoom(self, x, y=''):
def zoom(self, x, y='', *, from_coords=None):
"""Return a new PhotoImage with the same image as this widget
but zoom it with a factor of x in the X direction and y in the Y
direction. If y is not given, the default value is the same as x.
"""
destImage = PhotoImage(master=self.tk)
if y=='': y=x
self.tk.call(destImage, 'copy', self.name, '-zoom',x,y)
return destImage
but zoom it with a factor of X in the X direction and Y in the Y
direction. If Y is not given, the default value is the same as X.
def subsample(self, x, y=''):
"""Return a new PhotoImage based on the same image as this widget
but use only every Xth or Yth pixel. If y is not given, the
default value is the same as x.
The FROM_COORDS option specifies a rectangular sub-region of the
source image to be copied, as in the copy() method.
"""
destImage = PhotoImage(master=self.tk)
if y=='': y=x
self.tk.call(destImage, 'copy', self.name, '-subsample',x,y)
return destImage
return self.copy(zoom=(x, y), from_coords=from_coords)
def subsample(self, x, y='', *, from_coords=None):
"""Return a new PhotoImage based on the same image as this widget
but use only every Xth or Yth pixel. If Y is not given, the
default value is the same as X.
The FROM_COORDS option specifies a rectangular sub-region of the
source image to be copied, as in the copy() method.
"""
if y=='': y=x
return self.copy(subsample=(x, y), from_coords=from_coords)
def copy_replace(self, sourceImage, *, from_coords=None, to=None, shrink=False,
zoom=None, subsample=None, compositingrule=None):
"""Copy a region from the source image (which must be a PhotoImage) to
this image, possibly with pixel zooming and/or subsampling. If no
options are specified, this command copies the whole of the source
image into this image, starting at coordinates (0, 0).
The FROM_COORDS option specifies a rectangular sub-region of the
source image to be copied. It must be a tuple or a list of 1 to 4
integers (x1, y1, x2, y2). (x1, y1) and (x2, y2) specify diagonally
opposite corners of the rectangle. If x2 and y2 are not specified,
the default value is the bottom-right corner of the source image.
The pixels copied will include the left and top edges of the
specified rectangle but not the bottom or right edges. If the
FROM_COORDS option is not given, the default is the whole source
image.
The TO option specifies a rectangular sub-region of the destination
image to be affected. It must be a tuple or a list of 1 to 4
integers (x1, y1, x2, y2). (x1, y1) and (x2, y2) specify diagonally
opposite corners of the rectangle. If x2 and y2 are not specified,
the default value is (x1,y1) plus the size of the source region
(after subsampling and zooming, if specified). If x2 and y2 are
specified, the source region will be replicated if necessary to fill
the destination region in a tiled fashion.
If SHRINK is true, the size of the destination image should be
reduced, if necessary, so that the region being copied into is at
the bottom-right corner of the image.
If SUBSAMPLE or ZOOM are specified, the image is transformed as in
the subsample() or zoom() methods. The value must be a single
integer or a pair of integers.
The COMPOSITINGRULE option specifies how transparent pixels in the
source image are combined with the destination image. When a
compositing rule of 'overlay' is set, the old contents of the
destination image are visible, as if the source image were printed
on a piece of transparent film and placed over the top of the
destination. When a compositing rule of 'set' is set, the old
contents of the destination image are discarded and the source image
is used as-is. The default compositing rule is 'overlay'.
"""
options = []
if from_coords is not None:
options.extend(('-from', *from_coords))
if to is not None:
options.extend(('-to', *to))
if shrink:
options.append('-shrink')
if zoom is not None:
if not isinstance(zoom, (tuple, list)):
zoom = (zoom,)
options.extend(('-zoom', *zoom))
if subsample is not None:
if not isinstance(subsample, (tuple, list)):
subsample = (subsample,)
options.extend(('-subsample', *subsample))
if compositingrule:
options.extend(('-compositingrule', compositingrule))
self.tk.call(self.name, 'copy', sourceImage, *options)
def get(self, x, y):
"""Return the color (red, green, blue) of the pixel at X,Y."""