Intial commit

master
Thor 4 years ago
commit 74627c9c7a
  1. 14
      .classpath
  2. 8
      .gitignore
  3. 42
      build.xml
  4. BIN
      lib/TableLayout-bin-jdk1.5-2009-08-26.jar
  5. BIN
      lib/jspeex.jar
  6. BIN
      lib/jtablet-2010-04-13-1.jar
  7. BIN
      lib/jtablet2-thin-2010-04-13-1.jar
  8. BIN
      lib/proguard.jar
  9. 2
      lib/proguard_task.properties
  10. BIN
      sketcher2-client-20120527-1225.jar
  11. 1215
      sketcher2-client-20120527-1225.map
  12. BIN
      sketcher2-client-resources-20120527-1225.jar
  13. BIN
      sketcher2-server-20120527-1225.jar
  14. 626
      src/com/jotuntech/sketcher/client/Client.java
  15. 54
      src/com/jotuntech/sketcher/client/ClientApplet.java
  16. 8
      src/com/jotuntech/sketcher/client/Command.java
  17. 24
      src/com/jotuntech/sketcher/client/CommandEntry.java
  18. 113
      src/com/jotuntech/sketcher/client/Connection.java
  19. 1278
      src/com/jotuntech/sketcher/client/Editor.java
  20. 154
      src/com/jotuntech/sketcher/client/JCollapsiblePanel.java
  21. 7
      src/com/jotuntech/sketcher/client/JCollapsiblePanelGroup.java
  22. 74
      src/com/jotuntech/sketcher/client/JUserEntry.java
  23. 57
      src/com/jotuntech/sketcher/client/JUserList.java
  24. 81
      src/com/jotuntech/sketcher/client/LookAndFeel.java
  25. 568
      src/com/jotuntech/sketcher/client/PSDEncoder.java
  26. 141
      src/com/jotuntech/sketcher/client/PackBitsOutputStream.java
  27. 150
      src/com/jotuntech/sketcher/client/Smoother.java
  28. 1723
      src/com/jotuntech/sketcher/client/UserInterface.java
  29. BIN
      src/com/jotuntech/sketcher/client/audio/chat.wav
  30. BIN
      src/com/jotuntech/sketcher/client/audio/intro.wav
  31. BIN
      src/com/jotuntech/sketcher/client/audio/kick.wav
  32. BIN
      src/com/jotuntech/sketcher/client/audio/outtro.wav
  33. BIN
      src/com/jotuntech/sketcher/client/audio/query.wav
  34. BIN
      src/com/jotuntech/sketcher/client/audio/signin.wav
  35. BIN
      src/com/jotuntech/sketcher/client/audio/signout.wav
  36. 56
      src/com/jotuntech/sketcher/client/command/CanvasCommand.java
  37. 56
      src/com/jotuntech/sketcher/client/command/CreateLayerCommand.java
  38. 56
      src/com/jotuntech/sketcher/client/command/CursorCommand.java
  39. 74
      src/com/jotuntech/sketcher/client/command/DeleteLayerCommand.java
  40. 82
      src/com/jotuntech/sketcher/client/command/FilterCommand.java
  41. 58
      src/com/jotuntech/sketcher/client/command/KickCommand.java
  42. 66
      src/com/jotuntech/sketcher/client/command/LayerDataCommand.java
  43. 73
      src/com/jotuntech/sketcher/client/command/LineCommand.java
  44. 45
      src/com/jotuntech/sketcher/client/command/MergeCommand.java
  45. 68
      src/com/jotuntech/sketcher/client/command/MoveCommand.java
  46. 32
      src/com/jotuntech/sketcher/client/command/PingCommand.java
  47. 62
      src/com/jotuntech/sketcher/client/command/SayCommand.java
  48. 41
      src/com/jotuntech/sketcher/client/command/ServerMessageCommand.java
  49. 57
      src/com/jotuntech/sketcher/client/command/SetBrushCommand.java
  50. 53
      src/com/jotuntech/sketcher/client/command/SetColorCommand.java
  51. 64
      src/com/jotuntech/sketcher/client/command/SetLayerCommand.java
  52. 96
      src/com/jotuntech/sketcher/client/command/SignInCommand.java
  53. 49
      src/com/jotuntech/sketcher/client/command/SignOutCommand.java
  54. 35
      src/com/jotuntech/sketcher/client/command/UndoCommand.java
  55. 45
      src/com/jotuntech/sketcher/client/command/UndoDataCommand.java
  56. 46
      src/com/jotuntech/sketcher/client/command/UndoEntryCommand.java
  57. 37
      src/com/jotuntech/sketcher/client/command/VoiceCommand.java
  58. BIN
      src/com/jotuntech/sketcher/client/images/about.png
  59. BIN
      src/com/jotuntech/sketcher/client/images/ad1.png
  60. BIN
      src/com/jotuntech/sketcher/client/images/bezier.png
  61. BIN
      src/com/jotuntech/sketcher/client/images/blue.png
  62. BIN
      src/com/jotuntech/sketcher/client/images/brightness.png
  63. BIN
      src/com/jotuntech/sketcher/client/images/brush.png
  64. BIN
      src/com/jotuntech/sketcher/client/images/cmy.png
  65. BIN
      src/com/jotuntech/sketcher/client/images/color.png
  66. BIN
      src/com/jotuntech/sketcher/client/images/crosshair.gif
  67. BIN
      src/com/jotuntech/sketcher/client/images/cyan.png
  68. BIN
      src/com/jotuntech/sketcher/client/images/dummybanner.png
  69. BIN
      src/com/jotuntech/sketcher/client/images/eraser.png
  70. BIN
      src/com/jotuntech/sketcher/client/images/eyedropper.gif
  71. BIN
      src/com/jotuntech/sketcher/client/images/flow.png
  72. BIN
      src/com/jotuntech/sketcher/client/images/freehand.png
  73. BIN
      src/com/jotuntech/sketcher/client/images/green.png
  74. BIN
      src/com/jotuntech/sketcher/client/images/hand.gif
  75. BIN
      src/com/jotuntech/sketcher/client/images/hardness.png
  76. BIN
      src/com/jotuntech/sketcher/client/images/hsb.png
  77. BIN
      src/com/jotuntech/sketcher/client/images/hue.png
  78. BIN
      src/com/jotuntech/sketcher/client/images/ink.png
  79. BIN
      src/com/jotuntech/sketcher/client/images/jitter.png
  80. BIN
      src/com/jotuntech/sketcher/client/images/layers.png
  81. BIN
      src/com/jotuntech/sketcher/client/images/line.png
  82. BIN
      src/com/jotuntech/sketcher/client/images/madeinsketcher.png
  83. BIN
      src/com/jotuntech/sketcher/client/images/magenta.png
  84. BIN
      src/com/jotuntech/sketcher/client/images/move.png
  85. BIN
      src/com/jotuntech/sketcher/client/images/noise.png
  86. BIN
      src/com/jotuntech/sketcher/client/images/opacity.png
  87. BIN
      src/com/jotuntech/sketcher/client/images/oval.png
  88. BIN
      src/com/jotuntech/sketcher/client/images/pen.png
  89. BIN
      src/com/jotuntech/sketcher/client/images/pencil.png
  90. BIN
      src/com/jotuntech/sketcher/client/images/pin_normal.png
  91. BIN
      src/com/jotuntech/sketcher/client/images/pin_stuck.png
  92. BIN
      src/com/jotuntech/sketcher/client/images/rectangle.png
  93. BIN
      src/com/jotuntech/sketcher/client/images/red.png
  94. BIN
      src/com/jotuntech/sketcher/client/images/rgb.png
  95. BIN
      src/com/jotuntech/sketcher/client/images/saturation.png
  96. BIN
      src/com/jotuntech/sketcher/client/images/select.png
  97. BIN
      src/com/jotuntech/sketcher/client/images/size.png
  98. BIN
      src/com/jotuntech/sketcher/client/images/sketcherlogo.png
  99. BIN
      src/com/jotuntech/sketcher/client/images/smoothing.png
  100. BIN
      src/com/jotuntech/sketcher/client/images/spacing.png
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/TableLayout-bin-jdk1.5-2009-08-26.jar"/>
<classpathentry kind="lib" path="lib/jspeex.jar">
<attributes>
<attribute name="javadoc_location" value="http://jspeex.sourceforge.net/doc/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/jtablet-2010-04-13-1.jar"/>
<classpathentry kind="lib" path="lib/jtablet2-thin-2010-04-13-1.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

8
.gitignore vendored

