Skip to content

Commit

Permalink
Improve paint.
Browse files Browse the repository at this point in the history
  • Loading branch information
ezh committed Feb 25, 2014
1 parent 3bde04f commit d60abbf
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 45 deletions.
106 changes: 65 additions & 41 deletions src/main/scala/org/digimead/digi/lib/jfx4swt/FXCanvas.scala
Expand Up @@ -43,7 +43,7 @@ class FXCanvas(parent: Composite, style: Int, val bindSceneSizeToCanvas: Boolean
* Such solution helps VM to GC complex circular references that are used here and there.
*/
/** Adapter between JavaFX EmbeddedWindow and SWT FXCanvas. */
@volatile protected var adapterInstance = createAdapter(bindSceneSizeToCanvas)
@volatile protected var adapterInstance = createAdapter(bindSceneSizeToCanvas, true)
@volatile protected var hostInstance = createHost(adapterInstance)
@volatile protected var stageInstance = createJFaceCanvas(hostInstance)
@volatile protected var preferredHeight = SWT.DEFAULT
Expand Down Expand Up @@ -93,7 +93,7 @@ class FXCanvas(parent: Composite, style: Int, val bindSceneSizeToCanvas: Boolean
def stage = Option(stageInstance)

/** Create adapter. */
protected def createAdapter(bindSceneSizeToCanvas: Boolean) = new Adapter(bindSceneSizeToCanvas)
protected def createAdapter(bindSceneSizeToCanvas: Boolean, antiFreeze: Boolean) = new Adapter(bindSceneSizeToCanvas, antiFreeze)
/** Create embedded. */
protected def createJFaceCanvas(host: FXHost) = new JFaceCanvas(WeakReference(host))
/** Create host. */
Expand All @@ -118,7 +118,7 @@ class FXCanvas(parent: Composite, style: Int, val bindSceneSizeToCanvas: Boolean
})
}

