Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 519122

Summary: GC state is invalid for asynchronously drawn images
Product: [RT] RAP Reporter: Linuxhippy <linuxhippy>
Component: RWTAssignee: Project Inbox <rap-inbox>
Status: RESOLVED FIXED QA Contact:
Severity: normal    
Priority: P3    
Version: 3.2   
Target Milestone: 3.5   
Hardware: PC   
OS: Linux   
Whiteboard:

Description Linuxhippy CLA 2017-07-03 13:54:58 EDT
When drawing images using GC, two different code-paths can be taken:

- When the image is already loaded on the client, it is drawn immediatly - in the sequence the drawing commands are issued on the server-side
- When the image has to be loaded, it is drawn asynchonously in an onLoad handler. 

The async code path however, is severly broken. A comment mentions z-ordering will be wrong, but actually the whole GC state (transformation, clip, ....) can be invalid - because the drawImage call is issued at an unknown time - however the GC state depends on correct ordering of draw- and state-changing-calls.
Currently only the global alpha property is correctly restored.

The code in question:
      // On (native) canvas, only loaded images can be drawn:
      if( image.complete ) {
        this._context.drawImage.apply( this._context, args );
      } else {
        var alpha = this._context.globalAlpha;
        var context = this._context;
        image.onload = function() {
          // TODO [tb] : The z-order will be wrong in this case.
          context.save();
          context.globalAlpha = alpha;
          context.drawImage.apply( context, args );
          context.restore();
        };


Testcase to demonstrate the issue - an image should be drawn with translation, however it is drawn at 0/0:

  protected void createContents(Composite parent) {    	
	 
	Canvas child = new Canvas(parent, SWT.NONE);
	child.addPaintListener(new PaintListener() {
		public void paintControl(PaintEvent e) {
			//create SWT image from BufferedImage
			BufferedImage bImg = new BufferedImage(1024, 1024, BufferedImage.TYPE_INT_RGB);
			Graphics2D g2d = (Graphics2D) bImg.getGraphics();
			g2d.setColor(new Color(colorRandom.nextInt(255), colorRandom.nextInt(255), colorRandom.nextInt(255)));
			g2d.fillRect(0, 0, 1024, 1024);
			Image img = new Image(parent.getDisplay(), bufferedToSWTImageData(bImg));
			
			// clear canvas
		     e.gc.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
		     e.gc.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
		     e.gc.fillRectangle(0, 0, 1024, 1024);
		     
		     Transform transform = new Transform(parent.getDisplay());
		     transform.translate(100, 20);
		     e.gc.setTransform(transform);
		     
		     // drawImage is uncached -> async -> broken GC state
		     e.gc.drawImage(img, 0, 0);

		     //this is actually the transform that will be used to draw the Image
		     e.gc.setTransform(null);
		     
		     transform.dispose();
		     img.dispose();
		}
	});
	
	
	Button repaintBtn = new Button(parent, SWT.None);
	repaintBtn.setText("Repaint");
	repaintBtn.addSelectionListener(new SelectionAdapter() {
		public void widgetSelected(SelectionEvent e) {
			child.redraw();
		}
	});
	
  }
	
	
	protected ImageData bufferedToSWTImageData(BufferedImage bufferedImage) {
		DirectColorModel colorModel = (DirectColorModel) bufferedImage.getColorModel();
		PaletteData palette = new PaletteData(colorModel.getRedMask(), colorModel.getGreenMask(),
				colorModel.getBlueMask());
		ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
				colorModel.getPixelSize(), palette);
		for (int y = 0; y < data.height; y++) {
			for (int x = 0; x < data.width; x++) {
				data.setPixel(x, y, bufferedImage.getRGB(x, y));
			}
		}
		
		return data;
	}
Comment 1 Linuxhippy CLA 2017-08-28 08:01:55 EDT
Now with the fix for bug 512268 keeping the GC state on the client, it is possible to take a snapshot of that state at the time the drawImage() call was issued - and re-apply the state at the time the image is actually loaded.

Althouhg this does not solve the z-ordering issue (as the image is still drawn out-of-sequence), but at least it wil be drawn with the GC state at the time the drawImage command would have been executed in case the image was immediatly available.

I have a working patch available locally and will post it for review as soon as the fix for bug 512268 is in.
Comment 2 Ivan Furnadjiev CLA 2018-08-13 03:49:04 EDT
This issue should be fixed in RAP 3.5.
Comment 3 Linuxhippy CLA 2018-08-22 13:01:53 EDT
thanks a lot for taking care about this issue!