| Summary: | GC state is invalid for asynchronously drawn images | ||
|---|---|---|---|
| Product: | [RT] RAP | Reporter: | Linuxhippy <linuxhippy> |
| Component: | RWT | Assignee: | 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: | |||
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. This issue should be fixed in RAP 3.5. thanks a lot for taking care about this issue! |
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; }