Tk Source Code

Check-in [bfebc1f4]
Login

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

Overview
Comment:Fix [4af5ca1921]: XCopyArea is slow on macOS
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | bug-4af5ca1921
Files: files | file ages | folders
SHA3-256: bfebc1f40230be72bb1bd7848826e5cf932b25c811d1c4edbb519174b91e14ea
User & Date: culler 2025-07-26 18:49:13.778
Context
2025-07-26
18:49
Fix [4af5ca1921]: XCopyArea is slow on macOS Leaf check-in: bfebc1f4 user: culler tags: bug-4af5ca1921
2025-07-25
03:21
Fix [3d13f87c8f] and [e90e8ca1a6]: make the stdin text more readable when in dark mode on Aqua Leaf check-in: a6f28453 user: culler tags: core-9-0-branch
Changes
Unified Diff Ignore Whitespace Patch
Changes to macosx/tkMacOSXImage.c.
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
 *      back to the window.  Platforms, such as macOS, on which the system
 *      provides double-buffered drawing and GPU-based composition operations
 *      can avoid calls to XGetImage and XCopyArea from the core by defining
 *      the compile-time variable TK_NO_DOUBLE_BUFFERING.  Nonetheless, these
 *      two functions are in the stubs table and therefore could be used by
 *      extensions.
 *
 *      The implementation here does not always work correctly when the source
 *      is a window.  The original version of this function relied on
 *      [NSBitmapImageRep initWithFocusedViewRect:view_rect] which was
 *      deprecated by Apple in OSX 10.14 and also required the use of other
 *      deprecated functions such as [NSView lockFocus]. Apple's suggested
 *      replacement is [NSView cacheDisplayInRect: toBitmapImageRep:] and that
 *      is being used here.  However, cacheDisplayInRect works by calling
 *      [NSView drawRect] after setting the current graphics context to be one
 *      which draws to a bitmap.  There are situations in which this can be
 *      used, e.g. when taking a screenshot of a window.  But it cannot be used
 *      as part of a normal display procedure, using the copy-modify-paste
 *      paradigm that is the basis of the explicit double-buffering.  Since the
 *      copy operation will call the same display procedure that is calling
 *      this function via XGetImage or XCopyArea, this would create an infinite
 *      recursion.
 *
 *      An alternative to the copy-modify-paste paradigm is to use GPU-based
 *      graphics composition, clipping to the specified rectangle.  That is
 *      the approach that must be followed by display procedures on macOS.
 *
 * Results:
 *	Returns an NSBitmapRep representing the image of the given rectangle of
 *      the given drawable. This object is retained. The caller is responsible
 *      for releasing it.
 *
 *      NOTE: The x,y coordinates should be relative to a coordinate system
 *      with origin at the top left, as used by XImage and CGImage, not bottom







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







598
599
600
601
602
603
604




















605
606
607
608
609
610
611
 *      back to the window.  Platforms, such as macOS, on which the system
 *      provides double-buffered drawing and GPU-based composition operations
 *      can avoid calls to XGetImage and XCopyArea from the core by defining
 *      the compile-time variable TK_NO_DOUBLE_BUFFERING.  Nonetheless, these
 *      two functions are in the stubs table and therefore could be used by
 *      extensions.
 *




















 * Results:
 *	Returns an NSBitmapRep representing the image of the given rectangle of
 *      the given drawable. This object is retained. The caller is responsible
 *      for releasing it.
 *
 *      NOTE: The x,y coordinates should be relative to a coordinate system
 *      with origin at the top left, as used by XImage and CGImage, not bottom
1055
1056
1057
1058
1059
1060
1061
1062

1063
1064
1065
1066
1067
1068
1069
    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







|
>







1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
    int dst_y)
{
    TkMacOSXDrawingContext dc;
    CGImageRef img = NULL;
    CGRect dstRect;

    // XXXX Need to deal with pixmaps!
    MacDrawable *srcDraw = (MacDrawable *)dst;
    MacDrawable *dstDraw = (MacDrawable *)dst;
    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
1088
1089
1090
1091
1092
1093
1094
1095


1096









1097


1098
1099
1100
1101
1102
1103
1104
1105
1106

1107
1108
1109
1110
1111
1112
1113
	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.");

    }

    TkMacOSXRestoreDrawingContext(&dc);
    return Success;
}

/*







|
>
>
|
>
>
>
>
>
>
>
>
>
|
>
>
|
|
|
|
|
|
|
|
|
>







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
	return BadDrawable;
    }

    if (!dc.context) {
	TkMacOSXDbgMsg("Invalid destination drawable - no context.");
	return BadDrawable;
    }
    
    if ((srcDraw->flags & TK_IS_PIXMAP) && (dstDraw->flags & TK_IS_PIXMAP)){
	// If source and destination are both pixmaps we don't need to scale.
	CGRect srcRect = CGRectMake(src_x, src_y, width, height);
	CGImageRef full = CreateCGImageFromPixmap(src);
	img =  CGImageCreateWithImageInRect(full, srcRect);
	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.");
	}
    } else {
	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.");
	}
    }

    TkMacOSXRestoreDrawingContext(&dc);
    return Success;
}

/*