class Adapter(val bindSceneSizeToCanvas: Boolean) extends ControlAdapter with FXAdapter with PaintListener {
class Adapter(val bindSceneSizeToCanvas: Boolean, val antiFreeze: Boolean) extends ControlAdapter with FXAdapter with PaintListener {
private[this] final val paletteData = JFX.paletteData
private[this] final var imageDataFrameOne = new ImageData(1, 1, 32, paletteData, 4, new Array[Byte](4))
private[this] final var imageDataFrameTwo = new ImageData(1, 1, 32, paletteData, 4, new Array[Byte](4))
Expand All @@ -131,6 +131,10 @@ class FXCanvas(parent: Composite, style: Int, val bindSceneSizeToCanvas: Boolean
@volatile private[this] final var hostWidth = 0
private[this] final var display = parent.getDisplay()
private[this] final val disposeRWL = new ReentrantReadWriteLock()
private[this] final var paintLastX = 0
private[this] final var paintLastY = 0
private[this] final var paintLastWidth = 0
private[this] final var paintLastHeight = 0

/**
* Sent when the size (width, height) of a control changes.
Expand Down Expand Up @@ -194,6 +198,30 @@ class FXCanvas(parent: Composite, style: Int, val bindSceneSizeToCanvas: Boolean
if (event.width <= 0 || event.height <= 0)
return
paintControlToDraw = frameFull.getAndSet(null)
// Free shared resource early at the beginning.
if (paintControlToDraw != null) {
paintControlImageData = if (frameOne.get() == paintControlToDraw) {
frameEmpty.set(frameTwo.get())
if ((hostWidth * hostHeight * 4) != paintControlToDraw.length) {
null
} else {
if (imageDataFrameTwo.data != paintControlToDraw)
imageDataFrameTwo = new ImageData(hostWidth, hostHeight, 32, paletteData, 4, paintControlToDraw)
imageDataFrameTwo
}
} else {
frameEmpty.set(frameOne.get())
if ((hostWidth * hostHeight * 4) != paintControlToDraw.length) {
null
} else {
if (imageDataFrameOne.data != paintControlToDraw)
imageDataFrameOne = new ImageData(hostWidth, hostHeight, 32, paletteData, 4, paintControlToDraw)
imageDataFrameOne
}
}
} else if (paintLastX != event.x || paintLastY != event.y || paintLastWidth != event.width || paintLastHeight != event.height)
// Reset paintControlImageData since there was resize.
paintControlImageData = null
// Prepare offscreen image.
if (paintControlOffscreenImage == null ||
paintControlOffscreenBounds.width != hostWidth ||
Expand All @@ -214,48 +242,44 @@ class FXCanvas(parent: Composite, style: Int, val bindSceneSizeToCanvas: Boolean
paintControlGCOffscreenImage = null
}
}
if (paintControlToDraw != null) {
paintControlImageData = if (frameOne.get() == paintControlToDraw) {
frameEmpty.set(frameTwo.get())
if ((hostWidth * hostHeight * 4) != paintControlToDraw.length) {
null
} else {
if (imageDataFrameTwo.data != paintControlToDraw)
imageDataFrameTwo = new ImageData(hostWidth, hostHeight, 32, paletteData, 4, paintControlToDraw)
imageDataFrameTwo
}
// Obtain the next frame.
if (isVisible()) {
if (paintControlImageData != null) {
/*
* ---> HERE <---
* most CPU and memory hungry
*/
val imageFrame = new Image(event.display, paintControlImageData)
// Draw the image offscreen.
paintControlGCOffscreenImage.setBackground(event.gc.getBackground())
paintControlGCOffscreenImage.drawImage(imageFrame, 0, 0)
// Draw the offscreen buffer to the screen.
event.gc.fillRectangle(event.x, event.y, event.width, event.height)
event.gc.drawImage(paintControlOffscreenImage, 0, 0)
imageFrame.dispose()
// Reset paintControlImageData since there will maybe be resize.
paintLastX = event.x
paintLastY = event.y
paintLastWidth = event.width
paintLastHeight = event.height
} else {
frameEmpty.set(frameOne.get())
if ((hostWidth * hostHeight * 4) != paintControlToDraw.length) {
null
} else {
if (imageDataFrameOne.data != paintControlToDraw)
imageDataFrameOne = new ImageData(hostWidth, hostHeight, 32, paletteData, 4, paintControlToDraw)
imageDataFrameOne
}
}
// Obtain the next frame.
if (isVisible()) {
if (paintControlImageData != null) {
/*
* ---> HERE <---
* most CPU and memory hungry
*/
val imageFrame = new Image(event.display, paintControlImageData)
// Draw the image offscreen.
paintControlGCOffscreenImage.setBackground(event.gc.getBackground())
paintControlGCOffscreenImage.drawImage(imageFrame, 0, 0)
// Draw the offscreen buffer to the screen.
if (paintLastX != event.x || paintLastY != event.y || paintLastWidth != event.width || paintLastHeight != event.height)
event.gc.fillRectangle(event.x, event.y, event.width, event.height)
event.gc.drawImage(paintControlOffscreenImage, 0, 0)
imageFrame.dispose()
// Reset paintControlImageData since there will maybe be resize.
paintControlImageData = null
}
} else {
// This is a thread safe call.
hostInstance.embeddedScene.foreach(_.entireSceneNeedsRepaint())
event.gc.fillRectangle(event.x, event.y, event.width, event.height)
}
if (antiFreeze && paintControlImageData != null) {
// Anti freeze. Take last frame and compare it with previous.
val lastFrame = frameEmpty.getAndSet(null)
if (lastFrame != null && (lastFrame == frameOne.get() || lastFrame == frameTwo.get())) {
if (frameOne.get().deep != frameTwo.get().deep) {
// Renew if there is difference.
frameEmpty.set(lastFrame)
hostInstance.embeddedScene.foreach(_.entireSceneNeedsRepaint())
} else {
frameEmpty.set(lastFrame)
}
}
}
}
}
Expand Down
Expand Up @@ -66,7 +66,7 @@ class FXCanvasSpec extends FreeSpec with Matchers with LoggingHelper {
val shell = new Shell()
shell.setLayout(new FillLayout(SWT.VERTICAL))
val canvas = new FXCanvas(shell, SWT.NONE, false) {
override def createAdapter(bindSceneSizeToCanvas: Boolean) = new Adapter(bindSceneSizeToCanvas) {
override def createAdapter(bindSceneSizeToCanvas: Boolean, antiFreeze: Boolean) = new Adapter(bindSceneSizeToCanvas, antiFreeze) {
override def paintControl(event: org.eclipse.swt.events.PaintEvent) {
buf.append(System.currentTimeMillis())
super.paintControl(event)
Expand Down Expand Up @@ -199,8 +199,8 @@ class FXCanvasSpec extends FreeSpec with Matchers with LoggingHelper {
val shell = new Shell()
shell.setLayout(new FillLayout(SWT.VERTICAL))
val canvas = new FXCanvas(shell, SWT.NONE, false) {
override def createAdapter(bindSceneSizeToCanvas: Boolean) = {
adapter1 = spy(new Adapter(bindSceneSizeToCanvas))
override def createAdapter(bindSceneSizeToCanvas: Boolean, antiFreeze: Boolean) = {
adapter1 = spy(new Adapter(bindSceneSizeToCanvas, antiFreeze))
adapter1.asInstanceOf[this.Adapter]
}
}
Expand Down
Expand Up @@ -89,7 +89,7 @@ class FXMemoryLeaks extends FreeSpec with Matchers with LoggingHelper {
shell.addDisposeListener(new DisposeListener { def widgetDisposed(e: DisposeEvent) = latch.countDown() })
shell.setLayout(new FillLayout(SWT.VERTICAL))
val canvas = new FXCanvas(shell, SWT.NONE) {
override def createAdapter(bindSceneSizeToCanvas: Boolean) = new Adapter(bindSceneSizeToCanvas) {
override def createAdapter(bindSceneSizeToCanvas: Boolean, antiFreeze: Boolean) = new Adapter(bindSceneSizeToCanvas, antiFreeze) {
var n = 0
override def paintControl(event: PaintEvent) {
n += 1
Expand Down

0 comments on commit d60abbf

Please sign in to comment.