@ -0,0 +1,8 @@
**/.DS_Store
**/*.p12
.settings
.project
bin/
obfuscate-sketcher1.xml

@ -0,0 +1,42 @@
<project name="Sketcher2" default="dist" basedir=".">
<taskdef resource="proguard_task.properties" classpath="lib:lib/proguard.jar" />
<target name="dist">
<tstamp/>
<jar destfile="sketcher2-server-${DSTAMP}-${TSTAMP}.jar" basedir="bin" includes="com/jotuntech/sketcher/common/**,com/jotuntech/sketcher/server/**"/>
<jar destfile="sketcher2-client-internal-${DSTAMP}-${TSTAMP}.jar" basedir="bin" includes="com/jotuntech/sketcher/common/**,com/jotuntech/sketcher/client/**" excludes="com/jotuntech/sketcher/client/audio/**,com/jotuntech/sketcher/client/images/**"/>
<jar destfile="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" basedir="bin" includes="com/jotuntech/sketcher/client/audio/**,com/jotuntech/sketcher/client/images/**"/>
<proguard>
-libraryjars "${java.home}/lib/rt.jar"
-libraryjars lib/
-injars sketcher2-client-internal-${DSTAMP}-${TSTAMP}.jar
-outjars sketcher2-client-${DSTAMP}-${TSTAMP}.jar
-printmapping sketcher2-client-${DSTAMP}-${TSTAMP}.map
-keep public class * extends java.applet.Applet
-keep public class * extends com.jotuntech.sketcher.client.Command
-keep public class * extends com.jotuntech.sketcher.common.filter.Filter
</proguard>
<property name="certificate.keystore" location="jotun.p12"/>
<property name="certificate.storetype" value="pkcs12"/>
<property name="certificate.alias" value="le-20ab1c70-a64e-486b-ac42-a0f8ac0941f9"/>
<property name="certificate.password" value="rweysgib"/>
<signjar jar="sketcher2-client-${DSTAMP}-${TSTAMP}.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<!--
<signjar jar="lib/jtablet2-thin-2010-04-13-1.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/jtablet-2010-04-13-1.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/jspeex.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/mail.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/TableLayout-bin-jdk1.5-2009-08-26.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
-->
<delete file="sketcher2-client-internal-${DSTAMP}-${TSTAMP}.jar"/>
<copy file="sketcher2-client-${DSTAMP}-${TSTAMP}.jar" tofile="../ArtGrounds2/resources/sketcher2-client.jar"/>
<copy file="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" tofile="../ArtGrounds2/resources/sketcher2-client-resources.jar"/>
<copy file="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" tofile="sketcher2-client-resources.jar"/>
<copy file="sketcher2-server-${DSTAMP}-${TSTAMP}.jar" tofile="../ArtGrounds2/lib/sketcher2-server.jar"/>
<copy file="sketcher2-server-${DSTAMP}-${TSTAMP}.jar" tofile="../JotunWebsite/lib/sketcher2-server.jar"/>
</target>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,2 @@
proguard = proguard.ant.ProGuardTask
proguardconfiguration = proguard.ant.ConfigurationTask

File diff suppressed because it is too large Load Diff

@ -0,0 +1,626 @@
package com.jotuntech.sketcher.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.jotuntech.sketcher.client.command.PingCommand;
import com.jotuntech.sketcher.client.command.SignInCommand;
import com.jotuntech.sketcher.client.command.SignOutCommand;
import com.jotuntech.sketcher.client.voice.VoiceClient;
import com.jotuntech.sketcher.client.voice.VoiceEvent;
import com.jotuntech.sketcher.client.voice.VoiceListener;
import com.jotuntech.sketcher.common.Brush;
import com.jotuntech.sketcher.common.Canvas;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.Log;
import com.jotuntech.sketcher.common.StreamableUtils;
import com.jotuntech.sketcher.common.TwoWayHashMap;
import com.jotuntech.sketcher.common.User;
/**
* @author Thor Harald Johansen
*
*/
public class Client extends Thread {
private UserInterface userInterface;
private TwoWayHashMap<Integer, User> userMap;
private User[] userArray;
private Canvas canvas;
private long lastLayerClean;
private Connection connection;
private BlockingQueue<CommandEntry> commandQueue;
private ByteBuffer commandBuffer;
private String login;
private String password;
private String hostname;
private int port;
private VoiceClient voiceClient;
private boolean soundEnabled;
private boolean ads;
//private Random sendRandom = new Random(0x707733360A596E0AL);
//private Random recvRandom = new Random(0x59A70A401C8CD1EAL);
private Properties props;
private File propsFile;
Timer propsTimer = new Timer("PropsTimer", true);
private Brush defaultBrushes[] = new Brush[] {
new Brush("Pen", 255, 255, 3.375f, 1, 0.27f, true, false, true, 0, 0, 0, false, 0.5f),
new Brush("Pencil", 255, 255, 2f, 2, 0.45f, true, false, true, 1.5f, 0, 0, false, 0.5f),
new Brush("Water", 255, 255, 16f, 4, 0.22f, true, false, true, 0, 0, 128, false, 0.5f),
new Brush("Eraser", -255, 192, 16f, 4, 0.2f, false, true, false, 0, 0, 0, false, 0.5f),
new Brush("Ink", 255, 255, 4f, 1, 0.04f, false, false, true, 0, 0, 0, false, 0.5f),
new Brush("Wipe", -255, 255, 64f, 7, 0.2f, false, false, false, 0, 0, 0, false, 0.5f)
};
protected Brush[] brushes = new Brush[defaultBrushes.length];
public Client(String hostname, int port, String login, String password, boolean ads) {
super("ClientThread");
/** Save hostname and port */
this.hostname = hostname;
this.port = port;
/** Save login and password */
this.login = login;
this.password = password;
/** Save ad setting */
this.ads = ads;
/** Create user map & array */
userMap = new TwoWayHashMap<Integer, User>();
userMap.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
/** Populate user array in a thread-safe way */
User[] newUserArray = new User[0];
newUserArray = userMap.values().toArray(newUserArray);
userArray = newUserArray;
}
});
/** Create user array */
userArray = new User[0];
/** Create command queue */
commandQueue = new ArrayBlockingQueue<CommandEntry>(65536, true);
for(int i = 0; i < brushes.length; i++) {
brushes[i] = defaultBrushes[i].copy();
}
/** Create user interface */
userInterface = new UserInterface(this);
/** Load properties file */
try {
propsFile = new File(System.getProperty("user.home"), "sketcher.properties");
} catch(SecurityException e) {
propsFile = null;
}
loadProps();
/** Set last layer cleanup time */
lastLayerClean = System.currentTimeMillis();
propsTimer.schedule(new TimerTask() {
public void run() {
saveProps();
}
}, 60000, 60000);
}
synchronized public void resetProps() {
if(propsFile != null) {
propsFile.delete();
}
loadProps();
}
synchronized private void loadProps() {
props = new Properties();
if(propsFile != null) {
try {
if(propsFile.exists()) {
FileInputStream is = new FileInputStream(propsFile);
props.load(is);
is.close();
}
} catch(IOException e) {
} catch(SecurityException e) {
}
}
int propsVersion = Integer.valueOf(props.getProperty("file.version", "0"));
soundEnabled = "true".equals(props.getProperty("sound.enabled", "true"));
userInterface.getSoundItem().setSelected(soundEnabled);
userInterface.setSmoothZoom("true".equals(props.getProperty("zoom.smooth", "true")));
userInterface.getEditor().setTagsEnabled("true".equals(props.getProperty("tags.enabled", "true")));
userInterface.getEditor().setSoftwareCursorEnabled("true".equals(props.getProperty("cursor.software", "false")));
userInterface.getTagsItem().setSelected(userInterface.getEditor().isTagsEnabled());
for(JCollapsiblePanel panel : userInterface.getToolPanels()) {
panel.setExpanded("true".equals(props.getProperty("panel" + panel.getId() + ".expanded", panel.isExpandedByDefault() ? "true" : "false")), false);
panel.setStuck("true".equals(props.getProperty("panel" + panel.getId() + ".stuck", panel.isStuckByDefault() ? "true" : "false")));
}
for(int i = 0; i < brushes.length; i++) {
brushes[i] = defaultBrushes[i].copy();
}
for(int i = 0; i < defaultBrushes.length; i++) {
String name = props.getProperty("brush" + i + ".name");
String opacity = props.getProperty("brush" + i + ".opacity");
String flow = props.getProperty("brush" + i + ".flow");
String radius = props.getProperty("brush" + i + ".radius");
String hardness = props.getProperty("brush" + i + ".hardness");
String spacing = props.getProperty("brush" + i + ".spacing");
String pressureToFlow, pressureToOpacity;
if(propsVersion < 1) {
pressureToFlow = props.getProperty("brush" + i + ".pressureToOpacity");
pressureToOpacity = props.getProperty("brush" + i + ".realPressureToOpacity");
} else {
pressureToFlow = props.getProperty("brush" + i + ".pressureToFlow");
pressureToOpacity = props.getProperty("brush" + i + ".pressureToOpacity");
}
String pressureToRadius = props.getProperty("brush" + i + ".pressureToRadius");
String jitter = props.getProperty("brush" + i + ".jitter");
String water = props.getProperty("brush" + i + ".water");
String waterArea = props.getProperty("brush" + i + ".waterArea");
String lockTransparency = props.getProperty("brush" + i + ".lockTransparency");
Brush b = brushes[i];
if(name != null) {
b.setName(name);
}
if(opacity != null) {
b.setOpacity(Integer.valueOf(opacity));
}
if(flow != null) {
b.setFlow(Integer.valueOf(flow));
}
if(radius != null) {
b.setRadius(Float.valueOf(radius));
}
if(hardness != null) {
b.setHardness(Integer.valueOf(hardness));
}
if(spacing != null) {
b.setSpacing(Float.valueOf(spacing));
}
if(pressureToOpacity != null) {
b.setPressureToOpacity("true".equals(pressureToOpacity));
}
if(pressureToFlow != null) {
b.setPressureToFlow("true".equals(pressureToFlow));
}
if(pressureToRadius != null) {
b.setPressureToRadius("true".equals(pressureToRadius));
}
if(jitter != null) {
b.setJitter(Float.valueOf(jitter));
}
if(water != null) {
b.setWater(Integer.valueOf(water));
}
if(waterArea != null) {
b.setWaterArea(Float.valueOf(waterArea));
}
if(lockTransparency != null) {
b.setLockTransparency("true".equals(lockTransparency));
}
}
}
synchronized private void saveProps() {
if(propsFile == null) {
return;
}
props.setProperty("file.version", "1"); // Increase this when making format changes!
props.setProperty("sound.enabled", soundEnabled ? "true" : "false");
props.setProperty("zoom.smooth", userInterface.isSmoothZoom() ? "true" : "false");
props.setProperty("tags.enabled", userInterface.getEditor().isTagsEnabled() ? "true" : "false");
props.setProperty("cursor.software", userInterface.getEditor().isSoftwareCursorEnabled() ? "true" : "false");
for(JCollapsiblePanel panel : userInterface.getToolPanels()) {
props.setProperty("panel" + panel.getId() + ".expanded", panel.isExpanded() ? "true" : "false");
props.setProperty("panel" + panel.getId() + ".stuck", panel.isStuck() ? "true" : "false");
}
for(int i = 0; i < brushes.length; i++) {
props.setProperty("brush" + i + ".name", brushes[i].getName());
props.setProperty("brush" + i + ".opacity", String.valueOf(brushes[i].getOpacity()));
props.setProperty("brush" + i + ".flow", String.valueOf(brushes[i].getFlow()));
props.setProperty("brush" + i + ".radius", String.valueOf(brushes[i].getRadius()));
props.setProperty("brush" + i + ".hardness", String.valueOf(brushes[i].getHardness()));
props.setProperty("brush" + i + ".spacing", String.valueOf(brushes[i].getSpacing()));
props.setProperty("brush" + i + ".pressureToOpacity", brushes[i].isPressureToOpacity() ? "true" : "false");
props.setProperty("brush" + i + ".pressureToFlow", brushes[i].isPressureToFlow() ? "true" : "false");
props.setProperty("brush" + i + ".pressureToRadius", brushes[i].isPressureToRadius() ? "true" : "false");
props.setProperty("brush" + i + ".jitter", String.valueOf(brushes[i].getJitter()));
props.setProperty("brush" + i + ".noise", String.valueOf(brushes[i].getNoise()));
props.setProperty("brush" + i + ".water", String.valueOf(brushes[i].getWater()));
props.setProperty("brush" + i + ".waterArea", String.valueOf(brushes[i].getWaterArea()));
props.setProperty("brush" + i + ".lockTransparency", brushes[i].isLockTransparency() ? "true" : "false");
}
try {
FileOutputStream os = new FileOutputStream(propsFile);
props.store(os, "Sketcher Properties File");
os.close();
} catch(IOException e) {
} catch(SecurityException e) {
propsTimer.cancel();
}
}
public void close(String message) {
propsTimer.cancel();
saveProps();
/** Empty the send queue */
connection.getSendQueue().clear();
/** Send sign-out command */
connection.getSendQueue().offer(new CommandEntry(0, new SignOutCommand(message)));
/** Schedule connection death */
connection.setTimeOfDeath(System.currentTimeMillis() + 1000);
/** Play sound */
if("true".equals(props.getProperty("sound.enabled", "true"))) {
UserInterface.AUDIO_OUTTRO.play();
}
}
public TwoWayHashMap<Integer, User> getUserMap() {
return userMap;
}
public User[] getUserArray() {
return userArray;
}
public Canvas getCanvas() {
return canvas;
}
public void setCanvas(Canvas canvas) {
this.canvas = canvas;
userInterface.setCanvas(canvas);
}
public UserInterface getUserInterface() {
return userInterface;
}
public Connection getConnection() {
return connection;
}
public void run() {
try {
/** Create client connection */
connection = new Connection(SocketChannel.open());
userInterface.println("Connecting... ");
/** Enable blocking for connect */
connection.getChannel().configureBlocking(true);
/** Attempt to connect to server */
connection.getChannel().connect(new InetSocketAddress(hostname, port));
/** Disable blocking after connect */
connection.getChannel().configureBlocking(false);
userInterface.print("Success!");
/** Play audio */
if("true".equals(props.getProperty("sound.enabled", "true"))) {
UserInterface.AUDIO_INTRO.play();
}
/** Create command buffer */
commandBuffer = ByteBuffer.allocate(65538);
/** Send sign-in command */
connection.getSendQueue().offer(new CommandEntry(0, new SignInCommand(0, login, password, false)));
/** Set last ping time */
connection.setLastPing(0);
while(!Thread.interrupted()) {
if(!connection.getChannel().isConnected()) {
userInterface.println("Disconnected (Connection was closed)");
break;
} else if(connection.isTimeOfDeath()) {
try { connection.getChannel().close(); } catch(IOException e) { }
userInterface.println("Disconnected (Exit)");
break;
}
if(System.currentTimeMillis() - connection.getLastPing() >= Connection.PING_INTERVAL) {
long timestamp = System.currentTimeMillis();
connection.getSendQueue().offer(new CommandEntry(0, new PingCommand(timestamp)));
connection.setLastPing(timestamp);
}
for(CommandEntry e = commandQueue.poll(20, TimeUnit.MILLISECONDS); e != null; e = commandQueue.poll(20, TimeUnit.MILLISECONDS)) {
if(e.getCommand().perform(this, e.getSourceKey() == 0 ? connection.getUser() : userMap.get(e.getSourceKey())) == Connection.SEND_OTHERS) {
connection.getSendQueue().offer(new CommandEntry(e.getSourceKey(), e.getCommand()));
}
}
if(canvas != null && lastLayerClean + 30000 < System.currentTimeMillis()) {
lastLayerClean = System.currentTimeMillis();
for(Layer e : canvas.getLayerMap().values()) {
e.clean();
}
}
ByteBuffer inputBuffer = connection.getInputBuffer();
ByteBuffer outputBuffer = connection.getOutputBuffer();
ArrayBlockingQueue<CommandEntry> sendQueue = connection.getSendQueue();
//int startPosition = outputBuffer.position();
/** Only check queue if room for command length, peer key, name length, and one character of name */
while(outputBuffer.remaining() > 2 + 4 + 1 + 2 && sendQueue.size() > 0) {
CommandEntry e = sendQueue.peek();
Integer k = e.getSourceKey();
Command c = e.getCommand();
/** Clear command buffer */
commandBuffer.clear();
/** Write peer key */
commandBuffer.putInt(k);
/** Write command name */
String commandName = c.getClass().getSimpleName();
commandBuffer.put((byte) (commandName.length() - 1));
for(int i = 0; i < commandName.length(); i++) {
commandBuffer.putChar(commandName.charAt(i));
}
/** Write command */
c.encode(commandBuffer);
/** Flip command buffer before reading */
commandBuffer.flip();
/** Do not send this command yet if no room in output buffer */
if(commandBuffer.remaining() + 2 > outputBuffer.remaining()) {
break;
}
/** Write append command length and command data to output buffer */
outputBuffer.putShort((short) (commandBuffer.remaining() - 1));
outputBuffer.put(commandBuffer);
/** Remove command from queue */
sendQueue.remove();
}
// Primitive encryption
//for(int i = startPosition; i < outputBuffer.position(); i++) {
// outputBuffer.put(i, (byte) (outputBuffer.get(i) ^ sendRandom.nextInt(256)));
//}
/** Flip output buffer */
outputBuffer.flip();
if(outputBuffer.remaining() > 0) {
connection.getChannel().write(outputBuffer);
}
/** Compact buffer, preparing it for appending */
outputBuffer.compact();
if(canvas == null || !canvas.isDrawing()) {
try {
/** Append to input buffer */
//startPosition = inputBuffer.position();
int readResult = connection.getChannel().read(inputBuffer);
if(readResult == -1) {
try { connection.getChannel().close(); } catch(IOException e) { }
userInterface.println("Disconnected (Connection was closed)");
break;
} else if(readResult > 0) {
//for(int i = startPosition; i < inputBuffer.position(); i++) {
// inputBuffer.put(i, (byte) (inputBuffer.get(i) ^ recvRandom.nextInt(256)));
//}
/** Prepare buffer for reading */
inputBuffer.flip();
/** Long enough to peek at length? */
while((canvas == null || !canvas.isDrawing()) && inputBuffer.remaining() >= 2) {
/** Peek at length */
int commandLength = (inputBuffer.getShort(inputBuffer.position()) & 0xFFFF) + 1;
/** Don't read if not long enough to read entire command. */
if(inputBuffer.remaining() < commandLength + 2) {
break;
}
/** We already have the command length */
inputBuffer.getShort();
/** Read command buffer */
ByteBuffer commandBuffer = inputBuffer.slice();
commandBuffer.limit(commandLength);
inputBuffer.position(inputBuffer.position() + commandLength);
/** Read user key */
Integer userKey = commandBuffer.getInt();
/** Read command name */
int commandNameLength = (commandBuffer.get() & 0xFF) + 1;
StringBuffer commandNameBuffer = new StringBuffer();
for(int i = 0; i < commandNameLength; i++) {
commandNameBuffer.append(commandBuffer.getChar());
}
String commandName = commandNameBuffer.toString();
Command command = (Command) StreamableUtils.create("com.jotuntech.sketcher.client.command." + commandName, commandBuffer);
command.perform(this, userMap.get(userKey));
//++iterations;
}
/** Prepare input buffer for appending */
inputBuffer.compact();
}
} catch (IOException e) {
try { connection.getChannel().close(); } catch(IOException e2) { }
userInterface.println("Disconnected (Connection was broken: " + e.getMessage() + ")");
break;
} catch(Throwable t) {
sendQueue.clear();
sendQueue.offer(new CommandEntry(0, new SignOutCommand("Client crashed: " + t.getClass().getSimpleName())));
connection.setTimeOfDeath(System.currentTimeMillis() + 5000);
Log.error(t);
userInterface.println("Disconnected (Client crashed: " + t.getClass().getSimpleName() + ")");
}
}
}
} catch (InterruptedException e) {
Log.debug("Client interrupted.");
} catch (IOException e) {
userInterface.println("Disconnected (Connection was broken: " + e.getMessage() + ")");
Log.error(e);
}
try { connection.getChannel().close(); } catch(IOException e) { }
connection.setChannel(null);
connection.setUser(null);
if("true".equals(props.getProperty("sound.enabled", "true"))) {
UserInterface.AUDIO_OUTTRO.play();
}
}
public BlockingQueue<CommandEntry> getCommandQueue() {
return commandQueue;
}
public boolean isVoiceEnabled() {
return voiceClient != null;
}
public void setVoiceEnabled(boolean voiceEnabled) {
if(voiceEnabled) {
final Integer peerKey = getUserMap().getKeyForValue(getConnection().getUser());
voiceClient = new VoiceClient(new InetSocketAddress(hostname, port + 100), peerKey);
voiceClient.setListener(new VoiceListener() {
public void voiceEvent(VoiceEvent e) {
switch(e.getType()) {
case VoiceEvent.TYPE_CHANNEL_NEW:
Integer userKey = e.getChannel();
User user = userMap.get(userKey);
if(user == null) {
break;
}
break;
case VoiceEvent.TYPE_CHANNEL_DEAD: {
JUserEntry ue = userInterface.getUserList().getEntry(e.getChannel());
if(ue == null) {
break;
}
ue.clearVoice();
break;
}
case VoiceEvent.TYPE_PACKET_VOLUME: {
JUserEntry ue = userInterface.getUserList().getEntry(e.getChannel());
if(ue == null) {
break;
}
if(e.getChannel() == peerKey) {
ue.setVolume(e.getVolume(), 0);
} else {
ue.setVolume(e.getVolume(), 12);
}
break;
}
}
}
});
voiceClient.start();
} else {
voiceClient.interrupt();
try { voiceClient.join(); } catch (InterruptedException e) { }
voiceClient = null;
userInterface.getUserList().clearVoice();
}
}
public VoiceClient getVoiceClient() {
return voiceClient;
}
public Properties getProps() {
return props;
}
public void setSoundEnabled(boolean soundEnabled) {
this.soundEnabled = soundEnabled;
}
public boolean isSoundEnabled() {
return soundEnabled;
}
public void setAds(boolean ads) {
this.ads = ads;
}
public boolean isAds() {
return ads;
}
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
public String getHostname() {
return hostname;
}
}

@ -0,0 +1,54 @@
package com.jotuntech.sketcher.client;
import java.awt.HeadlessException;
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import com.jotuntech.sketcher.common.Log;
public class ClientApplet extends JApplet {
private Client client;
String login, password, hostname;
int port;
boolean ads;
public ClientApplet() throws HeadlessException {
super();
}
public void init() {
login = getParameter("nick");
password = getParameter("secret");
hostname = getParameter("server");
port = Integer.valueOf(getParameter("port"));
ads = "1".equals(getParameter("ads"));
Log.info("Sketcher applet is initializing.");
}
public void start() {
System.gc();
Log.info("Sketcher applet is starting.");
if(Runtime.getRuntime().totalMemory() < 92274688) {
JOptionPane.showMessageDialog(this, "There is too little Java heap memory space for Sketcher to function.\nYour platform likely requires manual configuration. Please pass the\nfollowing run-time parameters to your Java Virtual Machine:\n\n -ms96M -mx384M\n\nContact the website administrator for further assistance.");
}
client = new Client(hostname, port, login, password, ads);
getContentPane().removeAll();
getContentPane().add(client.getUserInterface());
client.start();
}
public void stop() {
Log.info("Sketcher applet is stopping.");
client.close("Exit");
System.gc();
}
public void destroy() {
Log.info("Sketcher applet is being destroyed.");
getContentPane().removeAll();
client = null;
}
}

