Skip to content

IllegalArgumentException when painting image while running out of handles #3443

Description

@ptziegler

Describe the bug

Trying to paint an image while all available handles have been consumed fails with an IllegalArgumentException instead of an SWTError.

To Reproduce

package gef;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class DrawGC2 {

	public static void main(String[] args) {
		Shell shell = new Shell();
		Display display = shell.getDisplay();

		int width = 310;
		int height = 310;

		List<Image> images = new ArrayList<>();
		for (int i = 0; i < 8000; ++i) {
			Image image = new Image(display, width, height);
			GC gc = new GC(image);
			gc.dispose();
			images.add(image);
		}

		shell.addPaintListener(event -> {
			for (Image image : images) {
				event.gc.drawImage(image, 0, 0, width, height, 0, 0, 100, 100);
			}
		});

		shell.open();

		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}

		images.forEach(Image::dispose);
	}
}

Stacktrace:

Exception in thread "main" java.lang.IllegalArgumentException: Argument not valid
	at org.eclipse.swt.SWT.error(SWT.java:4915)
	at org.eclipse.swt.SWT.error(SWT.java:4849)
	at org.eclipse.swt.SWT.error(SWT.java:4820)
	at org.eclipse.swt.graphics.ImageData.<init>(ImageData.java:439)
	at org.eclipse.swt.graphics.ImageData.<init>(ImageData.java:287)
	at org.eclipse.swt.graphics.Image$DestroyableImageHandle.getImageData(Image.java:3305)
	at org.eclipse.swt.graphics.Image$AbstractImageProviderWrapper.getClosestAvailableImageData(Image.java:2106)
	at org.eclipse.swt.graphics.Image$PlainImageProviderWrapper.loadImageData(Image.java:2388)
	at org.eclipse.swt.graphics.Image$HandleAtSize.getOrCreateImageHandleAtClosestSize(Image.java:254)
	at org.eclipse.swt.graphics.Image$HandleAtSize.lambda$0(Image.java:225)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at org.eclipse.swt.graphics.Image$HandleAtSize.refresh(Image.java:225)
	at org.eclipse.swt.graphics.Image.executeOnImageHandleAtBestFittingSize(Image.java:968)
	at org.eclipse.swt.graphics.GC$DrawScalingImageToImageOperation.draw(GC.java:1240)
	at org.eclipse.swt.graphics.GC$DrawScalingImageToImageOperation.apply(GC.java:1222)
	at org.eclipse.swt.graphics.GC.storeAndApplyOperationForExistingHandle(GC.java:6136)
	at org.eclipse.swt.graphics.GC.drawImage(GC.java:1152)
	at gef.DrawGC2.lambda$0(DrawGC2.java:30)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:272)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4363)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1217)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1241)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1226)
	at org.eclipse.swt.widgets.Composite.WM_PAINT(Composite.java:1627)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:5021)
	at org.eclipse.swt.widgets.Canvas.windowProc(Canvas.java:336)
	at org.eclipse.swt.widgets.Decorations.windowProc(Decorations.java:1490)
	at org.eclipse.swt.widgets.Shell.windowProc(Shell.java:2446)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:5131)
	at org.eclipse.swt.internal.win32.OS.UpdateWindow(Native Method)
	at org.eclipse.swt.widgets.Decorations.setVisible(Decorations.java:1322)
	at org.eclipse.swt.widgets.Shell.setVisible(Shell.java:2192)
	at org.eclipse.swt.widgets.Shell.open(Shell.java:1334)
	at gef.DrawGC2.main(DrawGC2.java:34)

Expected behavior

SWT should return a proper error message. Ideally an "No more handles" error.

Environment:

  1. Select the platform(s) on which the behavior is seen:
    • All OS
    • Windows
    • Linux
    • macOS

Version since
Only tested on master.

Additional information

I think the problem is that the return value is not validated when calling GetDIBits, for example here:

/* Call with null lpBits to get the image size */
OS.GetDIBits(hBitmapDC, handle(), 0, height, null, bmi, OS.DIB_RGB_COLORS);
OS.MoveMemory(bmiHeader, bmi, BITMAPINFOHEADER.sizeof);
imageSize = bmiHeader.biSizeImage;

From the documentation:

If the function fails, the return value is zero.

Which is the case here, resulting in an "empty" image data, causing the validation error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    WindowsHappens on Windows OS

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions