diff --git a/src/main/java/mpo/dayon/assistant/gui/Assistant.java b/src/main/java/mpo/dayon/assistant/gui/Assistant.java index 606f0b3f..2e5aa2e0 100644 --- a/src/main/java/mpo/dayon/assistant/gui/Assistant.java +++ b/src/main/java/mpo/dayon/assistant/gui/Assistant.java @@ -52,7 +52,6 @@ import static java.lang.Math.*; import static java.lang.String.format; import static java.lang.String.valueOf; -import static java.lang.Thread.sleep; import static javax.swing.SwingConstants.HORIZONTAL; import static mpo.dayon.common.babylon.Babylon.translate; import static mpo.dayon.common.gui.common.ImageUtilities.getOrCreateIcon; @@ -90,6 +89,8 @@ public class Assistant implements ClipboardOwner { private final Object prevBufferLOCK = new Object(); + private final Object upnpEnabledLOCK = new Object(); + private byte[] prevBuffer = null; private int prevWidth = -1; @@ -172,18 +173,6 @@ private void createCounters() { counters = new ArrayList<>(Arrays.asList(receivedBitCounter, receivedTileCounter, skippedTileCounter, mergedTileCounter, captureCompressionCounter)); } - public boolean isUpnpEnabled() { - while (upnpEnabled == null) { - try { - sleep(10L); - } catch (InterruptedException e) { - Log.warn("Swallowed", e); - Thread.currentThread().interrupt(); - } - } - return upnpEnabled; - } - public NetworkAssistantEngine getNetworkEngine() { return networkEngine; } @@ -764,12 +753,29 @@ protected void done() { } } + public boolean isUpnpEnabled() { + synchronized (upnpEnabledLOCK) { + while (upnpEnabled == null) { + try { + upnpEnabledLOCK.wait(); + } catch (InterruptedException e) { + Log.warn("Swallowed", e); + Thread.currentThread().interrupt(); + } + } + return upnpEnabled; + } + } + private void initUpnp() { - CompletableFuture.supplyAsync(UPnP::isUPnPAvailable).thenApply(enabled -> { - Log.info(format("UPnP is %s", enabled.booleanValue() ? "enabled" : "disabled")); - upnpEnabled = enabled; - return enabled; - }); + synchronized (upnpEnabledLOCK) { + CompletableFuture.supplyAsync(UPnP::isUPnPAvailable).thenApply(enabled -> { + Log.info(format("UPnP is %s", enabled.booleanValue() ? "enabled" : "disabled")); + upnpEnabled = enabled; + return enabled; + }); + upnpEnabledLOCK.notify(); + } } private class MyDeCompressorEngineListener implements DeCompressorEngineListener { @@ -782,7 +788,7 @@ public void onDeCompressed(Capture capture, int cacheHits, double compressionRat final AbstractMap.SimpleEntry image; // synchronized because of the reset onStarting() synchronized (prevBufferLOCK) { - image = captureEngineConfiguration.isCaptureColors() ? capture.createBufferedColoredImage(prevBuffer, prevWidth, prevHeight) : capture.createBufferedImage(prevBuffer, prevWidth, prevHeight); + image = capture.createBufferedImage(prevBuffer, prevWidth, prevHeight); prevBuffer = image.getValue(); prevWidth = image.getKey().getWidth(); prevHeight = image.getKey().getHeight(); @@ -860,7 +866,7 @@ public void onConnected(Socket connection, char osId, String inputLocale, int pe private void assureCompatibility(int peerMajorVersion) { if (!Version.isColoredVersion(peerMajorVersion) && (captureEngineConfiguration.isCaptureColors() || captureEngineConfiguration.getCaptureQuantization().getLevels() < Gray8Bits.X_32.getLevels())) { - Log.warn(format("Pre color version detected. CaptureEngineConfiguration will be adjusted to %s", Gray8Bits.X_128)); + Log.warn(format("Pre color v%d.x.x peer detected: CaptureEngineConfiguration adjusted to %s", peerMajorVersion, Gray8Bits.X_128)); captureEngineConfiguration = new CaptureEngineConfiguration(captureEngineConfiguration.getCaptureTick(), Gray8Bits.X_128, false); captureEngineConfiguration.persist(); } diff --git a/src/main/java/mpo/dayon/common/capture/Capture.java b/src/main/java/mpo/dayon/common/capture/Capture.java index 6034f180..869467ec 100644 --- a/src/main/java/mpo/dayon/common/capture/Capture.java +++ b/src/main/java/mpo/dayon/common/capture/Capture.java @@ -10,13 +10,14 @@ import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.util.AbstractMap; -import java.util.Arrays; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import mpo.dayon.common.buffer.MemByteBuffer; import mpo.dayon.common.log.Log; +import static java.util.Arrays.stream; + public class Capture { private final int id; @@ -91,7 +92,7 @@ public double computeCompressionRatio(int compressed) { * number of gray levels. */ private int computeInitialByteCount() { - return Arrays.stream(dirty).filter(Objects::nonNull).mapToInt(tile -> tile.getCapture().size()).sum(); + return stream(dirty).filter(Objects::nonNull).mapToInt(tile -> tile.getCapture().size()).sum(); } public int getWidth() { @@ -111,7 +112,7 @@ public int getTHeight() { } public int getDirtyTileCount() { - return (int) Arrays.stream(dirty).filter(Objects::nonNull).count(); + return (int) stream(dirty).filter(Objects::nonNull).count(); } public CaptureTile[] getDirtyTiles() { @@ -158,23 +159,34 @@ private void doMergeDirtyTiles(Capture older) { } /** - * Tile-rectangle buffer to screen-rectangle buffer. (monochromatic, 1 byte per pixel) + * Tile-rectangle buffer to screen-rectangle buffer. */ public AbstractMap.SimpleEntry createBufferedImage(byte[] prevBuffer, int prevWidth, int prevHeight) { + final boolean isGray = stream(dirty) + .parallel() + .filter(Objects::nonNull) + .anyMatch(tile -> tile.getCapture().size() == tile.getWidth() * tile.getHeight()); + return isGray ? createBufferedMonochromeImage(prevBuffer, prevWidth, prevHeight) : createBufferedColorImage(prevBuffer, prevWidth, prevHeight); + } + + /** + * Tile-rectangle buffer to screen-rectangle buffer. (monochromatic, 1 byte per pixel) + */ + private AbstractMap.SimpleEntry createBufferedMonochromeImage(byte[] prevBuffer, int prevWidth, int prevHeight) { final int capWidth = captureDimension.width; final int capHeight = captureDimension.height; final byte[] buffer = (prevBuffer != null && capWidth == prevWidth && capHeight == prevHeight && prevBuffer.length == capWidth * capHeight) ? prevBuffer : new byte[capWidth * capHeight]; - Arrays.stream(dirty) + stream(dirty) .parallel() .filter(Objects::nonNull) .forEach(tile -> { final MemByteBuffer src = tile.getCapture(); - final int tw = tile.getWidth(); - final int srcSize = tw * tile.getHeight(); - int destPos = tile.getY() * captureDimension.width + tile.getX(); - for (int srcPos = 0; srcPos < srcSize; srcPos += tw) { - System.arraycopy(src.getInternal(), srcPos, buffer, destPos, tw); - destPos += captureDimension.width; + final int tileWidth = tile.getWidth(); + final int srcSize = tileWidth * tile.getHeight(); + int destPos = tile.getY() * capWidth + tile.getX(); + for (int srcPos = 0; srcPos < srcSize; srcPos += tileWidth) { + System.arraycopy(src.getInternal(), srcPos, buffer, destPos, tileWidth); + destPos += capWidth; } }); @@ -187,26 +199,27 @@ public AbstractMap.SimpleEntry createBufferedImage(byte[] /** * Tile-rectangle buffer to screen-rectangle buffer. (color, 4 bytes per pixel) */ - public AbstractMap.SimpleEntry createBufferedColoredImage(byte[] prevBuffer, int prevWidth, int prevHeight) { + private AbstractMap.SimpleEntry createBufferedColorImage(byte[] prevBuffer, int prevWidth, int prevHeight) { final int capWidth = captureDimension.width; final int capHeight = captureDimension.height; final byte[] buffer = (prevBuffer != null && capWidth == prevWidth && capHeight == prevHeight && prevBuffer.length == capWidth * capHeight * 4) ? prevBuffer : new byte[capWidth * capHeight * 4]; - Arrays.stream(dirty) + final int capWidthByteSize = capWidth * 4; + stream(dirty) .parallel() .filter(Objects::nonNull) .forEach(tile -> { final MemByteBuffer src = tile.getCapture(); - final int srcSize = src.size(); - final int tileWidth = tile.getWidth(); - int destPos = tile.getY() * capWidth * 4 + tile.getX() * 4; - for (int srcPos = 0; srcPos < srcSize; srcPos += tileWidth * 4) { - System.arraycopy(src.getInternal(), srcPos, buffer, destPos, tileWidth * 4); - destPos += capWidth * 4; + final int tileWidthByteSize = tile.getWidth() * 4; + final int srcSize = tileWidthByteSize * tile.getHeight(); + int destPos = tile.getY() * capWidthByteSize + tile.getX() * 4; + for (int srcPos = 0; srcPos < srcSize; srcPos += tileWidthByteSize) { + System.arraycopy(src.getInternal(), srcPos, buffer, destPos, tileWidthByteSize); + destPos += capWidthByteSize; } }); final DataBufferByte dbuffer = new DataBufferByte(buffer, buffer.length); - final WritableRaster raster = Raster.createInterleavedRaster(dbuffer, capWidth, capHeight, capWidth * 4 , 4, new int[] { 1, 2, 3 }, null); + final WritableRaster raster = Raster.createInterleavedRaster(dbuffer, capWidth, capHeight, capWidthByteSize , 4, new int[] { 1, 2, 3 }, null); final ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); return new AbstractMap.SimpleEntry<>(new BufferedImage(cm, raster, false, null), buffer); }