Tk Source Code

Check-in [84c2ba4f]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Alternative fix for CreateCGImageFromDrawableRect() returning smaller image than requested. See [f215a9fdaa].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | bug-df4b603950-alt
Files: files | file ages | folders
SHA3-256: 84c2ba4fc8d483477fbcf68d709f91928c9aebb96273743dc1d4c5f729ceac6c
User & Date: chrstphrchvz 2025-08-16 01:33:04.505
Context
2025-08-16
01:36
Prevent a faint outline from appearing when downscaling Retina captures Leaf check-in: 45ebf58a user: chrstphrchvz tags: bug-df4b603950-alt
01:33
Alternative fix for CreateCGImageFromDrawableRect() returning smaller image than requested. See [f215a9fdaa]. check-in: 84c2ba4f user: chrstphrchvz tags: bug-df4b603950-alt
2025-08-15
22:23
Merge 8.6 check-in: d266a4ff user: jan.nijtmans tags: core-9-0-branch
Changes
Unified Diff Ignore Whitespace Patch
Changes to macosx/tkMacOSXImage.c.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "tkMacOSXConstants.h"
#include "tkMacOSXImage.h"
#include "tkColor.h"
#include "xbytes.h"

static CGImageRef CreateCGImageFromPixmap(Drawable pixmap);
static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, int force_1x_scale,
     int x, int y, unsigned int width, unsigned int height, CGFloat *scale);
static inline CGRect ClipCopyRects(CGRect srcBounds, CGRect dstBounds,
     int src_x, int src_y, unsigned int width,  unsigned int height);

