This commit is contained in:
RetGal 2021-02-19 20:04:14 +01:00
parent 5794d4db00
commit f21793e432
6 changed files with 440 additions and 448 deletions

View File

@ -1,64 +1,63 @@
package mpo.dayon.assistant.gui; package mpo.dayon.assistant.gui;
import mpo.dayon.common.configuration.Configuration; import mpo.dayon.common.configuration.Configuration;
import mpo.dayon.common.preference.Preferences; import mpo.dayon.common.preference.Preferences;
import mpo.dayon.common.utils.SystemUtilities; import mpo.dayon.common.utils.SystemUtilities;
import static mpo.dayon.common.preference.Preferences.*; import static mpo.dayon.common.preference.Preferences.*;
public class AssistantConfiguration extends Configuration { public class AssistantConfiguration extends Configuration {
private static final String PREF_VERSION = "assistant.version"; private static final String PREF_VERSION = "assistant.version";
private static final String PREF_LOOK_AND_FEEL = "assistant.lookAndFeel"; private static final String PREF_LOOK_AND_FEEL = "assistant.lookAndFeel";
private final String lookAndFeelClassName; private final String lookAndFeelClassName;
/** /**
* Default : takes its values from the current preferences. * Default : takes its values from the current preferences.
* *
* @see mpo.dayon.common.preference.Preferences * @see mpo.dayon.common.preference.Preferences
*/ */
public AssistantConfiguration() { public AssistantConfiguration() {
final Preferences prefs = getPreferences(); final Preferences prefs = getPreferences();
// Note: did not exist in version = 0 => no migration is required. lookAndFeelClassName = prefs.getStringPreference(PREF_LOOK_AND_FEEL, SystemUtilities.getDefaultLookAndFeel());
lookAndFeelClassName = prefs.getStringPreference(PREF_LOOK_AND_FEEL, SystemUtilities.getDefaultLookAndFeel()); }
}
AssistantConfiguration(String lookAndFeelClassName) {
AssistantConfiguration(String lookAndFeelClassName) { this.lookAndFeelClassName = lookAndFeelClassName;
this.lookAndFeelClassName = lookAndFeelClassName; }
}
String getLookAndFeelClassName() {
String getLookAndFeelClassName() { return lookAndFeelClassName;
return lookAndFeelClassName; }
}
@Override
@Override public boolean equals(Object o) {
public boolean equals(Object o) { if (this == o) {
if (this == o) { return true;
return true; }
} if (o == null || getClass() != o.getClass()) {
if (o == null || getClass() != o.getClass()) { return false;
return false; }
} final AssistantConfiguration that = (AssistantConfiguration) o;
final AssistantConfiguration that = (AssistantConfiguration) o; return lookAndFeelClassName.equals(that.lookAndFeelClassName);
return lookAndFeelClassName.equals(that.lookAndFeelClassName); }
}
@Override
@Override public int hashCode() {
public int hashCode() { return lookAndFeelClassName.hashCode();
return lookAndFeelClassName.hashCode(); }
}
/**
/** * @param clear
* @param clear * allows for clearing properties from previous version
* allows for clearing properties from previous version */
*/ @Override
@Override protected void persist(boolean clear) {
protected void persist(boolean clear) { final Props props = new Props();
final Props props = new Props(); props.set(PREF_VERSION, String.valueOf(1));
props.set(PREF_VERSION, String.valueOf(1)); props.set(PREF_LOOK_AND_FEEL, lookAndFeelClassName);
props.set(PREF_LOOK_AND_FEEL, lookAndFeelClassName); getPreferences().update(props); // atomic (!)
getPreferences().update(props); // atomic (!) }
}
}
}

View File

@ -133,7 +133,6 @@ public class CaptureEngine implements ReConfigurable<CaptureEngineConfiguration>
++captureCount; ++captureCount;
++captureId; ++captureId;
@Nullable
final byte[] pixels = captureFactory.captureGray(quantization); final byte[] pixels = captureFactory.captureGray(quantization);
if (pixels == null) // testing purpose (!) if (pixels == null) // testing purpose (!)

View File

@ -1,15 +1,13 @@
package mpo.dayon.assisted.capture; package mpo.dayon.assisted.capture;
import org.jetbrains.annotations.Nullable; import mpo.dayon.common.capture.Gray8Bits;
import mpo.dayon.common.capture.Gray8Bits; import java.awt.*;
import java.awt.*; interface CaptureFactory {
interface CaptureFactory { Dimension getDimension();
Dimension getDimension(); byte[] captureGray(Gray8Bits quantization);
@Nullable byte[] captureGray(Gray8Bits quantization); }
}

View File

@ -83,14 +83,14 @@ public class NetworkAssistedEngine extends NetworkEngine
private void runReceivers() { private void runReceivers() {
this.receiver = new Thread(new RunnableEx() { this.receiver = new Thread(new RunnableEx() {
@Override @Override
protected void doRun() throws Exception { protected void doRun() {
NetworkAssistedEngine.this.receivingLoop(); NetworkAssistedEngine.this.receivingLoop();
} }
}, "CommandReceiver"); }, "CommandReceiver");
this.fileReceiver = new Thread(new RunnableEx() { this.fileReceiver = new Thread(new RunnableEx() {
@Override @Override
protected void doRun() throws Exception { protected void doRun() {
NetworkAssistedEngine.this.fileReceivingLoop(); NetworkAssistedEngine.this.fileReceivingLoop();
} }
}, "FileReceiver"); }, "FileReceiver");
@ -169,7 +169,7 @@ public class NetworkAssistedEngine extends NetworkEngine
} }
} }
private void receivingLoop() throws IOException { private void receivingLoop() {
try { try {
//noinspection InfiniteLoopStatement //noinspection InfiniteLoopStatement
@ -250,7 +250,7 @@ public class NetworkAssistedEngine extends NetworkEngine
cancelling.set(false); cancelling.set(false);
} }
private void fileReceivingLoop() throws IOException { private void fileReceivingLoop() {
NetworkClipboardFilesHelper filesHelper = new NetworkClipboardFilesHelper(); NetworkClipboardFilesHelper filesHelper = new NetworkClipboardFilesHelper();
String tmpDir = getTempDir(); String tmpDir = getTempDir();

View File

@ -1,154 +1,152 @@
package mpo.dayon.common.buffer; package mpo.dayon.common.buffer;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
import org.jetbrains.annotations.NotNull; /**
* A mixed between a byte buffer and a byte stream ...
/** */
* A mixed between a byte buffer and a byte stream ... public class MemByteBuffer extends OutputStream {
*/ private static final int DEFAULT_INITIAL_CAPACITY = 32;
public class MemByteBuffer extends OutputStream {
private static final int DEFAULT_INITIAL_CAPACITY = 32; private byte[] buffer;
private byte[] buffer; private int count;
private int count; public MemByteBuffer() {
this(DEFAULT_INITIAL_CAPACITY);
public MemByteBuffer() { }
this(DEFAULT_INITIAL_CAPACITY);
} private MemByteBuffer(int capacity) {
buffer = new byte[capacity];
private MemByteBuffer(int capacity) { }
buffer = new byte[capacity];
} /**
* @param data
/** * the newly created buffer is adopting that byte array (!)
* @param data */
* the newly created buffer is adopting that byte array (!) public MemByteBuffer(byte[] data) {
*/ buffer = data;
public MemByteBuffer(byte[] data) { count = data.length;
buffer = data; }
count = data.length;
} public int size() {
return count;
public int size() { }
return count;
} public byte[] getInternal() {
return buffer;
public byte[] getInternal() { }
return buffer;
} public int mark() {
return count;
public int mark() { }
return count;
} private void resetToMark(int mark) {
count = mark;
private void resetToMark(int mark) { }
count = mark;
} /**
* Writes the specified byte to this output stream. The general contract for
/** * <code>write</code> is that one byte is written to the output stream. The
* Writes the specified byte to this output stream. The general contract for * byte to be written is the eight low-order bits of the argument
* <code>write</code> is that one byte is written to the output stream. The * <code>b</code>. The 24 high-order bits of <code>b</code> are ignored.
* byte to be written is the eight low-order bits of the argument */
* <code>b</code>. The 24 high-order bits of <code>b</code> are ignored. @Override
*/ public void write(int val) {
@Override final int newcount = count + 1;
public void write(int val) {
final int newcount = count + 1; if (newcount > buffer.length) {
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
if (newcount > buffer.length) { }
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
} buffer[count++] = (byte) val;
}
buffer[count++] = (byte) val;
} /**
* @see #write(int)
/** */
* @see #write(int) private void write(int val1, int val2) {
*/ final int newcount = count + 2;
private void write(int val1, int val2) {
final int newcount = count + 2; if (newcount > buffer.length) {
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
if (newcount > buffer.length) { }
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
} buffer[count++] = (byte) val1;
buffer[count++] = (byte) val2;
buffer[count++] = (byte) val1; }
buffer[count++] = (byte) val2;
} @Override
public void write(byte[] buffer) {
@Override write(buffer, 0, buffer.length);
public void write(@NotNull byte[] buffer) { }
write(buffer, 0, buffer.length);
} @Override
public void write(byte[] buffer, int off, int len) {
@Override if (len == 0) {
public void write(@NotNull byte[] buffer, int off, int len) { return;
if (len == 0) { }
return;
} final int newcount = count + len;
final int newcount = count + len; if (newcount > this.buffer.length) {
this.buffer = Arrays.copyOf(this.buffer, Math.max(this.buffer.length << 1, newcount));
if (newcount > this.buffer.length) { }
this.buffer = Arrays.copyOf(this.buffer, Math.max(this.buffer.length << 1, newcount));
} System.arraycopy(buffer, off, this.buffer, count, len);
System.arraycopy(buffer, off, this.buffer, count, len); count = newcount;
}
count = newcount;
} /**
* Equivalent to the DataOutputStream version (!)
/** */
* Equivalent to the DataOutputStream version (!) public final void writeInt(int val) {
*/ write((val >>> 24) & 0xFF, (val >>> 16) & 0xFF);
public final void writeInt(int val) { write((val >>> 8) & 0xFF, val & 0xFF);
write((val >>> 24) & 0xFF, (val >>> 16) & 0xFF); }
write((val >>> 8) & 0xFF, val & 0xFF);
} /**
* Equivalent to the DataOutputStream version (!)
/** */
* Equivalent to the DataOutputStream version (!) public final void writeShort(int val) {
*/ write((val >>> 8) & 0xFF, val & 0xFF);
public final void writeShort(int val) { }
write((val >>> 8) & 0xFF, val & 0xFF);
} public void writeLenAsShort(int mark) {
final int end = mark();
public void writeLenAsShort(int mark) { final int len = end - mark - 2; // -2: the len (as short) itself (!)
final int end = mark();
final int len = end - mark - 2; // -2: the len (as short) itself (!) resetToMark(mark);
writeShort(-len);
resetToMark(mark);
writeShort(-len); resetToMark(end);
}
resetToMark(end);
} public void fill(int len, int val) {
final int newcount = count + len;
public void fill(int len, int val) {
final int newcount = count + len; if (newcount > buffer.length) {
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
if (newcount > buffer.length) { }
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
} for (int idx = count; idx < newcount; idx++) {
buffer[idx] = (byte) val;
for (int idx = count; idx < newcount; idx++) { }
buffer[idx] = (byte) val;
} count = newcount;
}
count = newcount;
} public void arraycopy(byte[] in, int start, int len) {
final int newcount = count + len;
public void arraycopy(byte[] in, int start, int len) {
final int newcount = count + len; if (newcount > buffer.length) {
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
if (newcount > buffer.length) { }
buffer = Arrays.copyOf(buffer, Math.max(buffer.length << 1, newcount));
} System.arraycopy(in, start, buffer, count, len);
System.arraycopy(in, start, buffer, count, len); count = newcount;
}
count = newcount; }
}
}

View File

@ -1,210 +1,208 @@
package mpo.dayon.common.capture; package mpo.dayon.common.capture;
import java.awt.*; import java.awt.*;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel; import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer; import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte; import java.awt.image.DataBufferByte;
import java.awt.image.Raster; import java.awt.image.Raster;
import java.awt.image.WritableRaster; import java.awt.image.WritableRaster;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.Nullable; import mpo.dayon.common.buffer.MemByteBuffer;
import mpo.dayon.common.log.Log;
import mpo.dayon.common.buffer.MemByteBuffer;
import mpo.dayon.common.log.Log; public class Capture {
private final int id;
public class Capture {
private final int id; private final boolean reset;
private final boolean reset; /**
* @see #mergeDirtyTiles(Capture[])
/** */
* @see #mergeDirtyTiles(Capture[]) private final AtomicInteger skipped;
*/
private final AtomicInteger skipped; /**
* @see #mergeDirtyTiles(Capture[])
/** */
* @see #mergeDirtyTiles(Capture[]) private final AtomicInteger merged;
*/
private final AtomicInteger merged; private final Dimension captureDimension;
private final Dimension captureDimension; private final Dimension tileDimension;
private final Dimension tileDimension; private final CaptureTile[] dirty;
private final CaptureTile[] dirty; public Capture(int captureId, boolean reset, int skipped, int merged, Dimension captureDimension, Dimension tileDimension, CaptureTile[] dirty) {
this.id = captureId;
public Capture(int captureId, boolean reset, int skipped, int merged, Dimension captureDimension, Dimension tileDimension, CaptureTile[] dirty) { this.reset = reset;
this.id = captureId;
this.reset = reset; this.skipped = new AtomicInteger(skipped);
this.merged = new AtomicInteger(merged);
this.skipped = new AtomicInteger(skipped);
this.merged = new AtomicInteger(merged); this.captureDimension = captureDimension;
this.tileDimension = tileDimension;
this.captureDimension = captureDimension;
this.tileDimension = tileDimension; this.dirty = dirty;
}
this.dirty = dirty;
} public int getId() {
return id;
public int getId() { }
return id;
} public boolean isReset() {
return reset;
public boolean isReset() { }
return reset;
} public int getSkipped() {
return skipped.get();
public int getSkipped() { }
return skipped.get();
} public int getMerged() {
return merged.get();
public int getMerged() { }
return merged.get();
} /**
* @see #computeInitialByteCount()
/** */
* @see #computeInitialByteCount() public double computeCompressionRatio(int compressed) {
*/ return computeInitialByteCount() / (double) compressed;
public double computeCompressionRatio(int compressed) { }
return computeInitialByteCount() / (double) compressed;
} /**
* Based on the gray level data (not the RGB capture).
/** * <p/>
* Based on the gray level data (not the RGB capture). * That's the actual payload size of a capture to paint it on the screen -
* <p/> * that's the only amount of data I would need to send to the assistant over
* That's the actual payload size of a capture to paint it on the screen - * the network. But I've to send some extra-info for the location, size,
* that's the only amount of data I would need to send to the assistant over * etc... as well as some un-marshalling extra-stuff (e.g., len). Then, I'm
* the network. But I've to send some extra-info for the location, size, * going to encode and compress all that data and I want to compute on the
* etc... as well as some un-marshalling extra-stuff (e.g., len). Then, I'm * assistant side the actual compression ratio compared to that initial
* going to encode and compress all that data and I want to compute on the * amount of byte.
* assistant side the actual compression ratio compared to that initial * <p/>
* amount of byte. * Note that the actual number of gray levels does not change that original
* <p/> * amount as I want to see the impact on the compression of using less
* Note that the actual number of gray levels does not change that original * number of gray levels.
* amount as I want to see the impact on the compression of using less */
* number of gray levels. private int computeInitialByteCount() {
*/
private int computeInitialByteCount() { return Arrays.stream(dirty).filter(Objects::nonNull).mapToInt(tile -> tile.getCapture().size()).sum();
}
return Arrays.stream(dirty).filter(Objects::nonNull).mapToInt(tile -> tile.getCapture().size()).sum();
} public int getWidth() {
return captureDimension.width;
public int getWidth() { }
return captureDimension.width;
} public int getHeight() {
return captureDimension.height;
public int getHeight() { }
return captureDimension.height;
} public int getTWidth() {
return tileDimension.width;
public int getTWidth() { }
return tileDimension.width;
} public int getTHeight() {
return tileDimension.height;
public int getTHeight() { }
return tileDimension.height;
} public int getDirtyTileCount() {
public int getDirtyTileCount() { return (int) Arrays.stream(dirty).filter(Objects::nonNull).count();
}
return (int) Arrays.stream(dirty).filter(Objects::nonNull).count();
} public CaptureTile[] getDirtyTiles() {
return dirty;
public CaptureTile[] getDirtyTiles() { }
return dirty;
} public void mergeDirtyTiles(Capture[] olders) {
int xskipped = 0;
public void mergeDirtyTiles(Capture[] olders) { int xmerged = 0;
int xskipped = 0;
int xmerged = 0; for (final Capture older : olders) {
doMergeDirtyTiles(older);
for (final Capture older : olders) {
doMergeDirtyTiles(older); xskipped += older.skipped.get();
xmerged += older.merged.get();
xskipped += older.skipped.get(); }
xmerged += older.merged.get();
} skipped.addAndGet(xskipped);
merged.set(1 + xmerged);
skipped.addAndGet(xskipped);
merged.set(1 + xmerged); Log.warn(String.format("Merged [id:%d] [count:%d] [skipped:%d][merged:%d]", id, olders.length, skipped.get(), merged.get()));
}
Log.warn(String.format("Merged [id:%d] [count:%d] [skipped:%d][merged:%d]", id, olders.length, skipped.get(), merged.get()));
} /**
* <pre>
/** * [ this ] [+] [ older ]
* <pre> * x - : this.tile = this.tile
* [ this ] [+] [ older ] * x x : this.tile = this.tile
* x - : this.tile = this.tile * - x : this.tile = older.tile
* x x : this.tile = this.tile * </pre>
* - x : this.tile = older.tile */
* </pre> private void doMergeDirtyTiles(Capture older) {
*/ // The only way the tile 'length' may change is when the capture engine
private void doMergeDirtyTiles(Capture older) { // has been re-configured.
// The only way the tile 'length' may change is when the capture engine // In that case (for the sake of simplicity) a FULL capture will be
// has been re-configured. // sent.
// In that case (for the sake of simplicity) a FULL capture will be
// sent. if (dirty.length != older.dirty.length) {
return; // we're keeping the newest (FULL capture anyway)
if (dirty.length != older.dirty.length) { }
return; // we're keeping the newest (FULL capture anyway)
} for (int idx = 0; idx < dirty.length; idx++) {
final CaptureTile thisTile = dirty[idx];
for (int idx = 0; idx < dirty.length; idx++) { final CaptureTile olderTile = older.dirty[idx];
final CaptureTile thisTile = dirty[idx];
final CaptureTile olderTile = older.dirty[idx]; if (olderTile != null && thisTile == null) {
dirty[idx] = olderTile;
if (olderTile != null && thisTile == null) { }
dirty[idx] = olderTile; }
} }
}
} /**
* Tile-rectangle buffer to screen-rectangle buffer.
/** */
* Tile-rectangle buffer to screen-rectangle buffer. public AbstractMap.SimpleEntry<BufferedImage, byte[]> createBufferedImage(byte[] prevBuffer, int prevWidth, int prevHeight) {
*/ final byte[] buffer = new byte[captureDimension.width * captureDimension.height];
public AbstractMap.SimpleEntry<BufferedImage, byte[]> createBufferedImage(@Nullable byte[] prevBuffer, int prevWidth, int prevHeight) {
final byte[] buffer = new byte[captureDimension.width * captureDimension.height]; if (prevBuffer != null && captureDimension.width == prevWidth && captureDimension.height == prevHeight) {
System.arraycopy(prevBuffer, 0, buffer, 0, buffer.length);
if (prevBuffer != null && captureDimension.width == prevWidth && captureDimension.height == prevHeight) { }
System.arraycopy(prevBuffer, 0, buffer, 0, buffer.length);
} for (final CaptureTile tile : dirty) {
if (tile != null) {
for (final CaptureTile tile : dirty) { final MemByteBuffer src = tile.getCapture();
if (tile != null) { final int srcSize = src.size();
final MemByteBuffer src = tile.getCapture();
final int srcSize = src.size(); final int tw = tile.getWidth();
final int tw = tile.getWidth(); int srcPos = 0;
int destPos = tile.getY() * captureDimension.width + tile.getX();
int srcPos = 0;
int destPos = tile.getY() * captureDimension.width + tile.getX(); while (srcPos < srcSize) {
System.arraycopy(src.getInternal(), srcPos, buffer, destPos, tw);
while (srcPos < srcSize) {
System.arraycopy(src.getInternal(), srcPos, buffer, destPos, tw); srcPos += tw;
destPos += captureDimension.width;
srcPos += tw; }
destPos += captureDimension.width; }
} }
}
} final DataBuffer dbuffer = new DataBufferByte(buffer, buffer.length);
final DataBuffer dbuffer = new DataBufferByte(buffer, buffer.length); final WritableRaster raster = Raster.createInterleavedRaster(dbuffer, captureDimension.width, captureDimension.height, captureDimension.width, // scanlineStride
1, // pixelStride
final WritableRaster raster = Raster.createInterleavedRaster(dbuffer, captureDimension.width, captureDimension.height, captureDimension.width, // scanlineStride new int[] { 0 }, // bandOffsets
1, // pixelStride null);
new int[] { 0 }, // bandOffsets
null); final ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] { 8 }, false, false, Transparency.OPAQUE,
DataBuffer.TYPE_BYTE);
final ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] { 8 }, false, false, Transparency.OPAQUE,
DataBuffer.TYPE_BYTE); return new AbstractMap.SimpleEntry<>(new BufferedImage(cm, raster, false, null), buffer);
}
return new AbstractMap.SimpleEntry<>(new BufferedImage(cm, raster, false, null), buffer); }
}
}