@ -0,0 +1,8 @@
package com.jotuntech.sketcher.client;
import com.jotuntech.sketcher.common.Streamable;
import com.jotuntech.sketcher.common.User;
public interface Command extends Streamable {
public int perform(Client client, User user);
}

@ -0,0 +1,24 @@
package com.jotuntech.sketcher.client;
public class CommandEntry {
private Integer sourceKey;
private Command command;
public CommandEntry(Integer sourceKey, Command command) {
if(sourceKey == null) {
throw new NullPointerException("Source key can't be null.");
} else if(command == null) {
throw new NullPointerException("Command can't be null.");
}
this.sourceKey = sourceKey;
this.command = command;
}
public Integer getSourceKey() {
return sourceKey;
}
public Command getCommand() {
return command;
}
}

@ -0,0 +1,113 @@
/**
*
*/
package com.jotuntech.sketcher.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ArrayBlockingQueue;
import com.jotuntech.sketcher.common.User;
/**
* Client connection
*
* @author Thor Harald Johansen
*/
public class Connection {
/** Return values for Command */
public final static int SEND_NONE = 0; /** Send to nobody */
public final static int SEND_SELF = 1; /** Send to self only */
public final static int SEND_OTHERS = 2; /** Send to others only */
public final static int SEND_ALL = 3; /** Send to self and others */
public final static long PING_INTERVAL = 10000;
/** Socket channel */
private SocketChannel channel;
/** User to which connection belongs. */
private User user;
/** Input buffer */
private ByteBuffer inputBuffer;
/** Output buffer */
private ByteBuffer outputBuffer;
/** Send queue */
private ArrayBlockingQueue<CommandEntry> sendQueue = new ArrayBlockingQueue<CommandEntry>(98304);
/** Time of death */
private long timeOfDeath;
/** Last ping */
private long lastPing;
public Connection() {
}
public Connection(SocketChannel channel) throws IOException {
/** Store socket channel. */
this.channel = channel;
/** Allocate transmission buffers */
inputBuffer = ByteBuffer.allocate(65538);
outputBuffer = ByteBuffer.allocate(65538);
/** Disable time of death */
timeOfDeath = Long.MAX_VALUE;
}
/** Set user for connection */
public void setUser(User user) {
this.user = user;
}
/** Get user for connection */
public User getUser() {
return user;
}
public void setChannel(SocketChannel channel) {
this.channel = channel;
}
public SocketChannel getChannel() {
return channel;
}
public ByteBuffer getInputBuffer() {
return inputBuffer;
}
public ByteBuffer getOutputBuffer() {
return outputBuffer;
}
public ArrayBlockingQueue<CommandEntry> getSendQueue() {
return sendQueue;
}
public void setTimeOfDeath(long timeOfDeath) {
this.timeOfDeath = timeOfDeath;
}
public boolean hasTimeOfDeath() {
return timeOfDeath != Long.MAX_VALUE;
}
public boolean isTimeOfDeath() {
return timeOfDeath != Long.MAX_VALUE && System.currentTimeMillis() >= timeOfDeath;
}
public void setLastPing(long lastPing) {
this.lastPing = lastPing;
}
public long getLastPing() {
return lastPing;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,154 @@
package com.jotuntech.sketcher.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
public class JCollapsiblePanel extends JPanel {
private final static Icon expandedIcon = UIManager.getIcon("Tree.expandedIcon");
private final static Icon collapsedIcon = UIManager.getIcon("Tree.collapsedIcon");
private final static Icon pinNormalIcon = new ImageIcon(JCollapsiblePanel.class.getResource("images/pin_normal.png"));
private final static Icon pinStuckIcon = new ImageIcon(JCollapsiblePanel.class.getResource("images/pin_stuck.png"));
private JLabel label;
private JLabel pin;
private JPanel contentPane;
private JCollapsiblePanelGroup group;
private boolean stuck;
private int id;
private boolean expandedByDefault, stuckByDefault;
public JCollapsiblePanel(int id, String title, boolean expanded, boolean stuckx) {
super();
this.id = id;
this.stuck = stuckx;
this.stuckByDefault = stuckx;
this.expandedByDefault = expanded;
setBorder(new EmptyBorder(0, 0, 1, 0));
setAlignmentX(Component.LEFT_ALIGNMENT);
setLayout(new BorderLayout());
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
add(topPanel, BorderLayout.NORTH);
label = new JLabel();
label.setIcon(expanded ? expandedIcon : collapsedIcon);
label.setText(title);
label.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
if(!stuck) {
setExpanded(!contentPane.isVisible(), true);
}
}
});
label.setAlignmentX(Component.LEFT_ALIGNMENT);
label.setBackground(new Color(172, 200, 230));
label.setOpaque(true);
topPanel.add(label, BorderLayout.CENTER);
pin = new JLabel();
pin.setIcon(stuck ? pinStuckIcon : pinNormalIcon);
pin.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
setStuck(!isStuck());
}
});
pin.setAlignmentX(Component.LEFT_ALIGNMENT);
pin.setBackground(new Color(172, 200, 230));
pin.setOpaque(true);
topPanel.add(pin, BorderLayout.EAST);
contentPane = new JPanel();
contentPane.setVisible(expanded);
contentPane.setBorder(new EmptyBorder(4, 4, 4, 4));
add(contentPane, BorderLayout.CENTER);
}
public JPanel getContentPane() {
return contentPane;
}
public void setExpanded(boolean expanded, boolean interactive) {
if(interactive && expanded && group != null) {
for(JCollapsiblePanel panel : group) {
if(panel == this || panel.isStuck()) {
continue;
}
panel.setExpanded(false, false);
}
}
contentPane.setVisible(expanded);
label.setIcon(expanded ? expandedIcon : collapsedIcon);
}
public boolean isExpanded() {
return contentPane.isVisible();
}
public void setGroup(JCollapsiblePanelGroup group) {
this.group = group;
group.add(this);
}
public void setStuck(boolean stuck) {
this.stuck = stuck;
pin.setIcon(stuck ? pinStuckIcon : pinNormalIcon);
}
public boolean isStuck() {
return stuck;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public boolean isExpandedByDefault() {
return expandedByDefault;
}
public boolean isStuckByDefault() {
return stuckByDefault;
}
}

@ -0,0 +1,7 @@
package com.jotuntech.sketcher.client;
import java.util.HashSet;
public class JCollapsiblePanelGroup extends HashSet<JCollapsiblePanel> {
}

@ -0,0 +1,74 @@
package com.jotuntech.sketcher.client;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Arrays;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class JUserEntry extends JPanel {
private JLabel nameLabel;
private JPanel infoPanel;
private JProgressBar volumeBar;
private double slowVolume;
private int[] delayLine;
public JUserEntry(String name) {
setLayout(new BorderLayout());
setOpaque(false);
nameLabel = new JLabel(name);
add(nameLabel, BorderLayout.CENTER);
infoPanel = new JPanel();
infoPanel.setOpaque(false);
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.X_AXIS));
add(infoPanel, BorderLayout.EAST);
volumeBar = new JProgressBar(0, 32768);
volumeBar.setPreferredSize(new Dimension(64, 16));
volumeBar.setOpaque(false);
volumeBar.setValue(32768);
volumeBar.setEnabled(false);
volumeBar.setToolTipText(name + " is not on voice.");
infoPanel.add(volumeBar);
}
public void setVolume(int volume) {
setVolume(volume, 0);
}
public void setVolume(int volume, int delay) {
if(volume <= 0) {
volume = 1;
}
if(delayLine == null || delayLine.length <= delay) {
delayLine = new int[delay + 1];
Arrays.fill(delayLine, 1);
}
delayLine[delay] = volume;
double newVolume = Math.log(delayLine[0]) * 32768d / Math.log(32768);
for(int i = 0; i < delayLine.length - 1; i++) {
delayLine[i] = delayLine[i + 1];
}
delayLine[delayLine.length - 1] = 1;
slowVolume += (newVolume - slowVolume) * 0.333d;
volumeBar.setValue((int) Math.round(slowVolume));
if(!volumeBar.isEnabled()) {
volumeBar.setEnabled(true);
volumeBar.setToolTipText(nameLabel.getText() + " is on voice.");
}
}
public void clearVoice() {
volumeBar.setValue(32768);
volumeBar.setEnabled(false);
volumeBar.setToolTipText(nameLabel.getText() + " is not on voice.");
}
}

@ -0,0 +1,57 @@
package com.jotuntech.sketcher.client;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.jotuntech.sketcher.common.TwoWayHashMap;
import com.jotuntech.sketcher.common.User;
public class JUserList extends JPanel {
TwoWayHashMap<Integer, User> userMap;
Map<Integer, JUserEntry> entryMap;
public JUserList(TwoWayHashMap<Integer, User> userMap) {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.setBackground(Color.WHITE);
this.setOpaque(true);
entryMap = new HashMap<Integer, JUserEntry>();
this.userMap = userMap;
userMap.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
redo();
}
});
redo();
}
private void redo() {
entryMap.clear();
removeAll();
for(Map.Entry<Integer, User> e : userMap.entrySet()) {
JUserEntry ue = new JUserEntry(e.getValue().getName());
add(ue);
entryMap.put(e.getKey(), ue);
}
this.revalidate();
}
public JUserEntry getEntry(Integer key) {
return entryMap.get(key);
}
public void clearVoice() {
for(JUserEntry ue : entryMap.values()) {
ue.clearVoice();
}
}
}