/* Pixel formats
 *
 * Tk uses the XImage structure defined in Xlib.h for storing images.  The
 * image data in an XImage is a 32-bit aligned array of bytes.  Interpretation







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "tkMacOSXConstants.h"
#include "tkMacOSXImage.h"
#include "tkColor.h"
#include "xbytes.h"

static CGImageRef CreateCGImageFromPixmap(Drawable pixmap);
static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, int force_1x_scale,
	   int x, int y, unsigned int width, unsigned int height);
static inline CGRect ClipCopyRects(CGRect srcBounds, CGRect dstBounds,
     int src_x, int src_y, unsigned int width,  unsigned int height);

/* Pixel formats
 *
 * Tk uses the XImage structure defined in Xlib.h for storing images.  The
 * image data in an XImage is a 32-bit aligned array of bytes.  Interpretation
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679

680
681

682

683
684
685
686
687


688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
static CGImageRef
CreateCGImageFromDrawableRect(
    Drawable drawable,
    int force_1x_scale,
    int x,
    int y,
    unsigned int width,
    unsigned int height,
    CGFloat *scalePtr)
{
    MacDrawable *mac_drawable = (MacDrawable *)drawable;
    CGContextRef cg_context = NULL;
    CGImageRef cg_image = NULL, result = NULL;
    CGFloat scaleFactor = 1.0;
    if (mac_drawable->flags & TK_IS_PIXMAP) {
	cg_context = TkMacOSXGetCGContextForDrawable(drawable);
	CGContextRetain(cg_context);
    } else {
	NSView *view = TkMacOSXGetNSViewForDrawable(mac_drawable);
	if (view == nil) {
	    TkMacOSXDbgMsg("Invalid source drawable");
	    return NULL;
	}
	scaleFactor = view.layer.contentsScale;
	cg_context = ((TKContentView *)view).tkLayerBitmapContext;
	CGContextRetain(cg_context);
    }
    if (scalePtr != nil) {
	*scalePtr = scaleFactor;
    }
    if (cg_context) {
	cg_image = CGBitmapContextCreateImage(cg_context);
	CGContextRelease(cg_context);
    }
    if (cg_image) {

	CGRect rect = CGRectMake(x + mac_drawable->xOff, y + mac_drawable->yOff,
				 width, height);

	rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(scaleFactor, scaleFactor));

	if (force_1x_scale && (scaleFactor != 1.0)) {
	    // See https://web.archive.org/web/20200219030756/http://blog.foundry376.com/2008/07/scaling-a-cgimage/#comment-200
	    // create context, keeping original image properties
	    CGColorSpaceRef colorspace = CGImageGetColorSpace(cg_image);
	    cg_context = CGBitmapContextCreate(NULL, width, height,


		    CGImageGetBitsPerComponent(cg_image),
		    //CGImageGetBytesPerRow(cg_image), // wastes space?
		    CGImageGetBitsPerPixel(cg_image) * width / 8,
		    colorspace,
		    CGImageGetAlphaInfo(cg_image));
	    CGColorSpaceRelease(colorspace);
	    if (cg_context) {
		// Extract the subimage in the specified rectangle.
		CGImageRef subimage = CGImageCreateWithImageInRect(cg_image, rect);
		// Draw the subimage in our context (resizing it to fit).
		CGContextDrawImage(cg_context, CGRectMake(0, 0, width, height),
			subimage);
		// We will return the image we just drew.
		result = CGBitmapContextCreateImage(cg_context);
		CGContextRelease(cg_context);
		CGImageRelease(subimage);
	    }
	} else {
	    // No resizing is needed.  Just return the subimage
	    result = CGImageCreateWithImageInRect(cg_image, rect);
	}
	CGImageRelease(cg_image);
    }
    return result;
}

/*







|
<




|









|



<
<
<





>
|
|
>
|
>
|
|
|
|
|
>
>
|
<
|
|
|
|
|
<
<
|
|
<
|
|
|
<
<
<
<
<







645
646
647
648
649
650
651
652

653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670



671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689

690
691
692
693
694


695
696

697
698
699





700
701
702
703
704
705
706
static CGImageRef
CreateCGImageFromDrawableRect(
    Drawable drawable,
    int force_1x_scale,
    int x,
    int y,
    unsigned int width,
    unsigned int height)

{
    MacDrawable *mac_drawable = (MacDrawable *)drawable;
    CGContextRef cg_context = NULL;
    CGImageRef cg_image = NULL, result = NULL;
    CGFloat srcScaleFactor = 1.0;
    if (mac_drawable->flags & TK_IS_PIXMAP) {
	cg_context = TkMacOSXGetCGContextForDrawable(drawable);
	CGContextRetain(cg_context);
    } else {
	NSView *view = TkMacOSXGetNSViewForDrawable(mac_drawable);
	if (view == nil) {
	    TkMacOSXDbgMsg("Invalid source drawable");
	    return NULL;
	}
	srcScaleFactor = view.layer.contentsScale;
	cg_context = ((TKContentView *)view).tkLayerBitmapContext;
	CGContextRetain(cg_context);
    }



    if (cg_context) {
	cg_image = CGBitmapContextCreateImage(cg_context);
	CGContextRelease(cg_context);
    }
    if (cg_image) {
	CGFloat dstScaleFactor = force_1x_scale ? 1.0 : srcScaleFactor;
	CGRect rect = CGRectMake(-srcScaleFactor*(x + mac_drawable->xOff),
		srcScaleFactor*((int)height + y + mac_drawable->yOff) - (int)CGImageGetHeight(cg_image),
		CGImageGetWidth(cg_image), CGImageGetHeight(cg_image));
	rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(
		dstScaleFactor/srcScaleFactor, dstScaleFactor/srcScaleFactor));

	// See https://web.archive.org/web/20200219030756/http://blog.foundry376.com/2008/07/scaling-a-cgimage/#comment-200
	// create context, keeping original image properties; must have the requested width and height
	CGColorSpaceRef colorspace = CGImageGetColorSpace(cg_image);
	cg_context = CGBitmapContextCreate(NULL,
		width * dstScaleFactor,
		height * dstScaleFactor,
		CGImageGetBitsPerComponent(cg_image),

		CGImageGetBitsPerPixel(cg_image) * width * dstScaleFactor / 8,
		colorspace,
		CGImageGetAlphaInfo(cg_image));
	CGColorSpaceRelease(colorspace);
	if (cg_context) {


	    // Draw the source at an offset in our context (resizing it to fit).
	    CGContextDrawImage(cg_context, rect, cg_image);

	    // We will return the image we just drew.
	    result = CGBitmapContextCreateImage(cg_context);
	    CGContextRelease(cg_context);





	}
	CGImageRelease(cg_image);
    }
    return result;
}

/*
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
    if (format == ZPixmap) {
	CGImageRef cgImage;
	if (width == 0 || height == 0) {
	    return NULL;
	}

	// Request 1x-scale image for compatibility
	cgImage = CreateCGImageFromDrawableRect(drawable, 1, x, y, width, height, nil);
	if (cgImage) {
	    bitmapRep = [NSBitmapImageRep alloc];
	    [bitmapRep initWithCGImage:cgImage];
	    CFRelease(cgImage);
	} else {
	    TkMacOSXDbgMsg("XGetImage: Failed to construct CGImage");
	    return NULL;







|







828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
    if (format == ZPixmap) {
	CGImageRef cgImage;
	if (width == 0 || height == 0) {
	    return NULL;
	}

	// Request 1x-scale image for compatibility
	cgImage = CreateCGImageFromDrawableRect(drawable, 1, x, y, width, height);
	if (cgImage) {
	    bitmapRep = [NSBitmapImageRep alloc];
	    [bitmapRep initWithCGImage:cgImage];
	    CFRelease(cgImage);
	} else {
	    TkMacOSXDbgMsg("XGetImage: Failed to construct CGImage");
	    return NULL;
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
    NSRect srcRect, dstRect;
    int result = 0;
    NSView *view = TkMacOSXGetNSViewForDrawable(drawable);
    CGRect viewBounds = [view bounds];

    /*
     * To compute the damage region correctly we need to clip the source and
     * destination rectangles to the NSView bounds in the same way that
     * XCopyArea does.
     */

    CGRect bounds = ClipCopyRects(viewBounds, viewBounds, x, y, width, height);
    unsigned int w = bounds.size.width;
    unsigned int h = bounds.size.height;

    if (XCopyArea(Tk_Display(tkwin), drawable, drawable, gc, x, y,







|
<







976
977
978
979
980
981
982
983

984
985
986
987
988
989
990
    NSRect srcRect, dstRect;
    int result = 0;
    NSView *view = TkMacOSXGetNSViewForDrawable(drawable);
    CGRect viewBounds = [view bounds];

    /*
     * To compute the damage region correctly we need to clip the source and
     * destination rectangles to the NSView bounds.

     */

    CGRect bounds = ClipCopyRects(viewBounds, viewBounds, x, y, width, height);
    unsigned int w = bounds.size.width;
    unsigned int h = bounds.size.height;

    if (XCopyArea(Tk_Display(tkwin), drawable, drawable, gc, x, y,
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095

1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
    int dst_x,			/* Dest X & Y on dest rect. */
    int dst_y)
{
    TkMacOSXDrawingContext dc;
    CGImageRef img = NULL;
    CGRect dstRect;

    // XXXX Need to deal with pixmaps!

    NSView *srcView = TkMacOSXGetNSViewForDrawable(src);
    NSView *dstView = TkMacOSXGetNSViewForDrawable(dst);
    CGRect srcBounds = [srcView bounds];
    CGRect dstBounds = [dstView bounds];

    // To avoid distorting the image when it is drawn we must ensure that
    // the source and destination rectangles have the same size.  This is
    // tricky because each of those rectangles will be clipped to the
    // bounds of its containing NSView.  If the source gets clipped and
    // the destination does not, for example, then the shapes will differ.
    // We deal with this by reducing their common size  enough so that both
    // rectangles are  contained in their respective views.

    CGRect bounds = ClipCopyRects(srcBounds, dstBounds, src_x, src_y, width, height);
    width = (int) bounds.size.width;
    height = (int) bounds.size.height;
    CGFloat scaleFactor;

    LastKnownRequestProcessed(display)++;
    if (!width || !height) {
	return BadDrawable;
    }

    if (!TkMacOSXSetupDrawingContext(dst, gc, &dc)) {
	TkMacOSXDbgMsg("Failed to setup drawing context.");
	return BadDrawable;
    }

    if (!dc.context) {
	TkMacOSXDbgMsg("Invalid destination drawable - no context.");
	return BadDrawable;
    }


    img = CreateCGImageFromDrawableRect(src, 0, src_x, src_y, width, height, &scaleFactor);

    if (img) {
	unsigned int w = (unsigned int) (CGImageGetWidth(img) / scaleFactor);
	unsigned int h = (unsigned int) (CGImageGetHeight(img) / scaleFactor);
	dstRect = CGRectMake(dst_x, dst_y, w, h);
	TkMacOSXDrawCGImage(dst, gc, dc.context, img,
		gc->foreground, gc->background, dstRect);
	CFRelease(img);
    } else {
	TkMacOSXDbgMsg("Failed to construct CGImage.");
    }








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<















>
|


<
<
|







1045
1046
1047
1048
1049
1050
1051




















1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070


1071
1072
1073
1074
1075
1076
1077
1078
    int dst_x,			/* Dest X & Y on dest rect. */
    int dst_y)
{
    TkMacOSXDrawingContext dc;
    CGImageRef img = NULL;
    CGRect dstRect;





















    LastKnownRequestProcessed(display)++;
    if (!width || !height) {
	return BadDrawable;
    }

    if (!TkMacOSXSetupDrawingContext(dst, gc, &dc)) {
	TkMacOSXDbgMsg("Failed to setup drawing context.");
	return BadDrawable;
    }

    if (!dc.context) {
	TkMacOSXDbgMsg("Invalid destination drawable - no context.");
	return BadDrawable;
    }

    // Use unscaled source (TkMacOSXDrawCGImage() will implicitly downscale)
    img = CreateCGImageFromDrawableRect(src, 0, src_x, src_y, width, height);

    if (img) {


	dstRect = CGRectMake(dst_x, dst_y, width, height);
	TkMacOSXDrawCGImage(dst, gc, dc.context, img,
		gc->foreground, gc->background, dstRect);
	CFRelease(img);
    } else {
	TkMacOSXDbgMsg("Failed to construct CGImage.");
    }