Commit ec93b0b941d for php.net

commit ec93b0b941dc6ed7280184216d2d8d59f173e224
Author: Weilin Du <weilindu@php.net>
Date:   Sat Aug 29 00:25:55 2026 +0800

    Fix GH-23459: Improve imagegrabscreen() performance on Windows (#23485)

    A roughly 814x optimization in total for the two function.

diff --git a/UPGRADING b/UPGRADING
index 10364c88b65..78074404b40 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -939,6 +939,10 @@ PHP 8.6 UPGRADE NOTES
 - DOM:
   . Made splitText() faster and consume less memory.

+- GD:
+  . Improved performance of imagegrabscreen() and imagegrabwindow() on
+    Windows.
+
 - JSON:
   . Improve performance of encoding arrays and objects.
   . Improved performance of indentation generation in json_encode()
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index a6d51ccbce8..7f55b8bcc74 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -1097,6 +1097,51 @@ PHP_FUNCTION(imagecopyresampled)
 /* }}} */

 #ifdef PHP_WIN32
+/* The bitmap must not be selected into a device context. */
+static gdImagePtr php_gd_image_from_bitmap(HDC hdc, HBITMAP bitmap, int width, int height)
+{
+	BITMAPINFO bitmap_info = {0};
+	RGBQUAD *pixels;
+	gdImagePtr im;
+	size_t num_pixels;
+	bool overflow;
+	int x, y;
+
+	bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+	bitmap_info.bmiHeader.biWidth = width;
+	/* Request a top-down DIB so its row order matches GD's. */
+	bitmap_info.bmiHeader.biHeight = -height;
+	bitmap_info.bmiHeader.biPlanes = 1;
+	bitmap_info.bmiHeader.biBitCount = 32;
+	bitmap_info.bmiHeader.biCompression = BI_RGB;
+
+	num_pixels = zend_safe_address((size_t) width, (size_t) height, 0, &overflow);
+	if (overflow) {
+		return NULL;
+	}
+
+	pixels = safe_emalloc(num_pixels, sizeof(*pixels), 0);
+	if (GetDIBits(hdc, bitmap, 0, (UINT) height, pixels, &bitmap_info, DIB_RGB_COLORS) != height) {
+		efree(pixels);
+		return NULL;
+	}
+
+	im = gdImageCreateTrueColor(width, height);
+	if (im) {
+		for (y = 0; y < height; y++) {
+			const RGBQUAD *src = pixels + (size_t) y * width;
+			int *dst = im->tpixels[y];
+
+			for (x = 0; x < width; x++) {
+				dst[x] = gdTrueColor(src[x].rgbRed, src[x].rgbGreen, src[x].rgbBlue);
+			}
+		}
+	}
+
+	efree(pixels);
+	return im;
+}
+
 /* {{{ Grab a window or its client area using a windows handle (HWND property in COM instance) */
 PHP_FUNCTION(imagegrabwindow)
 {
@@ -1144,18 +1189,8 @@ PHP_FUNCTION(imagegrabwindow)

 	PrintWindow(window, memDC, (UINT) client_area);

-	im = gdImageCreateTrueColor(Width, Height);
-	if (im) {
-		int x,y;
-		for (y=0; y <= Height; y++) {
-			for (x=0; x <= Width; x++) {
-				int c = GetPixel(memDC, x,y);
-				gdImageSetPixel(im, x, y, gdTrueColor(GetRValue(c), GetGValue(c), GetBValue(c)));
-			}
-		}
-	}
-
 	SelectObject(memDC,hOld);
+	im = php_gd_image_from_bitmap(hdc, memBM, Width, Height);
 	DeleteObject(memBM);
 	DeleteDC(memDC);
 	ReleaseDC( 0, hdc );
@@ -1198,18 +1233,8 @@ PHP_FUNCTION(imagegrabscreen)
 	hOld	= (HBITMAP) SelectObject (memDC, memBM);
 	BitBlt( memDC, 0, 0, Width, Height , hdc, rc.left, rc.top , SRCCOPY );

-	im = gdImageCreateTrueColor(Width, Height);
-	if (im) {
-		int x,y;
-		for (y=0; y <= Height; y++) {
-			for (x=0; x <= Width; x++) {
-				int c = GetPixel(memDC, x,y);
-				gdImageSetPixel(im, x, y, gdTrueColor(GetRValue(c), GetGValue(c), GetBValue(c)));
-			}
-		}
-	}
-
 	SelectObject(memDC,hOld);
+	im = php_gd_image_from_bitmap(hdc, memBM, Width, Height);
 	DeleteObject(memBM);
 	DeleteDC(memDC);
 	ReleaseDC( 0, hdc );