@ -0,0 +1,81 @@
package com.jotuntech.sketcher.client;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
/**
* An collection of built-in Swing look and feels. This saves clients the
* trouble of manually setting the look and feel.
*
* @author Aaron Faanes
*
* @see javax.swing.LookAndFeel
*/
public enum LookAndFeel {
/**
* Represents the Nimbus look-and-feel that was added in Java 6 Update
* 10.
*/
NIMBUS() {
private volatile String name;
@Override
public String getName() {
if (this.name == null) {
for (final LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if (info.getClassName().contains("nimbus")) {
this.name = info.getClassName();
}
}
}
return this.name;
}
},
/**
* Represents the look-and-feel that emulates the system
*
* @see UIManager#getSystemLookAndFeelClassName()
*/
SYSTEM() {
@Override
public String getName() {
return UIManager.getSystemLookAndFeelClassName();
}
};
/**
* Returns the name of the class that implements this look-and-feel. The
* returned name, if non-null, can be directly used in a
* {@link UIManager#setLookAndFeel(String)} invocation. If the returned
* value is null, then no class name could be found.
*
*
* @return the name of the class that implements this look-and-feel, or
* {@code null} if no class name could be found
*/
public abstract String getName();
/**
* Sets the global look and feel to this look and feel.
*
* @return {@code true} if setting the look-and-feel was successful,
* otherwise {@code false}
*/
public boolean activate() {
final String name = this.getName();
if (name == null) {
return false;
}
try {
UIManager.setLookAndFeel(name);
return true;
} catch (final Exception e) {
return false;
}
}
}

@ -0,0 +1,568 @@
package com.jotuntech.sketcher.client;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.DataOutput;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Map;
import com.jotuntech.sketcher.common.BitmapLayer;
import com.jotuntech.sketcher.common.BitmapTile;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.TwoWayHashMap;
public class PSDEncoder {
private RandomAccessFile raf;
private BufferedImage image;
private BitmapLayer[] layers;
private long[][] channelLengthPositions;
public PSDEncoder(RandomAccessFile raf, BufferedImage image, TwoWayHashMap<Integer, Layer> layers) throws IOException {
this.raf = raf;
this.image = image;
int layersLength = 0;
for(Layer l : layers.values()) {
if(l instanceof BitmapLayer) {
++layersLength;
}
}
this.layers = new BitmapLayer[layersLength];
int layerIndex = 0;
for(Layer l : layers.values()) {
if(l instanceof BitmapLayer) {
this.layers[layerIndex++] = (BitmapLayer) l;
}
}
channelLengthPositions = new long[this.layers.length][4];
}
public void encode() throws IOException {
/* Signature */
raf.write(new byte[] {'8', 'B', 'P', 'S'});
/* Version */
raf.writeShort(1);
/* Reserved */
raf.write(new byte[] {0, 0, 0, 0, 0, 0});
/* Number of channels */
raf.writeShort(4);
/* Image height */
raf.writeInt(image.getHeight());
/* Image width */
raf.writeInt(image.getWidth());
/* Bits per channel */
raf.writeShort(8);
/* Color mode: RGB color */
raf.writeShort(3);
/* Length of color mode data section */
raf.writeInt(0);
/* Length of image resources section */
raf.writeInt(0);
/* Layer and mask information */
writeLayerAndMaskInfo(raf);
/* Image data */
writeImagePixelData(raf);
}
private void writeLayerAndMaskInfo(RandomAccessFile raf) throws IOException {
long lengthPointer = raf.getFilePointer();
raf.writeInt(0);
/* Layer info */
writeLayerInfo(raf);
/* Mask info */
writeMaskInfo(raf);
/* Go back and write length */
long endPointer = raf.getFilePointer();
raf.seek(lengthPointer);
raf.writeInt((int)(endPointer - lengthPointer - 4));
raf.seek(endPointer);
}
private void writeLayerInfo(RandomAccessFile odo) throws IOException {
System.err.println("Writing layer info.");
long lengthPointer = raf.getFilePointer();
odo.writeInt(0);
/* Layers struct */
writeLayersStruct(odo);
/* Pixel data */
writeAllLayersPixelData(odo);
/* Go back and write length */
long endPointer = raf.getFilePointer();
raf.seek(lengthPointer);
raf.writeInt((int)(endPointer - lengthPointer - 4));
raf.seek(endPointer);
}
private void writeLayersStruct(RandomAccessFile odo) throws IOException {
System.err.println("Writing layers struct.");
/* Layer count */
odo.writeShort(layers.length);
/* Layers */
for(int layerIndex = 0; layerIndex < layers.length; layerIndex++) {
writeLayer(odo, layerIndex);
}
}
private void writeLayer(RandomAccessFile odo, int layerIndex) throws IOException {
/* Get layer */
BitmapLayer l = layers[layerIndex];
System.err.println("Writing layer: " + l.getName());
/* Layer top */
odo.writeInt(0);
/* Layer left */
odo.writeInt(0);
/* Layer bottom */
odo.writeInt(image.getHeight());
/* Layer right */
odo.writeInt(image.getWidth());
/* Number of channels */
odo.writeShort(4);
/* Channel length info */
writeChannelLengthInfo(odo, layerIndex);
/* Blend mode signature */
odo.write(new byte[] {'8', 'B', 'I', 'M'});
/* Blend mode key */
odo.write(new byte[] {'n', 'o', 'r', 'm'});
/* Opacity */
odo.writeByte(Math.round(l.getOpacity() * 255f));
/* Clipping: Base */
odo.writeByte(0);
/* Flags: Transparency not protected and visible */
odo.writeByte(0x80);
/* Filler */
odo.writeByte(0);
/* Prepare layer name */
String name = l.getName();
int origLength = name.length();
int moduloLength = (origLength + 1) % 4;
int padLength = 0;
if(moduloLength > 0) {
padLength = 4 - moduloLength;
for(int i = 0; i < padLength; i++) {
name += "\000";
}
}
/* Extra data size */
odo.writeInt(4 + 44 + 1 + origLength + padLength);
/* Layer mask data size */
odo.writeInt(0);
/* Layer blending ranges */
writeLayerBlendingRanges(odo);
odo.writeByte(origLength);
odo.writeBytes(name);
}
private void writeChannelLengthInfo(RandomAccessFile raf, int layerIndex) throws IOException {
System.err.println("Writing channel length info.");
/* Channel ID: Transparency Mask */
raf.writeShort(-1);
/* Channel length */
channelLengthPositions[layerIndex][0] = raf.getFilePointer();
raf.writeInt(0);
/* Channel ID: Red */
raf.writeShort(0);
/* Channel length */
channelLengthPositions[layerIndex][1] = raf.getFilePointer();
raf.writeInt(0);
/* Channel ID: Green */
raf.writeShort(1);
/* Channel length */
channelLengthPositions[layerIndex][2] = raf.getFilePointer();
raf.writeInt(0);
/* Channel ID: Blue */
raf.writeShort(2);
/* Channel length */
channelLengthPositions[layerIndex][3] = raf.getFilePointer();
raf.writeInt(0);
}
private void writeLayerBlendingRanges(DataOutput odo) throws IOException {
System.err.println("Writing layer blending ranges.");
/* Length */
odo.writeInt(40);
/* Composite gray blend source */
odo.writeInt(0x0000FFFF);
/* Composite gray blend destination */
odo.writeInt(0x0000FFFF);
/* First channel source range */
odo.writeInt(0x0000FFFF);
/* First channel destination range */
odo.writeInt(0x0000FFFF);
/* Second channel source range */
odo.writeInt(0x0000FFFF);
/* Second channel destination range */
odo.writeInt(0x0000FFFF);
/* Third channel source range */
odo.writeInt(0x0000FFFF);
/* Third channel destination range */
odo.writeInt(0x0000FFFF);
/* Fourth channel source range */
odo.writeInt(0x0000FFFF);
/* Fourth channel destination range */
odo.writeInt(0x0000FFFF);
}
private void writeAllLayersPixelData(RandomAccessFile odo) throws IOException {
System.out.println("Writing pixel layer data.");
for(int layerIndex = 0; layerIndex < layers.length; layerIndex++) {
writeLayerPixelData(odo, layerIndex);
}
}
private void writeMaskInfo(RandomAccessFile raf) throws IOException {
System.err.println("Writing mask info.");
/* Mask info length */
long lengthPointer = raf.getFilePointer();
raf.writeInt(0);
/* Overlay color space */
raf.writeShort(0);
/* Color components */
raf.writeShort(0);
raf.writeShort(0);
raf.writeShort(0);
raf.writeShort(0);
/* Opacity */
raf.writeShort(0);
/* Kind: Use value stored per layer */
raf.writeByte(128);
/* Filler */
raf.writeByte(0);
/* Go back and write length */
long endPointer = raf.getFilePointer();
raf.seek(lengthPointer);
raf.writeInt((int)(endPointer - lengthPointer - 4));
raf.seek(endPointer);
}
public void writeImagePixelData(RandomAccessFile odo) throws IOException {
System.out.println("Writing image data.");
int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
byte[] scanline = new byte[image.getWidth()];
/* Compression: RLE */
odo.writeShort(1);
/* Placeholder scanline bytecounts */
long redCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
long greenCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
long blueCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
long alphaCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
PackBitsOutputStream pbos = new PackBitsOutputStream(raf);
/* Image data - Red */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) ((pixels[y * scanline.length + x] >> 16) & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(redCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
/* Image data - Green */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) ((pixels[y * scanline.length + x] >> 8) & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(greenCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
/* Image data - Blue */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) (pixels[y * scanline.length + x] & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(blueCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
/* Image data - Alpha */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) ((pixels[y * scanline.length + x] >> 24) & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(alphaCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
}
public void writeLayerPixelData(RandomAccessFile raf, int layerIndex) throws IOException {
System.out.println("Writing image data.");
byte[] scanline = new byte[image.getWidth()];
/* Get tiles */
Map<Point, BitmapTile> tiles = layers[layerIndex].getTiles();
PackBitsOutputStream pbos = new PackBitsOutputStream(raf);
long startOfAlpha = raf.getFilePointer();
int alphaLength;
/* Compression: RLE */
raf.writeShort(1);
/* Alpha scanline counts */
long alphaCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Alpha image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
int[] tilePixels = t.getPixels();
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
scanline[x + offset] = (byte) ((tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] >> 24) & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(alphaCountsPosition + y * 2);
raf.writeShort((short) (scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfAlpha = raf.getFilePointer();
alphaLength = (int) (endOfAlpha - startOfAlpha);
raf.seek(channelLengthPositions[layerIndex][0]);
raf.writeInt(alphaLength);
raf.seek(endOfAlpha);
long startOfRed = raf.getFilePointer();
int redLength;
/* Compression: RLE */
raf.writeShort(1);
/* Red scanline counts */
long redCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Red image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
int[] tilePixels = t.getPixels();
scanline[x + offset] = (byte) ((tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] >> 16) & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(redCountsPosition + y * 2);
raf.writeShort((short) (scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfRed = raf.getFilePointer();
redLength = (int) (endOfRed - startOfRed);
raf.seek(channelLengthPositions[layerIndex][1]);
raf.writeInt(redLength);
raf.seek(endOfRed);
long startOfGreen = raf.getFilePointer();
int greenLength;
/* Compression: RLE */
raf.writeShort(1);
/* Green scanline counts */
long greenCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Green image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
int[] tilePixels = t.getPixels();
scanline[x + offset] = (byte) ((tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] >> 8) & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(greenCountsPosition + y * 2);
raf.writeShort((short)(scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfGreen = raf.getFilePointer();
greenLength = (int) (endOfGreen - startOfGreen);
raf.seek(channelLengthPositions[layerIndex][2]);
raf.writeInt(greenLength);
raf.seek(endOfGreen);
long startOfBlue = raf.getFilePointer();
int blueLength;
/* Compression: RLE */
raf.writeShort(1);
/* Blue scanline counts */
long blueCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Blue image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
int[] tilePixels = t.getPixels();
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
scanline[x + offset] = (byte) (tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(blueCountsPosition + y * 2);
raf.writeShort((short)(scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfBlue = raf.getFilePointer();
blueLength = (int) (endOfBlue - startOfBlue);
raf.seek(channelLengthPositions[layerIndex][3]);
raf.writeInt(blueLength);
raf.seek(endOfBlue);
}
}

@ -0,0 +1,141 @@
package com.jotuntech.sketcher.client;
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
public class PackBitsOutputStream extends OutputStream {
private DataOutput mdo;
private final static int IN_BUFFER_SIZE = 2048;
private final static int IN_BUFFER_MASK = IN_BUFFER_SIZE - 1;
private final static int OUT_BUFFER_SIZE = 4096;
private int[] inBuffer;
private int inBufferWritePos, inBufferReadPos, inBufferMark;
private byte[] outBuffer;
private int outBufferWritePos;
public PackBitsOutputStream(DataOutput mdo) {
this.mdo = mdo;
this.inBuffer = new int[IN_BUFFER_SIZE];
this.inBufferWritePos = 0;
this.inBufferReadPos = 0;
this.inBufferMark = 0;
this.outBuffer = new byte[OUT_BUFFER_SIZE];
this.outBufferWritePos = 0;
}
public void write(int b) throws IOException {
inBuffer[inBufferWritePos++ & IN_BUFFER_MASK] = b;
if(inBufferWritePos - inBufferReadPos == IN_BUFFER_SIZE) {
pack();
}
}
public void write(byte[] b) throws IOException {
for(int i = 0; i < b.length; i++) {
inBuffer[inBufferWritePos++ & IN_BUFFER_MASK] = b[i];
if(inBufferWritePos - inBufferReadPos == IN_BUFFER_SIZE) {
pack();
}
}
}
public void write(byte[] b, int off, int len) throws IOException {
for(int i = off; i < off + len; i++) {
inBuffer[inBufferWritePos++ & IN_BUFFER_MASK] = b[i];
if(inBufferWritePos - inBufferReadPos == IN_BUFFER_SIZE) {
pack();
}
}
}
public void flush() throws IOException {
pack();
}
private void output(int b) {
outBuffer[outBufferWritePos++] = (byte) b;
}
private void pack() throws IOException {
outBufferWritePos = 0;
while(available() > 0) {
int b = read();
int repeat = 1;
while(available() > 0 && peek() == b && repeat < 128) {
++repeat;
skip();
}
if(repeat == 1) {
mark();
int b2 = read();
int literal = 0;
while(available() > 0 && peek() != b2 && literal < 127) {
b2 = read();
++literal;
}
if(available() == 0 && literal < 127) {
++literal;
}
if(literal == 0) {
reset();
output(0);
output(b);
} else {
reset();
output(literal);
output(b);
for(int i = 0; i < literal; i++) {
output(read());
}
}
} else if(repeat == 2) {
output(1);
output(b);
output(b);
} else {
output(1 - repeat);
output(b);
}
}
mdo.write(outBuffer, 0, outBufferWritePos);
}
public void close() throws IOException {
pack();
}
private int available() {
return inBufferWritePos - inBufferReadPos;
}
private int peek() {
return inBuffer[inBufferReadPos & IN_BUFFER_MASK];
}
private int read() {
return inBuffer[inBufferReadPos++ & IN_BUFFER_MASK];
}
private void skip() {
++inBufferReadPos;
}
private void mark() {
inBufferMark = inBufferReadPos;
}
private void reset() {
inBufferReadPos = inBufferMark;
}
}

@ -0,0 +1,150 @@
package com.jotuntech.sketcher.client;
import java.util.ArrayList;
import java.util.List;
import com.jotuntech.sketcher.common.Input;
/**
* Static singleton for input interpolation.
*
* @author Thor Harald Johansen
*/
public class Smoother {
/* Smoothing threshold */
private static float threshold = 4f;
private static float smoothness = 1.5f;
private static boolean enabled = true;
Input[] cursor = new Input[] {new Input(), new Input(), new Input()};
/**
* Sets all points to given input
* @param d input to add.
*/
public void set(Input d) {
cursor[0] = d;
cursor[1] = cursor[0];
cursor[2] = cursor[1];
}
public void setPressure(int pressure) {
//cursor[0].pressure = pressure;
//cursor[1].pressure = pressure;
//cursor[2].pressure = pressure;
}
/**
* Adds a new input for smoothing.
*
* @param d input to add.
*/
public void add(Input d) {
float dx = d.x - cursor[2].x;
float dy = d.y - cursor[2].y;
float dd = d.pressure - cursor[2].pressure;
float h = (float)Math.hypot(dx, dy);
float d2 = h - smoothness;
if(d2 > 0) {
float sx = dx * d2 / h;
float sy = dy * d2 / h;
float sd = dd * d2 / h;
cursor[0] = cursor[1];
cursor[1] = cursor[2];
cursor[2] = new Input(cursor[2].x + sx, cursor[2].y + sy, (int)(cursor[2].pressure + sd));
}
}
/**
* Return a list of smoothed inputs.
*
* @return List of smoothed inputs.
*/
public List<Input> get() {
/* Create list for inputs. */
List<Input> inputs = new ArrayList<Input>();
if(enabled) {
/* Segment cursor. */
segment(inputs, cursor[0], cursor[1], cursor[2]);
} else {
inputs.add(cursor[2]);
}
return inputs;
}
public Input getIndex(int index) {
return cursor[index];
}
// uses a, b and c to interpolate inputs between b and c, store in inputs.
void segment(List<Input> inputs, Input a, Input b, Input c) {
// difference AB & BC
Input dab = a.difference(b);
Input dbc = b.difference(c);
// magnitude AB & BC
float mab = (float)Math.hypot(dab.x, dab.y);
float mbc = (float)Math.hypot(dbc.x, dbc.y);
// orientation AB & BC
float oab = (float)Math.acos(dab.x / mab) * (dab.y < 0 ? -1 : 1);
float obc = (float)Math.acos(dbc.x / mbc) * (dbc.y < 0 ? -1 : 1);
// normalize
if(obc - oab > Math.PI || obc - oab < -Math.PI) {
obc -= Math.PI * 2;
}
// angle ABC
float abc = Math.abs(obc - oab);
// threshold of operation
if(mbc * abc > threshold && abc < Math.PI / 2) {
// three-quarter of angle ABC
float angle = (oab + (obc * 3)) / 4;
// half of magnitude BC
Input d = new Input(b.x + ((float)Math.cos(angle) * mbc / 2), b.y
+ ((float)Math.sin(angle) * mbc / 2), (b.pressure + c.pressure) / 2);
segment(inputs, a, b, d);
inputs.add(d);
segment(inputs, b, d, c);
} else {
inputs.add(c);
}
}
public static void setThreshold(float threshold) {
Smoother.threshold = threshold;
}
public static float getThreshold() {
return threshold;
}
public static void setSmoothness(float smoothness) {
Smoother.smoothness = smoothness;
}
public static float getSmoothness() {
return smoothness;
}
public static void setEnabled(boolean enabled) {
Smoother.enabled = enabled;
}
public static boolean isEnabled() {
return enabled;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,56 @@
package com.jotuntech.sketcher.client.command;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import javax.swing.SwingUtilities;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.Canvas;
import com.jotuntech.sketcher.common.Log;
import com.jotuntech.sketcher.common.User;
public class CanvasCommand implements Command {
protected int width, height;
public CanvasCommand() {
}
public CanvasCommand(int width, int height) {
this.width = width;
this.height = height;
}
public int perform(final Client client, User user) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
/** Create and set new canvas */
client.setCanvas(new Canvas(width, height));
Log.info("Created canvas.");
}
});
} catch (InterruptedException e) {
Log.error(e);
} catch (InvocationTargetException e) {
Log.error(e);
}
/** Return silently */
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
width = in.getInt();
height = in.getInt();
}
public void encode(ByteBuffer out) {
out.putInt(width);
out.putInt(height);
}
}

@ -0,0 +1,56 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.BitmapLayer;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.User;
public class CreateLayerCommand implements Command {
int layerKey;
int type;
String name;
public CreateLayerCommand() {
}
public CreateLayerCommand(int layerKey, int type, String name) {
this.layerKey = layerKey;
this.type = type;
this.name = name;
}
public int perform(Client client, User user) {
Layer l = new BitmapLayer(name);
client.getCanvas().getLayerMap().put(layerKey, l);
if(user != null) {
client.getUserInterface().println(user.getName() + " created layer " + l.getName());
}
return Connection.SEND_ALL;
}
public void decode(ByteBuffer in) {
layerKey = in.getInt();
type = in.get();
StringBuffer nameBuffer = new StringBuffer();
while(in.remaining() >= 2) {
nameBuffer.append(in.getChar());
}
name = nameBuffer.toString();
}
public void encode(ByteBuffer out) {
out.putInt(layerKey);
out.put((byte) type);
for(int i = 0; i < name.length(); i++) {
out.putChar(name.charAt(i));
}
}
}

@ -0,0 +1,56 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.Input;
import com.jotuntech.sketcher.common.User;
/** Cursor command.
* @author Thor Harald Johansen
*
*/
public class CursorCommand implements Command {
Input input;
/** Constructs an Cursor command. */
public CursorCommand() {
}
/** Constructs an Cursor command.
* @param user Source user.
* @param input Cursor position */
public CursorCommand(Input input) {
this.input = input;
}
public int perform(Client client, User user) {
if(user == null) {
/** Signed in users only. */
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
user.setCursor(input);
/** Return and broadcast. */
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
input = new Input();
input.decode(in);
}
public void encode(ByteBuffer out) {
input.encode(out);
}
}

@ -0,0 +1,74 @@
package com.jotuntech.sketcher.client.command;
import java.awt.image.ImageObserver;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.Canvas;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.UndoEntry;
import com.jotuntech.sketcher.common.User;
public class DeleteLayerCommand implements Command {
int layerKey;
public DeleteLayerCommand() {
}
public DeleteLayerCommand(int layerKey) {
this.layerKey = layerKey;
}
public int perform(Client client, User user) {
if(user == null) {
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
Canvas canvas = client.getCanvas();
Layer layer = canvas.getLayerMap().remove(layerKey);
if(layer == null) {
return Connection.SEND_NONE;
}
// Remove references to layer from undo deque
for(User u: client.getUserMap().values()) {
Set<UndoEntry> uers = new HashSet<UndoEntry>();
for(UndoEntry ue : u.getUndoDeque()) {
if(ue.getLayer() == layer) {
uers.add(ue);
}
}
for(UndoEntry ue : uers) {
u.getUndoDeque().remove(ue);
}
}
if(canvas.getImageObserver() != null) {
canvas.getImageObserver().imageUpdate(null, ImageObserver.ALLBITS, 0, 0, canvas.getWidth(), canvas.getHeight());
}
/** Notify the user */
client.getUserInterface().println(user.getName() + " deleted layer " + layer.getName());
/** Return and broadcast. */
return Connection.SEND_ALL;
}
public void decode(ByteBuffer in) {
layerKey = in.getInt();
}
public void encode(ByteBuffer out) {
out.putInt(layerKey);
}
}

@ -0,0 +1,82 @@
package com.jotuntech.sketcher.client.command;
import java.awt.AlphaComposite;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.BitmapLayer;
import com.jotuntech.sketcher.common.User;
import com.jotuntech.sketcher.common.filter.Filter;
public class FilterCommand implements Command {
private Filter filter;
private int x, y, w, h;
float a, b, c;
public FilterCommand() {
}
public FilterCommand(Filter filter, float x, float y, float w, float h, float a, float b, float c) {
this.filter = filter;
this.x = (int) x;
this.y = (int) y;
this.w = (int) w;
this.h = (int) h;
this.a = a;
this.b = b;
this.c = c;
}
public int perform(Client client, User user) {
if(user == null) {
return Connection.SEND_NONE;
}
BitmapLayer pl = (BitmapLayer) user.getPhantomLayer();
filter.setSize(w, h);
filter.setParameterA(a);
user.getLayer().copyTo(pl, null, false, x, y, x, y, w, h);
pl.setAlphaRule(AlphaComposite.SRC);
pl.setOpacity(1);
pl.applyFilter(filter, user.getLayer().getImageObserver(), x, y, w, h);
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
x = (int) in.getFloat();
y = (int) in.getFloat();
w = (int) in.getFloat();
h = (int) in.getFloat();
a = in.getFloat();
b = in.getFloat();
c = in.getFloat();
StringBuffer filterNameBuffer = new StringBuffer();
while(in.remaining() >= 2) {
filterNameBuffer.append(in.getChar());
}
String filterName = "com.jotuntech.sketcher.common.filter." + filterNameBuffer.toString();
try {
filter = (Filter) Class.forName(filterName).newInstance();
} catch (Throwable t) {
t.printStackTrace();
}
}
public void encode(ByteBuffer out) {
out.putFloat(x);
out.putFloat(y);
out.putFloat(w);
out.putFloat(h);
out.putFloat(a);
out.putFloat(b);
out.putFloat(c);
String filterName = filter.getClass().getSimpleName();
for(int i = 0; i < filterName.length(); i++) {
out.putChar(filterName.charAt(i));
}
}
}

@ -0,0 +1,58 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.client.UserInterface;
import com.jotuntech.sketcher.common.User;
public class KickCommand implements Command {
int targetKey;
String reason;
public KickCommand() {
}
public KickCommand(int targetKey, String reason) {
this.targetKey = targetKey;
this.reason = reason;
}
public int perform(Client client, User user) {
if(user != null && user.isViewer()) {
return Connection.SEND_NONE;
}
User targetUser = client.getUserMap().remove(targetKey);
client.getUserInterface().println(targetUser.getName() + " was kicked out (" + reason + ")");
if(targetUser == client.getConnection().getUser()) {
client.getConnection().setTimeOfDeath(System.currentTimeMillis());
}
if(client.isSoundEnabled()) {
UserInterface.AUDIO_KICK.play();
}
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
targetKey = in.getInt();
StringBuffer reasonBuffer = new StringBuffer();
while(in.remaining() >= 2) {
reasonBuffer.append(in.getChar());
}
reason = reasonBuffer.toString();
}
public void encode(ByteBuffer out) {
out.putInt(targetKey);
for(int i = 0; i < reason.length(); i++) {
out.putChar(reason.charAt(i));
}
}
}

@ -0,0 +1,66 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.Canvas;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.User;
public class LayerDataCommand implements Command {
int layerKey;
boolean phantom;
byte[] data;
public LayerDataCommand() {
}
public LayerDataCommand(int layerKey, boolean phantom, byte[] data) {
this.layerKey = layerKey;
this.phantom = phantom;
this.data = data;
}
public int perform(Client client, User user) {
Canvas canvas = client.getCanvas();
if(canvas == null) {
return Connection.SEND_NONE;
}
Layer layer;
if(phantom) {
if(user == null) {
return Connection.SEND_NONE;
}
layer = user.getPhantomLayer();
} else {
layer = canvas.getLayerMap().get(layerKey);
if(layer == null) {
return Connection.SEND_NONE;
}
}
layer.decode(data);
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
layerKey = in.getInt();
phantom = in.get() != 0;
data = new byte[in.remaining()];
in.get(data);
}
public void encode(ByteBuffer out) {
out.putInt(layerKey);
out.put((byte) (phantom ? 0xFF : 0x00));
out.put(data);
}
}

@ -0,0 +1,73 @@
/**
*
*/
package com.jotuntech.sketcher.client.command;
import java.awt.AlphaComposite;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.Input;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.User;
public class LineCommand implements Command {
protected Input input;
/** Constructs a Daub command */
public LineCommand() {
}
/** Constructs a Daub command.
* @param user Source user.
* @param input Position of new daub. */
public LineCommand(Input input) {
this.input = input;
}
public int perform(Client client, User user) {
if(user != null && user.getLayer() != null && user.getBrush() != null) {
if(user.isViewer()) {
return Connection.SEND_NONE;
}
Layer l = user.getPhantomLayer();
l.setOpacity(Math.abs(user.getBrush().getOpacity()) / 255f);
if(user.getBrush().isLockTransparency()) {
l.setAlphaRule(AlphaComposite.SRC_ATOP);
} else if(user.getBrush().getOpacity() >= 0) {
l.setAlphaRule(AlphaComposite.SRC_OVER);
} else {
l.setAlphaRule(AlphaComposite.DST_OUT);
}
/** Draw line */
Input end = user.getPhantomLayer().line(user.getCursor(), input, user.getColor(), user.getBrush(), user.getLayer());
if(end != null) {
/** Move cursor on success */
user.setCursor(end);
/** Return and broadcast */
return Connection.SEND_OTHERS;
}
}
/** Return silently */
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
input = new Input();
input.decode(in);
}
public void encode(ByteBuffer out) {
input.encode(out);
}
}

@ -0,0 +1,45 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import java.util.Deque;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.UndoEntry;
import com.jotuntech.sketcher.common.User;
public class MergeCommand implements Command {
public int perform(Client client, User user) {
if(user == null || user.getLayer() == null) {
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
if(user.getPhantomLayer().isEmpty()) {
return Connection.SEND_NONE;
}
UndoEntry undoEntry = new UndoEntry();
undoEntry.setLayer(user.getLayer());
undoEntry.setUndoData(user.getLayer().merge(user.getPhantomLayer()));
Deque<UndoEntry> undoDeque = user.getUndoDeque();
undoDeque.addFirst(undoEntry);
while(undoDeque.size() > 10) {
undoDeque.removeLast();
}
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
}
public void encode(ByteBuffer out) {
}
}

@ -0,0 +1,68 @@
package com.jotuntech.sketcher.client.command;
import java.awt.AlphaComposite;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.BitmapLayer;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.User;
public class MoveCommand implements Command {
private float sx, sy, dx, dy, w, h;
public MoveCommand() {
}
public MoveCommand(float sx, float sy, float dx, float dy, float w, float h) {
this.sx = sx;
this.sy = sy;
this.dx = dx;
this.dy = dy;
this.w = w;
this.h = h;
}
public int perform(Client client, User user) {
if(user == null || user.getLayer() == null) {
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
if(user.getPhantomLayer().isEmpty()) {
Layer pl = (BitmapLayer) user.getPhantomLayer();
pl.setAlphaRule(AlphaComposite.SRC_OVER);
pl.setOpacity(1);
user.getLayer().copyTo(pl, null, false, sx, sy, dx, dy, w, h);
} else {
user.getPhantomLayer().copyTo(user.getPhantomLayer(), user.getLayer().getImageObserver(), true, sx, sy, dx, dy, w, h);
user.getPhantomLayer().clean();
}
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
sx = in.getFloat();
sy = in.getFloat();
dx = in.getFloat();
dy = in.getFloat();
w = in.getFloat();
h = in.getFloat();
}
public void encode(ByteBuffer out) {
out.putFloat(sx);
out.putFloat(sy);
out.putFloat(dx);
out.putFloat(dy);
out.putFloat(w);
out.putFloat(h);
}
}

@ -0,0 +1,32 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.User;
public class PingCommand implements Command {
private long timestamp;
public PingCommand() {
this.timestamp = System.currentTimeMillis();
}
public PingCommand(long timestamp) {
this.timestamp = timestamp;
}
public int perform(Client client, User user) {
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
timestamp = in.getLong();
}
public void encode(ByteBuffer out) {
out.putLong(timestamp);
}
}

@ -0,0 +1,62 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.client.UserInterface;
import com.jotuntech.sketcher.common.User;
public class SayCommand implements Command {
boolean isAction;
String text;
public SayCommand() {
}
public SayCommand(String text, boolean isAction) {
this.isAction = isAction;
this.text = text;
}
public int perform(Client client, User user) {
if(user == null) {
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
if(isAction) {
client.getUserInterface().println("\u2022 " + user.getName() + text);
} else {
client.getUserInterface().println("<" + user.getName() + "> " + text);
}
if(client.isSoundEnabled()) {
UserInterface.AUDIO_CHAT.play();
}
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
isAction = in.get() != 0 ? true : false;
StringBuffer textBuffer = new StringBuffer();
while(in.remaining() >= 2) {
textBuffer.append(in.getChar());
}
text = textBuffer.toString();
}
public void encode(ByteBuffer out) {
out.put((byte) (isAction ? 0xFF : 0x00));
for(int i = 0; i < text.length(); i++) {
out.putChar(text.charAt(i));
}
}
}

@ -0,0 +1,41 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.User;
public class ServerMessageCommand implements Command {
String text;
public ServerMessageCommand() {
}
public ServerMessageCommand(String text) {
this.text = text;
}
public int perform(Client client, User user) {
/** Notify the user */
client.getUserInterface().println(text);
/** Return silently. */
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
StringBuffer textBuffer = new StringBuffer();
while(in.remaining() >= 2) {
textBuffer.append(in.getChar());
}
text = textBuffer.toString();
}
public void encode(ByteBuffer out) {
for(int i = 0; i < text.length(); i++) {
out.putChar(text.charAt(i));
}
}
}

@ -0,0 +1,57 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.client.Editor;
import com.jotuntech.sketcher.common.Brush;
import com.jotuntech.sketcher.common.User;
public class SetBrushCommand implements Command {
Brush brush;
public SetBrushCommand() {
}
public SetBrushCommand(Brush brush) {
this.brush = brush;
}
public int perform(Client client, User user) {
if(user == null) {
/** Signed in users only */
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
Brush oldBrush = user.getBrush();
user.setBrush(brush);
if(user == client.getConnection().getUser()) {
/** Update the GUI */
client.getUserInterface().updateBrushSliders();
if(oldBrush == null) {
/* Enable editor when first brush is selected */
client.getUserInterface().getEditor().setState(Editor.State.DRAW_HOVER);
}
}
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
brush = new Brush();
brush.decode(in);
}
public void encode(ByteBuffer out) {
brush.encode(out);
}
}

@ -0,0 +1,53 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.User;
public class SetColorCommand implements Command {
static boolean firstWhite = true;
int color;
public SetColorCommand() {
}
public SetColorCommand(int color) {
this.color = color;
}
public int perform(Client client, User user) {
if(color == 0xFFFFFF && user == client.getConnection().getUser() && client.getConnection().getUser().getBrush() != null && client.getConnection().getUser().getBrush().getOpacity() > 0 && firstWhite) {
client.getUserInterface().println("Use eraser unless drawing white. :-)");
firstWhite = false;
}
return perform(user) ? Connection.SEND_OTHERS : Connection.SEND_NONE;
}
private boolean perform(User user) {
if(user == null) {
/** Signed in users only */
return false;
}
if(user.isViewer()) {
return false;
}
user.setColor(color);
return true;
}
public void decode(ByteBuffer in) {
color = in.getInt();
}
public void encode(ByteBuffer out) {
out.putInt(color);
}
}

@ -0,0 +1,64 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.Canvas;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.User;
public class SetLayerCommand implements Command {
Integer layerKey;
public SetLayerCommand() {
}
public SetLayerCommand(Integer layerKey) {
this.layerKey = layerKey;
}
public int perform(Client client, User user) {
if(user == null) {
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
Canvas canvas = client.getCanvas();
if(canvas == null) {
return Connection.SEND_NONE;
}
Layer layer = canvas.getLayerMap().get(layerKey);
if(layer == null) {
return Connection.SEND_NONE;
}
if(user.getLayer() == layer) {
return Connection.SEND_NONE;
}
user.setLayer(layer);
if(user == client.getConnection().getUser()) {
client.getUserInterface().updateLayer();
}
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
layerKey = in.getInt();
}
public void encode(ByteBuffer out) {
out.putInt(layerKey);
}
}

@ -0,0 +1,96 @@
/**
*
*/
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.client.UserInterface;
import com.jotuntech.sketcher.common.User;
/**
* @author Thor Harald Johansen
*
*/
public class SignInCommand implements Command {
Integer peerKey;
String login;
String password;
boolean viewer;
public SignInCommand() {
}
public SignInCommand(Integer peerKey, String login, String password, boolean viewer) {
this.peerKey = peerKey;
this.login = login;
this.password = password;
this.viewer = viewer;
}
public void decode(ByteBuffer in) {
peerKey = in.getInt();
viewer = in.get() != 0;
int loginLength = (in.get() & 0xFF) + 1;
StringBuffer loginBuffer = new StringBuffer();
for(int i = 0; i < loginLength; i++) {
loginBuffer.append(in.getChar());
}
login = loginBuffer.toString();
StringBuffer passwordBuffer = new StringBuffer();
while(in.remaining() >= 2) {
passwordBuffer.append(in.getChar());
}
password = passwordBuffer.toString();
}
public void encode(ByteBuffer out) {
out.putInt(peerKey);
out.put((byte) (viewer ? 0xFF : 0x00));
out.put((byte) (login.length() - 1));
for(int i = 0; i < login.length(); i++) {
out.putChar(login.charAt(i));
}
for(int i = 0; i < password.length(); i++) {
out.putChar(password.charAt(i));
}
}
public int perform(Client client, User user) {
/** Create user */
user = new User(login);
/** Set viewer flag */
user.setViewer(viewer);
/** Put user in client user map */
client.getUserMap().put(peerKey, user);
if(client.getConnection().getUser() == null) {
/** Assign user to client */
client.getConnection().setUser(user);
/** Select a default brush */
client.getUserInterface().getBrushButtons()[0].doClick();
}
/** Add canvas as phantom layer listener */
user.getPhantomLayer().addImageObserver(client.getCanvas());
/** Print message */
client.getUserInterface().println(user.getName() + " has signed in");
/** Play audio */
if(client.isSoundEnabled()) {
UserInterface.AUDIO_SIGN_IN.play();
}
/** Return silently */
return Connection.SEND_NONE;
}
}

@ -0,0 +1,49 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.client.UserInterface;
import com.jotuntech.sketcher.common.User;
public class SignOutCommand implements Command {
String message;
public SignOutCommand() {
message = new String();
}
public SignOutCommand(String message) {
this.message = message;
}
public int perform(Client client, User user) {
client.getUserMap().removeByValue(user);
if(user != null) {
client.getUserInterface().println(user.getName() + " has disconnected (" + message + ")");
}
if(client.isSoundEnabled()) {
UserInterface.AUDIO_SIGN_OUT.play();
}
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
StringBuffer messageBuffer = new StringBuffer();
while(in.remaining() >= 2) {
messageBuffer.append(in.getChar());
}
message = messageBuffer.toString();
}
public void encode(ByteBuffer out) {
for(int i = 0; i < message.length(); i++) {
out.putChar(message.charAt(i));
}
}
}

@ -0,0 +1,35 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import java.util.Deque;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.UndoEntry;
import com.jotuntech.sketcher.common.User;
public class UndoCommand implements Command {
public int perform(Client client, User user) {
if(user.isViewer()) {
return Connection.SEND_NONE;
}
Deque<UndoEntry> undoDeque = user.getUndoDeque();
if(undoDeque.size() == 0) {
return Connection.SEND_NONE;
}
UndoEntry undoEntry = undoDeque.removeFirst();
undoEntry.getLayer().undo(undoEntry.getUndoData());
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
}
public void encode(ByteBuffer out) {
}
}

@ -0,0 +1,45 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import java.util.Deque;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.BitmapUndoData;
import com.jotuntech.sketcher.common.UndoEntry;
import com.jotuntech.sketcher.common.User;
public class UndoDataCommand implements Command {
byte[] data;
public int perform(Client client, User user) {
if(user == null) {
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
Deque<UndoEntry> undoDeque = user.getUndoDeque();
if(undoDeque.size() == 0) {
return Connection.SEND_NONE;
}
BitmapUndoData bud = (BitmapUndoData) undoDeque.getLast().getUndoData();
bud.decode(data);
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
data = new byte[in.remaining()];
in.get(data);
}
public void encode(ByteBuffer out) {
out.put(data);
}
}

@ -0,0 +1,46 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.common.BitmapUndoData;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.UndoEntry;
import com.jotuntech.sketcher.common.User;
import com.jotuntech.sketcher.server.Connection;
public class UndoEntryCommand implements Command {
Integer layerKey;
public int perform(Client client, User user) {
if(user == null) {
return Connection.SEND_NONE;
}
if(user.isViewer()) {
return Connection.SEND_NONE;
}
Layer layer = client.getCanvas().getLayerMap().get(layerKey);
if(layer == null) {
return Connection.SEND_NONE;
}
UndoEntry undoEntry = new UndoEntry();
undoEntry.setLayer(layer);
undoEntry.setUndoData(new BitmapUndoData());
user.getUndoDeque().addLast(undoEntry);
return Connection.SEND_NONE;
}
public void decode(ByteBuffer in) {
layerKey = in.getInt();
}
public void encode(ByteBuffer out) {
out.putInt(layerKey);
}
}

@ -0,0 +1,37 @@
package com.jotuntech.sketcher.client.command;
import java.nio.ByteBuffer;
import com.jotuntech.sketcher.client.Client;
import com.jotuntech.sketcher.client.Command;
import com.jotuntech.sketcher.client.Connection;
import com.jotuntech.sketcher.common.User;
public class VoiceCommand implements Command {
boolean voiceEnabled;
public VoiceCommand() {
}
public VoiceCommand(boolean voiceEnabled) {
this.voiceEnabled = voiceEnabled;
}
public int perform(Client client, User user) {
if(user.isViewer()) {
return Connection.SEND_NONE;
}
client.setVoiceEnabled(voiceEnabled);
return Connection.SEND_OTHERS;
}
public void decode(ByteBuffer in) {
voiceEnabled = in.get() != 0;
}
public void encode(ByteBuffer out) {
out.put((byte) (voiceEnabled ? 0xFF : 0x00));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save