Base alpha version with Apple HiRes and Apple Double HiRes support working.

This commit is contained in:
Brendan Robert 2013-08-03 00:03:17 -05:00
parent 4120579385
commit e17604383f
53 changed files with 5641 additions and 0 deletions

5
OutlawEditor/catalog.xml Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
<system systemId="file:/home/brobert/Documents/Personal/LawlessLegends/github/lawless-legends/OutlawEditor/src/main/java/org/badvision/outlaweditor/resources/OutlawSchema.xsd" uri="src/main/resources/jaxb/OutlawSchema/OutlawSchema.xsd"/>
<system systemId="file:/home/brobert/Documents/Personal/LawlessLegends/github/lawless-legends/OutlawEditor/src/main/resources/OutlawSchema.xsd" uri="src/main/resources/jaxb/OutlawSchema/OutlawSchema.xsd"/>
</catalog>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<netbeans.hint.jdkPlatform>JDK_1.7</netbeans.hint.jdkPlatform>
</properties>
</project-shared-configuration>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<actions>
<action>
<actionName>run</actionName>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath org.badvision.outlaweditor.Application</exec.args>
<exec.executable>java</exec.executable>
<exec.classpathScope>compile</exec.classpathScope>
</properties>
</action>
<action>
<actionName>run.single.main</actionName>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath ${packageClassName}</exec.args>
<exec.executable>java</exec.executable>
<exec.classpathScope>compile</exec.classpathScope>
</properties>
</action>
<action>
<actionName>debug</actionName>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath ${packageClassName}</exec.args>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
<exec.classpathScope>compile</exec.classpathScope>
</properties>
</action>
<action>
<actionName>debug.single.main</actionName>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath ${packageClassName}</exec.args>
<exec.executable>java</exec.executable>
<exec.classpathScope>compile</exec.classpathScope>
<jpda.listen>true</jpda.listen>
</properties>
</action>
<action>
<actionName>profile</actionName>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath ${packageClassName}</exec.args>
<exec.executable>java</exec.executable>
<exec.classpathScope>compile</exec.classpathScope>
</properties>
</action>
<action>
<actionName>profile.single.main</actionName>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath ${packageClassName}</exec.args>
<exec.executable>java</exec.executable>
<exec.classpathScope>compile</exec.classpathScope>
</properties>
</action>
</actions>

72
OutlawEditor/pom.xml Normal file
View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.badvision</groupId>
<artifactId>OutlawEditor</artifactId>
<name>OutlawEditor</name>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<organization>
<!-- Used as the 'Vendor' for JNLP generation -->
<name>org.badvision</name>
</organization>
<build>
<finalName>OutlawEditor</finalName>
<plugins>
<plugin>
<groupId>com.zenjava</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>2.0</version>
<configuration>
outlawEditor
<mainClass>org.badvision.outlaweditor.Application</mainClass>
<!-- only required if signing the jar file -->
<keyStoreAlias>example-user</keyStoreAlias>
<keyStorePassword>example-password</keyStorePassword>
<allPermissions>true</allPermissions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
<version>3.1</version>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.3</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<schemaIncludes>
<include>jaxb/OutlawSchema/*.xsd</include>
</schemaIncludes>
<episodeFile>${project.build.directory}/generated-sources/xjc/META-INF/jaxb-OutlawSchema.episode</episodeFile>
<generatePackage>org.badvision.outlaweditor.data.xml</generatePackage>
</configuration>
<id>jaxb-generate-OutlawSchema</id>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>javafx</artifactId>
<version>2</version>
<systemPath>/usr/lib/jvm/jdk1.7.0_21/jre/lib/jfxrt.jar</systemPath>
<!--<systemPath>${java.home}/lib/jfxrt.jar</systemPath>-->
<scope>system</scope>
</dependency>
</dependencies>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -0,0 +1,78 @@
package org.badvision.outlaweditor;
import java.io.IOException;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import static org.badvision.outlaweditor.UIAction.*;
import org.badvision.outlaweditor.data.xml.GameData;
/**
*
* @author brobert
*/
public class Application extends javafx.application.Application {
public static GameData gameData = new GameData();
public static Platform currentPlatform = Platform.AppleII;
static Application instance;
public Stage primaryStage;
ApplicationUIControllerImpl controller;
public static Stage getPrimaryStage() {
return instance.primaryStage;
}
@Override
public void start(Stage primaryStage) {
instance = this;
this.primaryStage = primaryStage;
javafx.application.Platform.setImplicitExit(true);
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/ApplicationUI.fxml"));
fxmlLoader.setResources(null);
try {
AnchorPane node = (AnchorPane) fxmlLoader.load();
controller = fxmlLoader.getController();
Scene s = new Scene(node);
primaryStage.setScene(s);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(final WindowEvent t) {
t.consume();
if (quit()) {
javafx.application.Platform.exit();
// System.exit(0);
}
javafx.application.Platform.runLater(new Runnable() {
@Override
public void run() {
getPrimaryStage().show();
}
});
}
});
primaryStage.show();
}
Canvas tilePreview;
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,327 @@
package org.badvision.outlaweditor;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.control.Menu;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import org.badvision.outlaweditor.data.xml.Image;
import org.badvision.outlaweditor.data.xml.Map;
import org.badvision.outlaweditor.data.xml.Script;
import org.badvision.outlaweditor.data.xml.Tile;
public abstract class ApplicationUIController {
@FXML // ResourceBundle that was given to the FXMLLoader
protected ResourceBundle resources;
@FXML // URL location of the FXML file that was given to the FXMLLoader
protected URL location;
@FXML // fx:id="imageCategoryField"
protected TextField imageCategoryField; // Value injected by FXMLLoader
@FXML // fx:id="imageEditorAnchorPane"
protected AnchorPane imageEditorAnchorPane; // Value injected by FXMLLoader
@FXML // fx:id="imageHeightField"
protected TextField imageHeightField; // Value injected by FXMLLoader
@FXML // fx:id="imageNameField"
protected TextField imageNameField; // Value injected by FXMLLoader
@FXML // fx:id="imagePatternMenu"
protected Menu imagePatternMenu; // Value injected by FXMLLoader
@FXML // fx:id="imageSelector"
protected ComboBox<Image> imageSelector; // Value injected by FXMLLoader
@FXML // fx:id="imageWidthField"
protected TextField imageWidthField; // Value injected by FXMLLoader
@FXML // fx:id="mapEditorAnchorPane"
protected AnchorPane mapEditorAnchorPane; // Value injected by FXMLLoader
@FXML // fx:id="mapHeightField"
protected TextField mapHeightField; // Value injected by FXMLLoader
@FXML // fx:id="mapNameField"
protected TextField mapNameField; // Value injected by FXMLLoader
@FXML // fx:id="mapScriptsList"
protected ListView<Script> mapScriptsList; // Value injected by FXMLLoader
@FXML // fx:id="mapSelect"
protected ComboBox<Map> mapSelect; // Value injected by FXMLLoader
@FXML
protected Menu mapSelectTile;
@FXML // fx:id="mapWidthField"
protected TextField mapWidthField; // Value injected by FXMLLoader
@FXML // fx:id="mapWrapAround"
protected CheckBox mapWrapAround; // Value injected by FXMLLoader
@FXML // fx:id="tileCategoryField"
protected TextField tileCategoryField; // Value injected by FXMLLoader
@FXML // fx:id="tileEditorAnchorPane"
protected AnchorPane tileEditorAnchorPane; // Value injected by FXMLLoader
@FXML // fx:id="tileIdField"
protected TextField tileIdField; // Value injected by FXMLLoader
@FXML // fx:id="tileNameField"
protected TextField tileNameField; // Value injected by FXMLLoader
@FXML // fx:id="tileObstructionField"
protected CheckBox tileObstructionField; // Value injected by FXMLLoader
@FXML // fx:id="tilePatternMenu"
protected Menu tilePatternMenu; // Value injected by FXMLLoader
@FXML // fx:id="tileSelector"
protected ComboBox<Tile> tileSelector; // Value injected by FXMLLoader
// Handler for MenuItem[javafx.scene.control.MenuItem@3a4bc91a] onAction
@FXML
abstract public void imageBitMode(ActionEvent event);
// Handler for MenuItem[javafx.scene.control.MenuItem@2fd6cf0] onAction
@FXML
abstract public void imageDraw1BitMode(ActionEvent event);
// Handler for MenuItem[javafx.scene.control.MenuItem@2fd4f37f] onAction
@FXML
abstract public void imageDraw3BitMode(ActionEvent event);
// Handler for MenuItem[javafx.scene.control.MenuItem@73091451] onAction
@FXML
abstract public void imageDrawFilledRectMode(ActionEvent event);
// Handler for MenuItem[javafx.scene.control.MenuItem@5768f863] onAction
@FXML
abstract public void imageShift(ActionEvent event);
@FXML
abstract public void imageTabActivated(Event event);
// Handler for MenuItem[javafx.scene.control.MenuItem@547638c0] onAction
@FXML
abstract public void imageTogglePanZoom(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button zoomInButton]] onAction
@FXML
abstract public void imageZoomIn(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button zoomOutButton]] onAction
@FXML
abstract public void imageZoomOut(ActionEvent event);
@FXML
abstract public void mapDraw1(ActionEvent event);
@FXML
abstract public void mapDraw3(ActionEvent event);
@FXML
abstract public void mapDraw5(ActionEvent event);
@FXML
abstract public void mapDrawFilledRectMode(ActionEvent event);
@FXML
abstract public void mapTabActivated(Event event);
@FXML
abstract public void mapTogglePanZoom(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button zoomInButton]] onAction
@FXML
abstract public void mapZoomIn(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button zoomOutButton]] onAction
@FXML
abstract public void mapZoomOut(ActionEvent event);
// Handler for ComboBox[fx:id="tileSelector"] onAction
@FXML
abstract public void onCurrentTileSelected(ActionEvent event);
@FXML
abstract public void onChangePlatformAppleSolid(ActionEvent event);
@FXML
abstract public void onChangePlatformAppleText(ActionEvent event);
@FXML
abstract public void onChangePlatformAppleDHGRSolid(ActionEvent event);
@FXML
abstract public void onChangePlatformAppleDHGRText(ActionEvent event);
@FXML
abstract public void onChangePlatformC64(ActionEvent event);
@FXML
abstract public void onEditCopy(ActionEvent event);
@FXML
abstract public void onEditPaste(ActionEvent event);
@FXML
abstract public void onEditSelect(ActionEvent event);
@FXML
abstract public void onFileOpen(ActionEvent event);
@FXML
abstract public void onFileQuit(ActionEvent event);
@FXML
abstract public void onFileSave(ActionEvent event);
@FXML
abstract public void onFileSaveAs(ActionEvent event);
@FXML
abstract public void onHelpAbout(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onImageClonePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onImageCreatePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onImageDeletePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onImageExportPressed(ActionEvent event);
// Handler for ComboBox[fx:id="imageSelector"] onAction
@FXML
abstract public void onImageSelected(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onMapClonePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onMapCreatePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onMapDeletePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onMapExportPressed(ActionEvent event);
@FXML
abstract public void onMapPreviewPressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onMapScriptAddPressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onMapScriptClonePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onMapScriptDeletePressed(ActionEvent event);
// Handler for ComboBox[id="tileSelect"] onAction
@FXML
abstract public void onMapSelected(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onTileClonePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onTileCreatePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onTileDeletePressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button]] onAction
@FXML
abstract public void onTileExportPressed(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollImageDown(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollImageLeft(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollImageRight(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollImageUp(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollMapDown(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollMapLeft(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollMapRight(ActionEvent event);
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
@FXML
abstract public void scrollMapUp(ActionEvent event);
// Handler for MenuItem[javafx.scene.control.MenuItem@3b007e44] onAction
@FXML
abstract public void tileBitMode(ActionEvent event);
// Handler for MenuItem[javafx.scene.control.MenuItem@4771c0b8] onAction
@FXML
abstract public void tileDraw1BitMode(ActionEvent event);
// Handler for MenuItem[javafx.scene.control.MenuItem@766bd19d] onAction
@FXML
abstract public void tileDraw3BitMode(ActionEvent event);
@FXML
abstract public void imageDraw5BitMode(ActionEvent event);
@FXML
abstract public void tileTabActivated(Event event);
// Handler for MenuItem[javafx.scene.control.MenuItem@622410f1] onAction
@FXML
abstract public void tileShift(ActionEvent event);
@FXML // This method is called by the FXMLLoader when initialization is complete
public void initialize() {
assert imageCategoryField != null : "fx:id=\"imageCategoryField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert imageEditorAnchorPane != null : "fx:id=\"imageEditorAnchorPane\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert imageHeightField != null : "fx:id=\"imageHeightField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert imageNameField != null : "fx:id=\"imageNameField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert imagePatternMenu != null : "fx:id=\"imagePatternMenu\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert imageSelector != null : "fx:id=\"imageSelector\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert imageWidthField != null : "fx:id=\"imageWidthField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapEditorAnchorPane != null : "fx:id=\"mapEditorAnchorPane\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapHeightField != null : "fx:id=\"mapHeightField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapNameField != null : "fx:id=\"mapNameField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapScriptsList != null : "fx:id=\"mapScriptsList\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapSelect != null : "fx:id=\"mapSelect\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapSelectTile != null : "fx:id=\"mapSelectTile\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapWidthField != null : "fx:id=\"mapWidthField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert mapWrapAround != null : "fx:id=\"mapWrapAround\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert tileCategoryField != null : "fx:id=\"tileCategoryField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert tileEditorAnchorPane != null : "fx:id=\"tileEditorAnchorPane\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert tileIdField != null : "fx:id=\"tileIdField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert tileNameField != null : "fx:id=\"tileNameField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert tileObstructionField != null : "fx:id=\"tileObstructionField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert tilePatternMenu != null : "fx:id=\"tilePatternMenu\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
assert tileSelector != null : "fx:id=\"tileSelector\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
// Initialize your logic here: all @FXML variables will have been injected
}
}

View File

@ -0,0 +1,835 @@
package org.badvision.outlaweditor;
import org.badvision.outlaweditor.apple.AppleTileRenderer;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.ComboBoxListCell;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.util.Callback;
import static org.badvision.outlaweditor.Application.currentPlatform;
import static org.badvision.outlaweditor.Application.gameData;
import static org.badvision.outlaweditor.data.PropertyHelper.*;
import org.badvision.outlaweditor.data.TileUtils;
import org.badvision.outlaweditor.data.TilesetUtils;
import org.badvision.outlaweditor.data.xml.Image;
import org.badvision.outlaweditor.data.xml.PlatformData;
import org.badvision.outlaweditor.data.xml.Tile;
/**
* Actual implementation of Application UI, isolated from auto-generated code
*
* @author brobert
*/
public class ApplicationUIControllerImpl extends ApplicationUIController {
public Tile currentTile = null;
public TileEditor currentTileEditor = null;
public org.badvision.outlaweditor.data.xml.Map currentMap = null;
public MapEditor currentMapEditor = null;
public Image currentImage = null;
public ImageEditor currentImageEditor = null;
@Override
public void initialize() {
super.initialize();
TilesetUtils.addObserver(new org.badvision.outlaweditor.data.DataObserver() {
@Override
public void observedObjectChanged(Object object) {
rebuildTileSelectors();
}
});
tileSelector.setButtonCell(new ComboBoxListCell<Tile>() {
{
super.setPrefWidth(125);
}
@Override
public void updateItem(Tile item, boolean empty) {
textProperty().unbind();
super.updateItem(item, empty);
if (item != null) {
textProperty().bind(tileNameField.textProperty());
} else {
setText(null);
}
}
});
tileSelector.setCellFactory(new Callback<ListView<Tile>, ListCell<Tile>>() {
@Override
public ListCell<Tile> call(ListView<Tile> param) {
return new EntitySelectorCell<Tile>(tileNameField) {
@Override
public void finishUpdate(Tile item) {
setGraphic(new ImageView(TileUtils.getImage(item, Application.currentPlatform)));
}
};
}
});
mapSelect.setButtonCell(new ComboBoxListCell<org.badvision.outlaweditor.data.xml.Map>() {
{
super.setPrefWidth(125);
}
@Override
public void updateItem(org.badvision.outlaweditor.data.xml.Map item, boolean empty) {
textProperty().unbind();
super.updateItem(item, empty);
if (item != null) {
textProperty().bind(mapNameField.textProperty());
} else {
setText(null);
}
}
});
mapSelect.setCellFactory(new Callback<ListView<org.badvision.outlaweditor.data.xml.Map>, ListCell<org.badvision.outlaweditor.data.xml.Map>>() {
@Override
public ListCell<org.badvision.outlaweditor.data.xml.Map> call(ListView<org.badvision.outlaweditor.data.xml.Map> param) {
return new EntitySelectorCell<org.badvision.outlaweditor.data.xml.Map>(mapNameField) {
@Override
public void finishUpdate(org.badvision.outlaweditor.data.xml.Map item) {
}
};
}
});
imageSelector.setButtonCell(new ComboBoxListCell<Image>() {
{
super.setPrefWidth(125);
}
@Override
public void updateItem(Image item, boolean empty) {
textProperty().unbind();
super.updateItem(item, empty);
if (item != null) {
textProperty().bind(imageNameField.textProperty());
} else {
setText(null);
}
}
});
imageSelector.setCellFactory(new Callback<ListView<Image>, ListCell<Image>>() {
@Override
public ListCell<Image> call(ListView<Image> param) {
return new EntitySelectorCell<Image>(imageNameField) {
@Override
public void finishUpdate(Image item) {
}
};
}
});
}
@Override
public void imageBitMode(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Toggle);
}
}
@Override
public void imageDraw1BitMode(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Pencil1px);
}
}
@Override
public void imageDraw3BitMode(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Pencil3px);
}
}
@Override
public void imageDraw5BitMode(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Pencil5px);
}
}
@Override
public void imageDrawFilledRectMode(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Rectangle);
}
}
@Override
public void imageShift(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.showShiftUI();
}
}
@Override
public void imageTogglePanZoom(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.togglePanZoom();
}
}
@Override
public void imageZoomIn(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.zoomIn();
}
}
@Override
public void imageZoomOut(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.zoomOut();
}
}
@Override
public void mapDraw1(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.setDrawMode(MapEditor.DrawMode.Pencil1px);
}
}
@Override
public void mapDraw3(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.setDrawMode(MapEditor.DrawMode.Pencil3px);
}
}
@Override
public void mapDraw5(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.setDrawMode(MapEditor.DrawMode.Pencil5px);
}
}
@Override
public void mapDrawFilledRectMode(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.setDrawMode(MapEditor.DrawMode.FilledRect);
}
}
@Override
public void mapTogglePanZoom(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.togglePanZoom();
}
}
@Override
public void mapZoomIn(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.zoomIn();
}
}
@Override
public void mapZoomOut(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.zoomOut();
}
}
@Override
public void onCurrentTileSelected(ActionEvent event) {
setCurrentTile(tileSelector.getSelectionModel().getSelectedItem());
}
@Override
public void onChangePlatformAppleSolid(ActionEvent event) {
AppleTileRenderer.useSolidPalette = true;
Application.currentPlatform = Platform.AppleII;
platformChange();
}
@Override
public void onChangePlatformAppleText(ActionEvent event) {
AppleTileRenderer.useSolidPalette = false;
Application.currentPlatform = Platform.AppleII;
platformChange();
}
@Override
public void onChangePlatformAppleDHGRSolid(ActionEvent event) {
AppleTileRenderer.useSolidPalette = true;
Application.currentPlatform = Platform.AppleII_DHGR;
platformChange();
}
@Override
public void onChangePlatformAppleDHGRText(ActionEvent event) {
AppleTileRenderer.useSolidPalette = false;
Application.currentPlatform = Platform.AppleII_DHGR;
platformChange();
}
private void platformChange() {
for (Tile t : Application.gameData.getTile()) {
TileUtils.redrawTile(t);
}
Tile tile = currentTile;
rebuildTileSelectors();
setCurrentTile(tile);
if (currentMapEditor != null) {
currentMapEditor.redraw();
}
if (currentImageEditor != null) {
currentImageEditor.redraw();
}
}
@Override
public void onChangePlatformC64(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onEditCopy(ActionEvent event) {
if (getVisibleEditor() != null) {
getVisibleEditor().copy();
}
}
@Override
public void onEditPaste(ActionEvent event) {
if (getVisibleEditor() != null) {
getVisibleEditor().paste();
}
}
@Override
public void onEditSelect(ActionEvent event) {
if (getVisibleEditor() != null) {
getVisibleEditor().select();
}
}
@Override
public void onFileOpen(ActionEvent event) {
try {
UIAction.actionPerformed(UIAction.MAIN_ACTIONS.Load);
rebuildImageSelector();
rebuildMapSelectors();
rebuildTileSelectors();
} catch (IOException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public void onFileQuit(ActionEvent event) {
UIAction.quit();
}
@Override
public void onFileSave(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.currentMap.updateBackingMap();
}
try {
UIAction.actionPerformed(UIAction.MAIN_ACTIONS.Save);
} catch (IOException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public void onFileSaveAs(ActionEvent event) {
try {
UIAction.actionPerformed(UIAction.MAIN_ACTIONS.Save_as);
} catch (IOException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public void onHelpAbout(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onImageClonePressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onImageCreatePressed(ActionEvent event) {
Image i = new Image();
i.setName("Untitled");
Application.gameData.getImage().add(i);
setCurrentImage(i);
rebuildImageSelector();
}
@Override
public void onImageDeletePressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onImageExportPressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onImageSelected(ActionEvent event) {
setCurrentImage(imageSelector.getSelectionModel().getSelectedItem());
}
@Override
public void onMapClonePressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onMapCreatePressed(ActionEvent event) {
org.badvision.outlaweditor.data.xml.Map m = new org.badvision.outlaweditor.data.xml.Map();
m.setName("Untitled");
gameData.getMap().add(m);
m.setWidth(512);
m.setHeight(512);
setCurrentMap(m);
rebuildMapSelectors();
}
@Override
public void onMapDeletePressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onMapExportPressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onMapScriptAddPressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onMapPreviewPressed(ActionEvent event) {
if (currentMapEditor == null) {
return;
}
currentMapEditor.showPreview();
}
@Override
public void onMapScriptClonePressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onMapScriptDeletePressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onMapSelected(ActionEvent event) {
setCurrentMap(mapSelect.getSelectionModel().getSelectedItem());
}
@Override
public void onTileClonePressed(ActionEvent event) {
if (currentTile == null) {
return;
}
Tile t = new Tile();
TileUtils.getId(t);
t.setName(currentTile.getName() + " (clone)");
t.setObstruction(currentTile.isObstruction());
t.getCategory().addAll(currentTile.getCategory());
for (PlatformData d : currentTile.getDisplayData()) {
PlatformData p = new PlatformData();
p.setHeight(d.getHeight());
p.setWidth(d.getWidth());
p.setPlatform(d.getPlatform());
p.setValue(Arrays.copyOf(d.getValue(), d.getValue().length));
t.getDisplayData().add(p);
}
TilesetUtils.add(t);
rebuildTileSelectors();
setCurrentTile(t);
}
@Override
public void onTileCreatePressed(ActionEvent event) {
Tile t = TileUtils.newTile();
t.setName("Untitled");
TilesetUtils.add(t);
rebuildTileSelectors();
setCurrentTile(t);
}
@Override
public void onTileDeletePressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void onTileExportPressed(ActionEvent event) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void scrollImageDown(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.scrollBy(0, 1);
}
}
@Override
public void scrollImageLeft(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.scrollBy(-1, 0);
}
}
@Override
public void scrollImageRight(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.scrollBy(1, 0);
}
}
@Override
public void scrollImageUp(ActionEvent event) {
if (currentImageEditor != null) {
currentImageEditor.scrollBy(0, -1);
}
}
@Override
public void scrollMapDown(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.scrollBy(0, 1);
}
}
@Override
public void scrollMapLeft(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.scrollBy(-1, 0);
}
}
@Override
public void scrollMapRight(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.scrollBy(1, 0);
}
}
@Override
public void scrollMapUp(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.scrollBy(0, -1);
}
}
@Override
public void tileBitMode(ActionEvent event) {
if (currentTileEditor != null) {
currentTileEditor.setDrawMode(TileEditor.DrawMode.Toggle);
}
}
@Override
public void tileDraw1BitMode(ActionEvent event) {
if (currentTileEditor != null) {
currentTileEditor.setDrawMode(TileEditor.DrawMode.Pencil1px);
}
}
@Override
public void tileDraw3BitMode(ActionEvent event) {
if (currentTileEditor != null) {
currentTileEditor.setDrawMode(TileEditor.DrawMode.Pencil3px);
}
}
@Override
public void tileShift(ActionEvent event) {
if (currentTileEditor != null) {
currentTileEditor.showShiftUI();
}
}
private void setCurrentTileEditor(TileEditor editor) {
if (editor != null) {
editor.buildEditorUI(tileEditorAnchorPane);
editor.buildPatternSelector(tilePatternMenu);
}
currentTileEditor = editor;
}
public Tile getCurrentTile() {
return currentTile;
}
public void setCurrentTile(Tile t) {
tileSelector.getSelectionModel().select(t);
if (t != null && t.equals(currentTile)) {
return;
}
tileEditorAnchorPane.getChildren().clear();
if (t == null) {
bind(tileIdField.textProperty(), null);
bind(tileCategoryField.textProperty(), null);
bind(tileObstructionField.selectedProperty(), null);
bind(tileNameField.textProperty(), null);
tileIdField.setDisable(true);
tileCategoryField.setDisable(true);
tileObstructionField.setDisable(true);
tileNameField.setDisable(true);
setCurrentTileEditor(null);
} else {
if (t.isObstruction() == null) {
t.setObstruction(false);
}
try {
tileIdField.setDisable(false);
tileCategoryField.setDisable(false);
tileObstructionField.setDisable(false);
tileNameField.setDisable(false);
bind(tileIdField.textProperty(), stringProp(t, "id"));
bind(tileCategoryField.textProperty(), categoryProp(t, "category"));
bind(tileObstructionField.selectedProperty(), boolProp(t, "obstruction"));
bind(tileNameField.textProperty(), stringProp(t, "name"));
TileEditor editor = Application.currentPlatform.tileEditor.newInstance();
editor.setEntity(t);
setCurrentTileEditor(editor);
} catch (NoSuchMethodException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
currentTile = t;
}
public void rebuildTileSelectors() {
tileSelector.getItems().clear();
tileSelector.getItems().addAll(Application.gameData.getTile());
tileSelector.getSelectionModel().select(getCurrentTile());
mapSelectTile.getItems().clear();
for (final Tile t : Application.gameData.getTile()) {
WritableImage img = TileUtils.getImage(t, currentPlatform);
ImageView iv = new ImageView(img);
MenuItem mapSelectItem = new MenuItem(t.getName(), iv);
mapSelectItem.setGraphic(new ImageView(TileUtils.getImage(t, currentPlatform)));
mapSelectItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (currentMapEditor != null) {
currentMapEditor.setCurrentTile(t);
}
}
});
mapSelectTile.getItems().add(mapSelectItem);
}
}
public void setCurrentMap(org.badvision.outlaweditor.data.xml.Map m) {
if (currentMap != null && currentMap.equals(m)) {
return;
}
// mapEditorAnchorPane.getChildren().clear();
currentMap = m;
if (currentMapEditor != null) {
currentMapEditor.unregister();
}
if (m == null) {
bind(mapHeightField.textProperty(), null);
bind(mapNameField.textProperty(), null);
bind(mapWidthField.textProperty(), null);
bind(mapWrapAround.selectedProperty(), null);
mapHeightField.setDisable(true);
mapNameField.setDisable(true);
mapWidthField.setDisable(true);
mapWrapAround.setDisable(true);
currentMapEditor = null;
} else {
if (m.getHeight() == null) {
m.setHeight(512);
}
if (m.getWidth() == null) {
m.setWidth(512);
}
if (m.getName() == null) {
m.setName("Untitled");
}
try {
mapHeightField.setDisable(false);
mapNameField.setDisable(false);
mapWidthField.setDisable(false);
mapWrapAround.setDisable(false);
// bind(mapHeightField.textProperty(), intProp(m, "height"));
bind(mapNameField.textProperty(), stringProp(m, "name"));
// bind(mapWidthField.textProperty(), intProp(m, "width"));
// bind(mapWrapAround.selectedProperty(),boolProp(m, "wrap"));
} catch (NoSuchMethodException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
currentMapEditor = new MapEditor();
currentMapEditor.setEntity(m);
currentMapEditor.buildEditorUI(mapEditorAnchorPane);
}
}
public void rebuildMapSelectors() {
mapSelect.getItems().clear();
mapSelect.getItems().addAll(Application.gameData.getMap());
mapSelect.getSelectionModel().select(getCurrentMap());
}
public org.badvision.outlaweditor.data.xml.Map getCurrentMap() {
return currentMap;
}
private void setCurrentImage(Image i) {
if (currentImage != null && currentImage.equals(i)) {
return;
}
currentImage = i;
if (currentImageEditor != null) {
currentImageEditor.unregister();
}
if (i == null) {
bind(imageCategoryField.textProperty(), null);
// bind(imageHeightField.textProperty(), null);
bind(imageNameField.textProperty(), null);
// bind(imageWidthField.textProperty(),null);
imageCategoryField.setDisable(true);
imageHeightField.setDisable(true);
imageNameField.setDisable(true);
imageWidthField.setDisable(true);
currentImageEditor = null;
} else {
if (i.getName() == null) {
i.setName("Untitled");
}
try {
imageCategoryField.setDisable(false);
imageHeightField.setDisable(false);
imageNameField.setDisable(false);
imageWidthField.setDisable(false);
bind(imageNameField.textProperty(), stringProp(i, "name"));
bind(imageCategoryField.textProperty(), categoryProp(i, "category"));
} catch (NoSuchMethodException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
try {
currentImageEditor = currentPlatform.imageEditor.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
currentImageEditor.setEntity(i);
currentImageEditor.buildEditorUI(imageEditorAnchorPane);
currentImageEditor.buildPatternSelector(imagePatternMenu);
}
}
private Image getCurrentImage() {
return currentImage;
}
private void rebuildImageSelector() {
imageSelector.getItems().clear();
imageSelector.getItems().addAll(Application.gameData.getImage());
imageSelector.getSelectionModel().select(getCurrentImage());
}
public static enum TABS{image,map,tile};
TABS currentTab;
@Override
public void imageTabActivated(Event event) {
currentTab = TABS.image;
}
@Override
public void mapTabActivated(Event event) {
currentTab = TABS.map;
}
@Override
public void tileTabActivated(Event event) {
currentTab = TABS.tile;
}
public Editor getVisibleEditor() {
switch (currentTab) {
case image:
return currentImageEditor;
case map:
return currentMapEditor;
case tile:
return currentTileEditor;
}
return null;
}
abstract public static class EntitySelectorCell<T> extends ComboBoxListCell<T> {
static Map<TextField, Object> lastSelected = new HashMap<>();
TextField nameField;
public EntitySelectorCell(TextField tileNameField) {
super.setPrefWidth(125);
nameField = tileNameField;
}
@Override
public void updateSelected(boolean sel) {
if (sel) {
Object o = lastSelected.get(nameField);
if (o != null && !o.equals(getItem())) {
((ListCell) o).updateSelected(false);
}
textProperty().unbind();
textProperty().bind(nameField.textProperty());
lastSelected.put(nameField, this);
} else {
updateItem(getItem(), false);
}
}
@Override
public void updateItem(T item, boolean empty) {
textProperty().unbind();
super.updateItem(item, empty);
if (item != null && !(item instanceof String)) {
try {
textProperty().bind(stringProp(item, "name"));
} catch (NoSuchMethodException ex) {
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
finishUpdate(item);
} else {
setText(null);
}
}
public void finishUpdate(T item) {
}
};
}

View File

@ -0,0 +1,56 @@
package org.badvision.outlaweditor;
import javafx.scene.layout.AnchorPane;
import org.badvision.outlaweditor.data.DataObserver;
import org.badvision.outlaweditor.data.DataProducer;
/**
* Extremely generic editor abstraction -- useful for uniform edit features across application
* @author brobert
*/
public abstract class Editor<T,D> implements DataObserver<T> {
T editEntity;
public void setEntity(T t) {
editEntity = t;
DataProducer.addObserver(t, this);
}
public T getEntity() {
return editEntity;
}
abstract public void setDrawMode(D drawMode);
abstract public void showShiftUI();
abstract public void buildEditorUI(AnchorPane tileEditorAnchorPane);
abstract public void unregister();
abstract public void copy();
abstract public void paste();
abstract public void select();
abstract public void selectNone();
int startX = 0;
int startY = 0;
int endX = 0;
int endY = 0;
public void setSelectionArea(int x1, int y1, int x2, int y2) {
startX = Math.min(x1, x2);
startY = Math.min(y1, y2);
endX = Math.max(x1, x2);
endY = Math.max(y1, y2);
if (startX + startY + endX + endY == 0) {
selectInfo = null;
} else {
selectInfo="x1/"+startX+"/y1/"+startY+"/x2/"+endX+"/y2/"+endY;
}
}
public String getSelectedAllInfo() {
return "all";
}
String selectInfo;
public String getSelectionInfo() {
if (selectInfo == null) {
return getSelectedAllInfo();
}
return selectInfo;
};
}

View File

@ -0,0 +1,148 @@
package org.badvision.outlaweditor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import javafx.stage.FileChooser;
/**
*
* @author brobert
*/
public class FileUtils {
public static enum Extension {
XML("XML files", "xml"),
TILESET("Tileset", "ots"),
ALL("All files", "*");
String description;
String extension;
Extension(String desc, String ext) {
this.description = desc;
this.extension = ext;
}
public FileChooser.ExtensionFilter getExtensionFilter() {
return new FileChooser.ExtensionFilter(description + " (*." + extension + ")", "*." + extension);
}
}
public static File getFile(File prevFile, String title, Boolean create, Extension... supportedExtensions) {
FileChooser f = new FileChooser();
if (prevFile != null) {
if (prevFile.isFile()) {
f.setInitialDirectory(prevFile.getParentFile());
} else {
f.setInitialDirectory(prevFile);
}
}
f.setTitle(title);
for (Extension e : supportedExtensions) {
f.getExtensionFilters().add(e.getExtensionFilter());
}
if (create) {
File file = f.showSaveDialog(Application.getPrimaryStage());
if (!file.getName().contains(".")) {
return new File(file.getParentFile(), file.getName()+"."+supportedExtensions[0].extension);
} else {
return file;
}
} else {
return f.showOpenDialog(Application.getPrimaryStage());
}
}
public static <T> T loadFromFile(File f, Class<T> clazz) throws FileNotFoundException, IOException, ClassNotFoundException {
if (!f.isFile() || !f.exists()) {
return null;
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] b = new byte[1024];
try (FileInputStream in = new FileInputStream(f)) {
int read;
while ((read = in.read(b)) > 0) {
buffer.write(b, 0, read);
}
byte[] decompressed = decompress(buffer.toByteArray());
return byteToObject(decompressed);
}
}
public static void saveToFile(File f, Object obj) throws IOException {
try (FileOutputStream fis = new FileOutputStream(f, false)) {
fis.write(compress(objectToByte(obj)));
fis.flush();
fis.close();
}
}
public static <T> T byteToObject(byte[] data) throws IOException, ClassNotFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is = new ObjectInputStream(in);
return (T) is.readObject();
}
public static byte[] objectToByte(Object o) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ObjectOutputStream os = new ObjectOutputStream(out)) {
os.writeObject(o);
os.flush();
os.close();
}
return out.toByteArray();
}
public static byte[] compress(byte[] bytesToCompress) {
Deflater deflater = new Deflater();
deflater.setInput(bytesToCompress);
deflater.finish();
byte[] bytesCompressed = new byte[Short.MAX_VALUE];
int numberOfBytesAfterCompression = deflater.deflate(bytesCompressed);
byte[] returnValues = new byte[numberOfBytesAfterCompression];
System.arraycopy(
bytesCompressed,
0,
returnValues,
0,
numberOfBytesAfterCompression);
return returnValues;
}
public static byte[] decompress(byte[] bytesToDecompress) {
int numberOfBytesToDecompress = bytesToDecompress.length;
try {
Inflater inflater = new Inflater();
inflater.setInput(
bytesToDecompress,
0,
numberOfBytesToDecompress);
int compressionFactorMaxLikely = 3;
int bufferSizeInBytes = numberOfBytesToDecompress * compressionFactorMaxLikely;
byte[] bytesDecompressed = new byte[bufferSizeInBytes];
int numberOfBytesAfterDecompression = inflater.inflate(bytesDecompressed);
byte[] returnValues = new byte[numberOfBytesAfterDecompression];
System.arraycopy(
bytesDecompressed,
0,
returnValues,
0,
numberOfBytesAfterDecompression);
inflater.end();
return returnValues;
} catch (DataFormatException dfe) {
dfe.printStackTrace(System.err);
}
return null;
}
}

View File

@ -0,0 +1,30 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor;
import javafx.scene.control.Menu;
import org.badvision.outlaweditor.data.xml.Image;
/**
*
* @author brobert
*/
public abstract class ImageEditor extends Editor<Image, ImageEditor.DrawMode> {
public static enum DrawMode {
Toggle, Pencil1px, Pencil3px, Pencil5px, Rectangle, Circle, Stamp
}
abstract public void buildPatternSelector(Menu tilePatternMenu);
abstract public void redraw();
public abstract void scrollBy(int deltaX, int deltaY);
public abstract void togglePanZoom();
public abstract void zoomIn();
public abstract void zoomOut();
}

View File

@ -0,0 +1,25 @@
package org.badvision.outlaweditor;
import javafx.scene.image.WritableImage;
import org.badvision.outlaweditor.data.TileMap;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author brobert
*/
public abstract class ImageRenderer {
public abstract WritableImage renderImage(WritableImage img, byte[] rawImage);
public abstract byte[] createImageBuffer();
public abstract WritableImage renderPreview(TileMap map, int startX, int startY);
public abstract WritableImage renderScanline(WritableImage currentImage, int y, byte[] imageData);
}

View File

@ -0,0 +1,426 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.ImageCursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import org.badvision.outlaweditor.data.TileMap;
import org.badvision.outlaweditor.data.TileUtils;
import org.badvision.outlaweditor.data.xml.Map;
import org.badvision.outlaweditor.data.xml.Tile;
import static org.badvision.outlaweditor.Application.currentPlatform;
/**
*
* @author brobert
*/
public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventHandler<MouseEvent> {
AnchorPane anchorPane;
Canvas drawCanvas;
private Tile currentTile;
int posX = 0;
int posY = 0;
double zoom = 1.0;
DrawMode drawMode = DrawMode.Pencil1px;
TileMap currentMap;
double tileWidth = currentPlatform.tileRenderer.getWidth() * zoom;
double tileHeight = currentPlatform.tileRenderer.getHeight() * zoom;
@Override
public void setEntity(Map t) {
super.setEntity(t);
currentMap = new TileMap(t);
}
EventHandler<ScrollEvent> scrollHandler = new EventHandler<ScrollEvent>() {
@Override
public void handle(ScrollEvent t) {
if (t.isShiftDown()) {
t.consume();
if (t.getDeltaY() > 0) {
zoomIn();
} else {
zoomOut();
}
}
}
};
@Override
public void setDrawMode(DrawMode drawMode) {
this.drawMode = drawMode;
}
@Override
public void showShiftUI() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void buildEditorUI(AnchorPane tileEditorAnchorPane) {
anchorPane = tileEditorAnchorPane;
initCanvas();
redraw();
}
public void initCanvas() {
if (drawCanvas != null) {
anchorPane.getChildren().remove(drawCanvas);
}
drawCanvas = new Canvas();
drawCanvas.heightProperty().bind(Application.getPrimaryStage().heightProperty().subtract(120));
drawCanvas.widthProperty().bind(Application.getPrimaryStage().widthProperty().subtract(200));
// drawCanvas.widthProperty().bind(anchorPane.widthProperty());
drawCanvas.widthProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
redraw();
}
});
drawCanvas.heightProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
redraw();
}
});
drawCanvas.addEventFilter(ScrollEvent.ANY, scrollHandler);
drawCanvas.setOnMousePressed(this);
drawCanvas.setOnMouseDragged(this);
drawCanvas.setOnMouseDragReleased(this);
drawCanvas.setOnMouseReleased(this);
anchorPane.getChildren().add(0, drawCanvas);
}
public void togglePanZoom() {
for (Node n : anchorPane.getChildren()) {
if (n == drawCanvas) {
continue;
}
n.setVisible(!n.isVisible());
}
}
public void scrollBy(int deltaX, int deltaY) {
posX += deltaX * (drawCanvas.getWidth() / tileWidth / 2);
posY += deltaY * (drawCanvas.getHeight() / tileWidth / 2);
posX = Math.max(0, posX);
posY = Math.max(0, posY);
redraw();
}
public void zoomOut() {
if (zoom <= 1) {
zoom(-0.1);
} else {
zoom(-0.25);
}
}
public void zoomIn() {
if (zoom >= 1) {
zoom(0.25);
} else {
zoom(0.1);
}
}
private void zoom(double delta) {
double oldZoom = zoom;
zoom += delta;
zoom = Math.min(Math.max(0.15, zoom), 4.0);
// double left = mapEditorScroll.getHvalue();
// double top = mapEditorScroll.getVvalue();
//
// double pointerX = t.getX();
// double pointerY = t.getY();
//
double ratio = zoom / oldZoom;
//
// double newLeft = (left + pointerX) * ratio - pointerX;
// double newTop = (top + pointerY) * ratio - pointerY;
tileWidth = currentPlatform.tileRenderer.getWidth() * zoom;
tileHeight = currentPlatform.tileRenderer.getHeight() * zoom;
redraw();
}
@Override
public void observedObjectChanged(Map object) {
redraw();
}
private long redrawRequested;
private Thread redrawThread;
public void redraw() {
redrawRequested = System.nanoTime();
if (redrawThread == null || redrawThread.isAlive()) {
redrawThread = new Thread(new Runnable() {
@Override
public void run() {
long test = redrawRequested;
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(MapEditor.class.getName()).log(Level.SEVERE, null, ex);
}
while (test != redrawRequested) {
test = redrawRequested;
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(MapEditor.class.getName()).log(Level.SEVERE, null, ex);
}
}
Platform.runLater(new Runnable() {
@Override
public void run() {
doRedraw();
}
});
redrawThread = null;
}
});
redrawThread.start();
}
}
private synchronized void doRedraw() {
drawCanvas.getGraphicsContext2D().clearRect(0, 0, drawCanvas.getWidth(), drawCanvas.getHeight());
int cols = (int) (drawCanvas.getWidth() / tileWidth);
int rows = (int) (drawCanvas.getHeight() / tileHeight);
for (int x = 0; x <= cols; x++) {
for (int y = 0; y <= rows; y++) {
Tile tile = currentMap.get(posX + x, posY + y);
doDraw(x, y, tile);
}
}
anchorPane.getChildren().get(1).setLayoutX((drawCanvas.getWidth() - 30) / 2);
anchorPane.getChildren().get(2).setLayoutY((drawCanvas.getHeight() - 30) / 2);
anchorPane.getChildren().get(3).setLayoutX((drawCanvas.getWidth() - 30) / 2);
anchorPane.getChildren().get(4).setLayoutY((drawCanvas.getHeight() - 30) / 2);
}
private void doDraw(int x, int y, Tile tile) {
double xx = x * tileWidth;
double yy = y * tileHeight;
if (tile != null) {
drawCanvas.getGraphicsContext2D().drawImage(TileUtils.getImage(tile, currentPlatform), xx, yy, tileWidth, tileHeight);
} else {
drawCanvas.getGraphicsContext2D().clearRect(xx, yy, tileWidth, tileHeight);
}
}
@Override
public void unregister() {
drawCanvas.widthProperty().unbind();
drawCanvas.heightProperty().unbind();
anchorPane.getChildren().remove(drawCanvas);
currentMap.updateBackingMap();
}
/**
* @return the currentTile
*/
public Tile getCurrentTile() {
return currentTile;
}
/**
* @param currentTile the currentTile to set
*/
public void setCurrentTile(Tile currentTile) {
this.currentTile = currentTile;
ImageCursor cursor = new ImageCursor(TileUtils.getImage(currentTile, currentPlatform), 2, 2);
drawCanvas.setCursor(cursor);
}
public void showPreview() {
WritableImage img = currentPlatform.imageRenderer.renderPreview(currentMap, posX, posY);
Stage stage = new Stage();
stage.setTitle("Preview");
ImageView imgView = new ImageView(img);
Group root = new Group(imgView);
stage.setScene(new Scene(root, img.getWidth(), img.getHeight()));
stage.show();
}
@Override
public void copy() {
WritableImage img = currentPlatform.imageRenderer.renderPreview(currentMap, posX, posY);
java.util.Map<DataFormat,Object> clip = new HashMap<>();
clip.put(DataFormat.IMAGE, img);
clip.put(DataFormat.PLAIN_TEXT, "selection/map/"+Application.gameData.getMap().indexOf(getEntity())+"/"+getSelectionInfo());
Clipboard.getSystemClipboard().setContent(clip);
}
@Override
public String getSelectedAllInfo() {
setSelectionArea(posX, posY, posX+19, posY+11);
String result = getSelectionInfo();
setSelectionArea(0,0,0,0);
return result;
}
@Override
public void paste() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void select() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void selectNone() {
setSelectionArea(0,0,0,0);
}
public static enum DrawMode {
Pencil1px, Pencil3px, Pencil5px, FilledRect
};
public void plot(final int x, final int y, final Tile t) {
if (x < 0) {
return;
}
if (y < 0) {
return;
}
if (x >= getEntity().getWidth()) {
System.out.println("X out of bounds " + getEntity().getWidth());
return;
}
if (y >= getEntity().getHeight()) {
System.out.println("Y out of bounds " + getEntity().getHeight());
return;
}
currentMap.put(x, y, t);
doDraw(x - posX, y - posY, t);
}
public void drawBrush(int x, int y, int size, Tile t) {
for (int xx = x - size; xx <= x + size; xx++) {
for (int yy = y - size; yy <= y + size; yy++) {
if (Math.sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y)) > size) {
continue;
}
plot(xx, yy, getCurrentTile());
}
}
}
public static Rectangle selectRect = null;
public double selectStartX = 0;
public double selectStartY = 0;
private void startSelection(double x, double y) {
selectRect = new Rectangle(1, 1, Color.NAVY);
selectRect.setTranslateX(x);
selectRect.setTranslateY(y);
selectRect.setOpacity(0.5);
selectStartX = x;
selectStartY = y;
anchorPane.getChildren().add(selectRect);
}
private void updateSelection(double x, double y) {
if (selectRect == null) {
startSelection(x, y);
}
double minX = Math.min(selectStartX, x);
double minY = Math.min(selectStartY, y);
double maxX = Math.max(selectStartX, x);
double maxY = Math.max(selectStartY, y);
selectRect.setTranslateX(minX);
selectRect.setTranslateY(minY);
selectRect.setWidth(maxX - minX);
selectRect.setHeight(maxY - minY);
}
private void fillSelection(double x, double y) {
anchorPane.getChildren().remove(selectRect);
selectRect = null;
int startx = (int) (selectStartX / tileWidth);
int starty = (int) (selectStartY / tileHeight);
int endx = (int) (x / tileWidth);
int endy = (int) (y / tileHeight);
for (int yy = Math.min(starty, endy); yy <= Math.max(starty, endy); yy++) {
for (int xx = Math.min(startx, endx); xx <= Math.max(startx, endx); xx++) {
plot(xx, yy, getCurrentTile());
}
}
}
public static int lastX = -1;
public static int lastY = -1;
DrawMode lastDrawMode = null;
Tile lastTile = null;
@Override
public void handle(MouseEvent t) {
if (getCurrentTile() == null) {
System.out.println("No tile selected, ignoring");
return;
}
t.consume();
int x = (int) (t.getX() / tileWidth) + posX;
int y = (int) (t.getY() / tileHeight) + posY;
boolean canSkip = false;
if (getCurrentTile() == lastTile && x == lastX && y == lastY && drawMode == lastDrawMode) {
canSkip = true;
}
lastX = x;
lastY = y;
lastDrawMode = drawMode;
lastTile = getCurrentTile();
switch (drawMode) {
case Pencil1px:
if (canSkip) {
return;
}
plot(x, y, getCurrentTile());
break;
case Pencil3px:
if (canSkip) {
return;
}
drawBrush(x, y, 2, getCurrentTile());
break;
case Pencil5px:
if (canSkip) {
return;
}
drawBrush(x, y, 5, getCurrentTile());
break;
case FilledRect:
if (t.getEventType().equals(MouseEvent.MOUSE_RELEASED)) {
fillSelection(t.getX(), t.getY());
} else {
updateSelection(t.getX(), t.getY());
}
}
}
}

View File

@ -0,0 +1,36 @@
package org.badvision.outlaweditor;
import org.badvision.outlaweditor.apple.AppleImageEditor;
import org.badvision.outlaweditor.apple.AppleTileEditor;
import org.badvision.outlaweditor.apple.AppleTileRenderer;
import org.badvision.outlaweditor.apple.AppleImageRenderer;
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRImageEditor;
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRImageRenderer;
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRTileEditor;
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRTileRenderer;
/**
* Enumeration of platforms
* @author brobert
*/
public enum Platform {
AppleII(AppleTileEditor.class, AppleImageEditor.class, new AppleTileRenderer(), new AppleImageRenderer(),2, 16),
AppleII_DHGR(AppleDHGRTileEditor.class, AppleDHGRImageEditor.class, new AppleDHGRTileRenderer(), new AppleDHGRImageRenderer(),4, 16),
C64(null, null, null, null, 16, 16);
public Class<? extends TileEditor> tileEditor;
public Class<? extends ImageEditor> imageEditor;
public TileRenderer tileRenderer;
public ImageRenderer imageRenderer;
public int dataWidth;
public int dataHeight;
Platform(Class ed, Class imged, TileRenderer ren, ImageRenderer img, int w, int h) {
tileEditor = ed;
imageEditor = imged;
tileRenderer = ren;
imageRenderer = img;
dataWidth = w;
dataHeight = h;
}
}

View File

@ -0,0 +1,16 @@
package org.badvision.outlaweditor;
/**
*
* @author brobert
*/
import javafx.scene.control.Menu;
import org.badvision.outlaweditor.data.xml.Tile;
public abstract class TileEditor extends Editor<Tile, TileEditor.DrawMode> {
abstract public void buildPatternSelector(Menu tilePatternMenu);
public static enum DrawMode {
Pencil1px, Pencil3px, Toggle
}
}

View File

@ -0,0 +1,14 @@
package org.badvision.outlaweditor;
import javafx.scene.image.WritableImage;
/**
*
* @author brobert
*/
public abstract class TileRenderer {
public abstract WritableImage redrawSprite(byte[] spriteData, WritableImage image);
public abstract int getWidth();
public abstract int getHeight();
}

View File

@ -0,0 +1,148 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javax.swing.JOptionPane;
import javax.xml.bind.JAXB;
import org.badvision.outlaweditor.data.TilesetUtils;
import org.badvision.outlaweditor.data.xml.GameData;
/**
*
* @author brobert
*/
public class UIAction {
private static File currentSaveFile;
public static enum MAIN_ACTIONS {
_General,
Load, Save, Save_as, Apple_Mode, C64_Mode, About_Outlaw_Editor, Quit,
_Map,
Create_new_map, Export_map_to_image,
_Tiles,
Create_new_tile, Export_tile_to_image, Delete_tile,
_Images,
Create_new_image, Import_image, Export_image_as_raw, Delete_image;
};
public static void actionPerformed(MAIN_ACTIONS action) throws IOException {
switch (action) {
case About_Outlaw_Editor:
break;
case Apple_Mode:
break;
case C64_Mode:
break;
case Delete_tile:
// if (currentSelectedTile != null) {
// Tile t = currentSelectedTile;
// selectTile(null, null);
// TilesetUtils.remove(t);
// Application.instance.redrawTileSelector();
// Application.instance.rebuildMapEditor();
// }
break;
case Export_map_to_image:
break;
case Export_tile_to_image:
break;
case Load:
File f = FileUtils.getFile(currentSaveFile, "Load game data", Boolean.FALSE, FileUtils.Extension.XML, FileUtils.Extension.ALL);
if (f == null) {
return;
}
currentSaveFile = f;
GameData newData = JAXB.unmarshal(currentSaveFile, GameData.class);
Application.instance.controller.setCurrentMap(null);
Application.instance.controller.setCurrentTile(null);
TilesetUtils.clear();
Application.gameData = newData;
Application.instance.controller.rebuildTileSelectors();
Application.instance.controller.rebuildMapSelectors();
break;
case Quit:
quit();
break;
case Save_as:
f = FileUtils.getFile(currentSaveFile, "Save game data", Boolean.TRUE, FileUtils.Extension.XML, FileUtils.Extension.ALL);
if (f == null) {
return;
}
currentSaveFile = f;
case Save:
if (currentSaveFile == null) {
currentSaveFile = FileUtils.getFile(currentSaveFile, "Save game data", Boolean.TRUE, FileUtils.Extension.XML, FileUtils.Extension.ALL);
}
if (currentSaveFile != null) {
currentSaveFile.delete();
JAXB.marshal(Application.gameData, currentSaveFile);
}
break;
}
}
public static MenuBar buildMenu() {
MenuBar menu = new MenuBar();
Menu currentMenu = null;
for (final MAIN_ACTIONS action : UIAction.MAIN_ACTIONS.values()) {
if (action.name().startsWith("_")) {
if (currentMenu != null) {
menu.getMenus().add(currentMenu);
}
currentMenu = new Menu(action.name().replace("_", ""));
} else {
MenuItem item = new MenuItem(action.name().replaceAll("_", " "));
item.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
try {
actionPerformed(action);
} catch (IOException ex) {
Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
currentMenu.getItems().add(item);
}
}
menu.getMenus().add(currentMenu);
menu.setMinWidth(1.0);
return menu;
}
public static boolean quit() {
if (JOptionPane.showConfirmDialog(null, "Quit? Are you sure?") == JOptionPane.OK_OPTION) {
Platform.exit();
return true;
}
return false;
}
static Image badImage;
public static WritableImage getBadImage(int width, int height) {
if (badImage == null) {
badImage = new Image("/org/badvision/outlaw/resources/icon_brokenLink.png");
}
WritableImage img = new WritableImage(width, height);
img.getPixelWriter().setPixels(0, 0, (int) badImage.getWidth(), (int) badImage.getHeight(), badImage.getPixelReader(), 0, 0);
return img;
}
}

View File

@ -0,0 +1,402 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.apple;
import java.util.HashMap;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Menu;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import org.badvision.outlaweditor.Application;
import org.badvision.outlaweditor.ImageEditor;
import org.badvision.outlaweditor.Platform;
import org.badvision.outlaweditor.data.DataObserver;
import org.badvision.outlaweditor.data.xml.Image;
import org.badvision.outlaweditor.data.xml.PlatformData;
/**
*
* @author brobert
*/
public class AppleImageEditor extends ImageEditor implements EventHandler<MouseEvent> {
public int[] currentFillPattern = FillPattern.White.bytePattern;
public boolean hiBitMatters = true;
protected DrawMode currentDrawMode = DrawMode.Pencil1px;
protected WritableImage currentImage;
protected AnchorPane anchorPane;
protected ImageView screen;
protected int posX = 0;
protected int posY = 0;
protected double zoom = 1.0;
protected int xScale = 2;
protected int yScale = 2;
public Platform getPlatform() {
return Platform.AppleII;
}
@Override
public void buildEditorUI(AnchorPane editorAnchorPane) {
anchorPane = editorAnchorPane;
redraw();
screen = new ImageView(currentImage);
anchorPane.getChildren().add(0, screen);
screen.setOnMouseClicked(this);
screen.setOnMouseReleased(this);
screen.setOnMouseDragged(this);
screen.setOnMouseDragReleased(this);
}
@Override
public void buildPatternSelector(Menu tilePatternMenu) {
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
@Override
public void observedObjectChanged(FillPattern object) {
changeCurrentPattern(object);
}
});
}
public void changeCurrentPattern(FillPattern pattern) {
currentFillPattern = pattern.getBytePattern();
hiBitMatters = pattern.hiBitMatters;
lastActionX = -1;
lastActionY = -1;
}
@Override
public void setDrawMode(DrawMode drawMode) {
currentDrawMode = drawMode;
lastActionX = -1;
lastActionY = -1;
}
@Override
public void showShiftUI() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void unregister() {
anchorPane.getChildren().remove(screen);
}
@Override
public void observedObjectChanged(Image object) {
redraw();
}
public void redrawScanline(int y) {
currentImage = getPlatform().imageRenderer.renderScanline(currentImage, y, getImageData());
}
@Override
public void redraw() {
System.out.println("Redraw "+getPlatform().name());
currentImage = getPlatform().imageRenderer.renderImage(currentImage, getImageData());
anchorPane.getChildren().get(1).setLayoutX((anchorPane.getWidth() - 30) / 2);
anchorPane.getChildren().get(2).setLayoutY((anchorPane.getHeight() - 30) / 2);
anchorPane.getChildren().get(3).setLayoutX((anchorPane.getWidth() - 30) / 2);
anchorPane.getChildren().get(4).setLayoutY((anchorPane.getHeight() - 30) / 2);
}
private byte[] imageData = null;
public byte[] getImageData() {
if (imageData == null) {
PlatformData data = null;
for (PlatformData d : getEntity().getDisplayData()) {
if (d.getPlatform().equalsIgnoreCase(getPlatform().name())) {
data = d;
break;
}
}
if (data == null) {
data = new PlatformData();
data.setWidth(40);
data.setHeight(192);
data.setPlatform(getPlatform().name());
data.setValue(getPlatform().imageRenderer.createImageBuffer());
getEntity().getDisplayData().add(data);
}
imageData = data.getValue();
}
return imageData;
}
public void setData(byte[] data) {
imageData = data;
for (PlatformData d : getEntity().getDisplayData()) {
if (d.getPlatform().equalsIgnoreCase(getPlatform().name())) {
d.setValue(data);
break;
}
}
}
@Override
public void togglePanZoom() {
for (Node n : anchorPane.getChildren()) {
if (n == screen) {
continue;
}
n.setVisible(!n.isVisible());
}
}
@Override
public void scrollBy(int deltaX, int deltaY) {
posX += deltaX * 10;
posY += deltaY * 10;
posX = Math.max(0, posX);
posY = Math.max(0, posY);
redraw();
}
@Override
public void zoomOut() {
if (zoom <= 1) {
zoom(-0.1);
} else {
zoom(-0.25);
}
}
@Override
public void zoomIn() {
if (zoom >= 1) {
zoom(0.25);
} else {
zoom(0.1);
}
}
private void zoom(double delta) {
double oldZoom = zoom;
zoom += delta;
zoom = Math.min(Math.max(0.15, zoom), 4.0);
// double left = mapEditorScroll.getHvalue();
// double top = mapEditorScroll.getVvalue();
//
// double pointerX = t.getX();
// double pointerY = t.getY();
//
double ratio = zoom / oldZoom;
//
// double newLeft = (left + pointerX) * ratio - pointerX;
// double newTop = (top + pointerY) * ratio - pointerY;
redraw();
}
@Override
public void handle(MouseEvent t) {
performAction(t.isShiftDown() || t.isSecondaryButtonDown(), t.getEventType().equals(MouseEvent.MOUSE_RELEASED), (int) t.getX() / xScale, (int) t.getY() / yScale);
}
protected int lastActionX = -1;
protected int lastActionY = -1;
public void performAction(boolean alt, boolean released, int x, int y) {
y = Math.min(Math.max(y, 0), 191);
x = Math.min(Math.max(x, 0), (getWidth() * 7) - 1);
boolean canSkip = false;
if (lastActionX == x && lastActionY == y) {
canSkip = true;
}
lastActionX = x;
lastActionY = y;
switch (currentDrawMode) {
case Toggle:
if (canSkip) {
return;
}
if (alt) {
toggleHiBit(x, y);
} else {
toggle(x, y);
}
redrawScanline(y);
break;
case Pencil1px:
if (canSkip) {
return;
}
plot(x, y, currentFillPattern, hiBitMatters);
redrawScanline(y);
break;
case Pencil3px:
if (canSkip) {
return;
}
drawBrush(x, y, 3, currentFillPattern, hiBitMatters);
break;
case Pencil5px:
if (canSkip) {
return;
}
drawBrush(x, y, 5, currentFillPattern, hiBitMatters);
break;
case Rectangle:
if (released) {
fillSelection(x, y);
redraw();
} else {
updateSelection(x, y);
}
}
// observedObjectChanged(getEntity());
}
public static Rectangle selectRect = null;
public int selectStartX = 0;
public int selectStartY = 0;
private void startSelection(int x, int y) {
selectRect = new Rectangle(1, 1, Color.NAVY);
selectRect.setTranslateX(x);
selectRect.setTranslateY(y);
selectRect.setOpacity(0.5);
selectStartX = x;
selectStartY = y;
anchorPane.getChildren().add(selectRect);
}
private void updateSelection(int x, int y) {
if (selectRect == null) {
startSelection(x, y);
}
double minX = Math.min(selectStartX, x) * xScale;
double minY = Math.min(selectStartY, y) * yScale;
double maxX = Math.max(selectStartX, x) * xScale;
double maxY = Math.max(selectStartY, y) * yScale;
selectRect.setTranslateX(minX);
selectRect.setTranslateY(minY);
selectRect.setWidth(maxX - minX);
selectRect.setHeight(maxY - minY);
}
private void fillSelection(int x, int y) {
anchorPane.getChildren().remove(selectRect);
selectRect = null;
for (int yy = Math.min(selectStartY, y); yy <= Math.max(selectStartY, y); yy++) {
for (int xx = Math.min(selectStartX, x); xx <= Math.max(selectStartX, x); xx++) {
plot(xx, yy, currentFillPattern, hiBitMatters);
}
}
}
public void drawBrush(int x, int y, int size, int[] pattern, boolean hiBitMatters) {
for (int yy = y - size; yy <= y + size; yy++) {
for (int xx = x - size; xx <= x + size; xx++) {
if (Math.sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y)) > size) {
continue;
}
plot(xx, yy, pattern, hiBitMatters);
}
redrawScanline(yy);
}
}
public void plot(int x, int y, int[] pattern, boolean hiBitMatters) {
if (x < 0 || y < 0 || x >= getWidth() * 7 || y >= getHeight()) {
return;
}
int pat = pattern[(y % 16) * 4 + ((x / 7) % 4)];
set((pat & (1 << (x % 7))) != 0, x, y);
if (hiBitMatters) {
setHiBit(pat >= 128, x, y);
}
}
public void toggleHiBit(int x, int y) {
byte[] data = getImageData();
data[y * getWidth() + (x / 7)] ^= 128;
setData(data);
}
public void setHiBit(boolean on, int x, int y) {
byte[] data = getImageData();
if (on) {
data[y * getWidth() + (x / 7)] |= 128;
} else {
data[y * getWidth() + (x / 7)] &= 127;
}
setData(data);
}
public void toggle(int x, int y) {
byte[] data = getImageData();
data[y * getWidth() + (x / 7)] ^= (1 << (x % 7));
setData(data);
}
public void set(boolean on, int x, int y) {
byte[] data = getImageData();
data[y * getWidth() + (x / 7)] |= (1 << (x % 7));
if (!on) {
data[y * getWidth() + (x / 7)] ^= (1 << (x % 7));
}
setData(data);
}
public int getWidth() {
return 40;
}
public int getHeight() {
return 192;
}
@Override
public void copy() {
java.util.Map<DataFormat, Object> clip = new HashMap<>();
clip.put(DataFormat.IMAGE, currentImage);
clip.put(DataFormat.PLAIN_TEXT, "selection/image/" + Application.gameData.getImage().indexOf(getEntity()) + "/" + getSelectionInfo());
Clipboard.getSystemClipboard().setContent(clip);
}
@Override
public void paste() {
if (Clipboard.getSystemClipboard().hasContent(DataFormat.PLAIN_TEXT)) {
if (pasteAppContent((String) Clipboard.getSystemClipboard().getContent(DataFormat.PLAIN_TEXT))) {
return;
}
};
if (Clipboard.getSystemClipboard().hasContent(DataFormat.IMAGE)) {
javafx.scene.image.Image image = Clipboard.getSystemClipboard().getImage();
importImage(image);
}
}
public boolean pasteAppContent(String contentPath) {
return false;
}
@Override
public void select() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void selectNone() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
private void importImage(javafx.scene.image.Image image) {
FloydSteinbergDither.floydSteinbergDither(image, getPlatform(), 0, 0, getWidth(), getHeight(), new FloydSteinbergDither.DitherCallback() {
@Override
public void ditherCompleted(byte[] data) {
setData(data);
redraw();
}
});
}
}

View File

@ -0,0 +1,138 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.apple;
import javafx.scene.image.PixelWriter;
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.*;
import javafx.scene.image.WritableImage;
import org.badvision.outlaweditor.ImageRenderer;
import org.badvision.outlaweditor.Platform;
import org.badvision.outlaweditor.data.TileMap;
import org.badvision.outlaweditor.data.TileUtils;
import org.badvision.outlaweditor.data.xml.Tile;
/**
*
* @author brobert
*/
public class AppleImageRenderer extends ImageRenderer {
public static int BLACK = 0xff000000;
public static int WHITE = 0xffffffff;
// scanline is 20 16-bit words
// If mixed-mode is used then useColor needs to be an 80-boolean array indicating which bytes are supposed to be BW
@Override
public byte[] createImageBuffer() {
return new byte[40 * 192];
}
@Override
public WritableImage renderPreview(TileMap map, int startX, int startY) {
byte[] buffer = createImageBuffer();
int pos = 0;
for (int y = 0; y < 12; y++) {
for (int yy = 0; yy < 16; yy++) {
for (int x = 0; x < 20; x++) {
Tile t = map.get(x + startX, y + startY);
if (t == null) {
buffer[pos++] = 0;
buffer[pos++] = 0;
} else {
byte[] tileData = TileUtils.getPlatformData(t, Platform.AppleII);
buffer[pos++] = tileData[yy * 2];
buffer[pos++] = tileData[yy * 2 + 1];
}
}
}
}
return renderImage(null, buffer);
}
@Override
public WritableImage renderImage(WritableImage img, byte[] rawImage) {
if (img == null) {
img = new WritableImage(560, 384);
}
for (int y = 0; y < 192; y++) {
renderScanline(img, y, rawImage);
}
return img;
}
@Override
public WritableImage renderScanline(WritableImage img, int y, byte[] rawImage) {
int[] scanline = new int[20];
boolean extraHalfBit = false;
for (int x = 0; x < 40; x += 2) {
int b1 = rawImage[y * 40 + x] & 255;
int b2 = rawImage[y * 40 + x + 1] & 255;
int i = hgrToDhgr[(extraHalfBit && x > 0) ? b1 | 0x0100 : b1][b2];
extraHalfBit = (i & 0x10000000) != 0;
scanline[x / 2] = i & 0xfffffff;
}
renderScanline(img.getPixelWriter(), y * 2, scanline, true, false);
renderScanline(img.getPixelWriter(), y * 2 + 1, scanline, true, false);
return img;
}
public static void renderScanline(PixelWriter img, int y, int[] scanline, boolean hiresMode, boolean mixedMode, boolean... useColor) {
int[][] activePalette = AppleTileRenderer.useSolidPalette ? solidPalette : textPalette;
int byteCounter = 0;
int x = 0;
for (int s = 0; s < scanline.length; s++) {
int add = 0;
int bits = 0;
if (hiresMode) {
bits = scanline[s] << 2;
if (s > 0) {
bits |= (scanline[s - 1] >> 26) & 3;
}
} else {
bits = scanline[s] << 3;
if (s > 0) {
bits |= (scanline[s - 1] >> 25) & 7;
}
}
if (s < scanline.length - 1) {
add = (scanline[s + 1] & 7);
}
boolean isBW = false;
if (mixedMode) {
for (int i = 0; i < 28; i++) {
if (i % 7 == 0) {
isBW = !hiresMode && !useColor[byteCounter];
byteCounter++;
}
try {
if (isBW) {
img.setArgb(x++, y, ((bits & 0x8) == 0) ? BLACK : WHITE);
} else {
img.setArgb(x++, y, activePalette[i % 4][bits & 0x07f]);
}
} catch (IndexOutOfBoundsException ex) {
// Ignore
}
bits >>= 1;
if (i == 20) {
bits |= add << (hiresMode ? 9 : 10);
}
}
} else {
for (int i = 0; i < 28; i++) {
try {
img.setArgb(x++, y, activePalette[i % 4][bits & 0x07f]);
} catch (IndexOutOfBoundsException ex) {
// Ignore
}
bits >>= 1;
if (i == 20) {
bits |= add << (hiresMode ? 9 : 10);
}
}
}
}
}
}

View File

@ -0,0 +1,116 @@
package org.badvision.outlaweditor.apple;
/**
*
* @author brobert
*/
public class AppleNTSCGraphics {
// i Range [-0.5957, 0.5957]
public static final double MAX_I = 0.5957;
// q Range [-0.5226, 0.5226]
public static final double MAX_Q = 0.5226;
public static final double MAX_Y = 1;
// y Range [0,1]
public static final double MIN_Y = 0;
public static int byteDoubler(byte b) {
int num = ((b & 64) << 6) | ((b & 32) << 5) | ((b & 16) << 4) | ((b & 8) << 3) | ((b & 4) << 2) | ((b & 2) << 1) | (b & 1);
return num | (num << 1);
}
public static double normalize(double x, double minX, double maxX) {
if (x < minX) {
return minX;
}
if (x > maxX) {
return maxX;
}
return x;
}
public static int yiqToRgb(double y, double i, double q) {
int r = (int) (normalize(y + 0.956 * i + 0.621 * q, 0, 1) * 255);
int g = (int) (normalize(y - 0.272 * i - 0.647 * q, 0, 1) * 255);
int b = (int) (normalize(y - 1.105 * i + 1.702 * q, 0, 1) * 255);
return (r << 16) | (g << 8) | b;
}
public static int getRed(int col) {
return ((col >> 16) & 255);
}
public static int getGreen(int col) {
return ((col >> 8) & 255);
}
public static int getBlue(int col) {
return (col & 255);
}
public static int[][] hgrToDhgr;
public static int[][] textPalette = new int[4][128];
public static int[][] hgrToDhgrBW;
public static int[][] solidPalette = new int[4][128];
public static void initPalettes() {
double[][] yiq = {
{0.0, 0.0, 0.0},
{0.25, 0.5, 0.5},
{0.25, -0.5, 0.5},
{0.5, 0.0, 1.0},
{0.25, -0.5, -0.5},
{0.5, 0.0, 0.0},
{0.5, -1.0, 0.0},
{0.75, -0.5, 0.5},
{0.25, 0.5, -0.5},
{0.5, 1.0, 0.0},
{0.5, 0.0, 0.0},
{0.75, 0.5, 0.5},
{0.5, 0.0, -1.0},
{0.75, 0.5, -0.5},
{0.75, -0.5, -0.5},
{1.0, 0.0, 0.0}};
int maxLevel = 10;
for (int offset = 0; offset < 4; offset++) {
for (int pattern = 0; pattern < 128; pattern++) {
int level = (pattern & 1) + ((pattern >> 1) & 1) * 1 + ((pattern >> 2) & 1) * 2 + ((pattern >> 3) & 1) * 4 + ((pattern >> 4) & 1) * 2 + ((pattern >> 5) & 1) * 1;
int col = (pattern >> 2) & 15;
for (int rot = 0; rot < offset; rot++) {
col = ((col & 8) >> 3) | ((col << 1) & 15);
}
double y1 = yiq[col][0];
double y2 = (double) level / (double) maxLevel;
solidPalette[offset][pattern] = (255 << 24) | yiqToRgb(y1, yiq[col][1] * MAX_I, yiq[col][2] * MAX_Q);
textPalette[offset][pattern] = (255 << 24) | yiqToRgb(y2, yiq[col][1] * MAX_I, yiq[col][2] * MAX_Q);
}
}
hgrToDhgr = new int[512][256];
hgrToDhgrBW = new int[256][256];
for (int bb1 = 0; bb1 < 512; bb1++) {
for (int bb2 = 0; bb2 < 256; bb2++) {
int value = ((bb1 & 385) >= 257) ? 1 : 0;
int b1 = byteDoubler((byte) (bb1 & 127));
if ((bb1 & 128) != 0) {
b1 <<= 1;
}
int b2 = byteDoubler((byte) (bb2 & 127));
if ((bb2 & 128) != 0) {
b2 <<= 1;
}
if ((bb1 & 64) == 64 && (bb2 & 1) != 0) {
b2 |= 1;
}
value |= b1 | (b2 << 14);
if ((bb2 & 64) != 0) {
value |= 268435456;
}
hgrToDhgr[bb1][bb2] = value;
hgrToDhgrBW[bb1 & 255][bb2] = byteDoubler((byte) bb1) | (byteDoubler((byte) bb2) << 14);
}
}
}
static {
initPalettes();
}
}

View File

@ -0,0 +1,238 @@
package org.badvision.outlaweditor.apple;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.control.Menu;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import org.badvision.outlaweditor.Platform;
import org.badvision.outlaweditor.TileEditor;
import org.badvision.outlaweditor.data.DataObserver;
import org.badvision.outlaweditor.data.xml.Tile;
import org.badvision.outlaweditor.data.TileUtils;
/**
*
* @author brobert
*/
public class AppleTileEditor extends TileEditor {
FillPattern currentPattern = FillPattern.DarkViolet1;
DrawMode drawMode = DrawMode.Toggle;
@Override
public void setEntity(Tile t) {
super.setEntity(t);
if (TileUtils.getPlatformData(t, Platform.AppleII) == null) {
TileUtils.setPlatformData(t, Platform.AppleII, new byte[32]);
}
}
@Override
public void buildEditorUI(AnchorPane tileEditorAnchorPane) {
grid = new Rectangle[14][16];
gridGroup = new Group();
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 14; x++) {
final int xx = x;
final int yy = y;
Rectangle rect = new Rectangle(zoom * x + 5, zoom * y + 5, zoom - 2, zoom - 2);
rect.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
performDragAction((int) (t.getX() / zoom), (int) (t.getY() / zoom));
}
});
rect.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
handleMouse(t, xx, yy);
}
});
grid[x][y] = rect;
gridGroup.getChildren().add(rect);
rect.setStrokeWidth(2);
}
}
Group mainGroup = new Group();
Rectangle background = new Rectangle(0, 0, zoom * 14 + 10, zoom * 16 + 10);
background.setFill(Color.BLACK);
mainGroup.getChildren().add(background);
mainGroup.getChildren().add(gridGroup);
tileEditorAnchorPane.getChildren().add(mainGroup);
TileUtils.redrawTile(getEntity());
observedObjectChanged(getEntity());
}
private void handleMouse(MouseEvent t, int x, int y) {
t.consume();
if (t.getButton() == null || t.getButton() == MouseButton.NONE) {
return;
}
performAction(t.isShiftDown() || t.isSecondaryButtonDown(), x, y);
}
int lastActionX = -1;
int lastActionY = -1;
public void performDragAction(int x, int y) {
performAction(false, x, y);
}
private void performAction(boolean alt, int x, int y) {
y = Math.min(Math.max(y,0), 15);
x = Math.min(Math.max(x,0), 13);
if (lastActionX == x && lastActionY == y) {
return;
}
lastActionX = x;
lastActionY = y;
switch (drawMode) {
case Toggle:
if (alt) {
toggleHiBit(x, y);
} else {
toggle(x, y);
}
break;
case Pencil1px:
int pat = currentPattern.getBytePattern()[y * 4 + (x / 7)];
set((pat & (1 << (x % 7))) != 0, x, y);
if (currentPattern.hiBitMatters) {
setHiBit(pat >= 128, x, y);
}
break;
case Pencil3px:
for (int xx = x-1; xx <= x+1; xx++) {
if (xx < 0 || xx >= 14) continue;
pat = currentPattern.getBytePattern()[y * 4 + (xx / 7)];
set((pat & (1 << (xx % 7))) != 0, xx, y);
if (currentPattern.hiBitMatters) {
setHiBit(pat >= 128, xx, y);
}
}
break;
}
observedObjectChanged(getEntity());
}
@Override
public void observedObjectChanged(Tile tile) {
recolorGrid(
TileUtils.getPlatformData(tile, Platform.AppleII),
grid,
TileUtils.getImage(tile, Platform.AppleII));
}
@Override
public void showShiftUI() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void setDrawMode(DrawMode drawMode) {
this.drawMode = drawMode;
lastActionX = -1;
lastActionY = -1;
}
@Override
public void buildPatternSelector(Menu tilePatternMenu) {
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
@Override
public void observedObjectChanged(FillPattern object) {
changeCurrentPattern(object);
}
});
}
public void changeCurrentPattern(FillPattern pat) {
currentPattern = pat;
lastActionX = -1;
lastActionY = -1;
}
@Override
public void unregister() {
}
int zoom = 25;
Group gridGroup;
Rectangle[][] grid;
public void toggleHiBit(int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
data[y * 2 + (x / 7)] ^= 128;
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
TileUtils.redrawTile(getEntity());
}
public void setHiBit(boolean on, int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
if (on) {
data[y * 2 + (x / 7)] |= 128;
} else {
data[y * 2 + (x / 7)] &= 127;
}
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
TileUtils.redrawTile(getEntity());
}
public void toggle(int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
data[y * 2 + (x / 7)] ^= (1 << (x % 7));
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
TileUtils.redrawTile(getEntity());
}
public void set(boolean on, int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
data[y * 2 + (x / 7)] |= (1 << (x % 7));
if (!on) {
data[y * 2 + (x / 7)] ^= (1 << (x % 7));
}
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
TileUtils.redrawTile(getEntity());
}
public void recolorGrid(byte[] spriteData, Shape[][] grid, WritableImage img) {
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 14; x++) {
boolean isHiBit = ((spriteData[y * 2 + x / 7] & 128) != 0);
grid[x][y].setFill(img.getPixelReader().getColor(x * 2 + (isHiBit ? 1 : 0), y * 2));
if ((spriteData[y * 2 + x / 7] & (1 << (x % 7))) != 0) {
grid[x][y].setStroke(Color.ANTIQUEWHITE);
} else {
grid[x][y].setStroke(
isHiBit
? (x % 2 == 1) ? Color.CHOCOLATE : Color.CORNFLOWERBLUE
: (x % 2 == 1) ? Color.GREEN : Color.VIOLET);
}
}
}
}
@Override
public void copy() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void paste() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void select() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void selectNone() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

View File

@ -0,0 +1,58 @@
package org.badvision.outlaweditor.apple;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import org.badvision.outlaweditor.TileRenderer;
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.*;
/**
*
* @author brobert
*/
public class AppleTileRenderer extends TileRenderer {
public static boolean useSolidPalette = true;
@Override
public WritableImage redrawSprite(byte[] spriteData, WritableImage img) {
if (img == null) {
img = new WritableImage(28, 32);
}
if (spriteData == null) return img;
int[][] palette = useSolidPalette ? solidPalette : textPalette;
for (int y = 0; y < 16; y++) {
int bleedOver = (spriteData[y * 2 + 1] & 192)==192 ? 256 : 0;
int scan = hgrToDhgr[bleedOver | (spriteData[y * 2] & 255)][spriteData[y * 2 + 1] & 255];
int last = (scan >> 26) & 3;
int keep = scan & 0xff;
scan <<= 2;
scan |= last;
for (int x = 0; x < 14; x++) {
boolean isHiBit = ((spriteData[y * 2 + x / 7] & 128) != 0);
int col1 = palette[ (x & 1) << 1][scan & 0x07f];
Color color1 = Color.rgb(getRed(col1), getGreen(col1), getBlue(col1));
scan >>= 1;
if (x == 12) {
scan = scan & (isHiBit ? 0x07f : 0x01f) | (keep << 5);
}
int col2 = palette[ ((x & 1) << 1) + 1][scan & 0x07f];
Color color2 = Color.rgb(getRed(col2), getGreen(col2), getBlue(col2));
scan >>= 1;
img.getPixelWriter().setColor(x * 2, y * 2, color1);
img.getPixelWriter().setColor(x * 2, y * 2 + 1, color1);
img.getPixelWriter().setColor(x * 2 + 1, y * 2, color2);
img.getPixelWriter().setColor(x * 2 + 1, y * 2 + 1, color2);
}
}
return img;
}
@Override
public int getWidth() {
return 28;
}
@Override
public int getHeight() {
return 32;
}
}

View File

@ -0,0 +1,239 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.apple;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import org.badvision.outlaweditor.Platform;
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
import org.badvision.outlaweditor.data.DataObserver;
import org.badvision.outlaweditor.data.TileUtils;
import org.badvision.outlaweditor.data.xml.Tile;
/**
*
* @author brobert
*/
public enum FillPattern {
Violet(true, 2, false,
"+-+-+-+-+-+-+-"),
DarkViolet1(true, 4, false,
"+---+---+---+---+---+---+---",
"--+---+---+---+---+---+---+-"
),
DarkViolet2(true, 4, false,
"--+---+---+---+---+---+---+-",
"+---+---+---+---+---+---+---"
),
LightViolet1(true, 4, false,
"+++-+++-+++-+++-+++-+++-+++-",
"+-+++-+++-+++-+++-+++-+++-++"
),
LightViolet2(true, 4, false,
"+-+++-+++-+++-+++-+++-+++-++",
"+++-+++-+++-+++-+++-+++-+++-"
),
Green(true, 2, false,
"-+-+-+-+-+-+-+"),
DarkGreen1(true, 4, false,
"-+---+---+---+---+---+---+--",
"---+---+---+---+---+---+---+"
),
DarkGreen2(true, 4, false,
"---+---+---+---+---+---+---+",
"-+---+---+---+---+---+---+--"
),
LightGreen1(true, 4, false,
"++-+++-+++-+++-+++-+++-+++-+",
"-+++-+++-+++-+++-+++-+++-+++"
),
LightGreen2(true, 4, false,
"-+++-+++-+++-+++-+++-+++-+++",
"++-+++-+++-+++-+++-+++-+++-+"),
Blue(true, 2, true,
"+-+-+-+-+-+-+-"),
DarkBlue(true, 4, true,
"+---+---+---+---+---+---+---",
"--+---+---+---+---+---+---+-"
),
DarkBlue2(true, 4, true,
"--+---+---+---+---+---+---+-",
"+---+---+---+---+---+---+---"
),
LightBlue1(true, 4, true,
"+++-+++-+++-+++-+++-+++-+++-",
"+-+++-+++-+++-+++-+++-+++-++"
),
LightBlue2(true, 4, true,
"+-+++-+++-+++-+++-+++-+++-++",
"+++-+++-+++-+++-+++-+++-+++-"
),
Orange(true, 2, true,
"-+-+-+-+-+-+-+"),
DarkOrange1(true, 4, true,
"-+---+---+---+---+---+---+--",
"---+---+---+---+---+---+---+"
),
DarkOrange(true, 4, true,
"---+---+---+---+---+---+---+",
"-+---+---+---+---+---+---+--"
),
LightOrange1(true, 4, true,
"++-+++-+++-+++-+++-+++-+++-+",
"-+++-+++-+++-+++-+++-+++-+++"
),
LightOrange2(true, 4, true,
"-+++-+++-+++-+++-+++-+++-+++",
"++-+++-+++-+++-+++-+++-+++-+"),
Black(false, 1, false, "-------"),
White(false, 1, false, "+++++++"),
BW1(false, 4, false,
"++--++--++--++--++--++--++--",
"--++--++--++--++--++--++--++"
),
BW2(false, 4, false,
"--++--++--++--++--++--++--++",
"++--++--++--++--++--++--++--"
);
// ,
// BW3(false, 4, false,
// "+--++--++--++--++--++--++--+",
// "-++--++--++--++--++--++--++-"
// ),
// BW4(false, 4, false,
// "-++--++--++--++--++--++--++-",
// "+--++--++--++--++--++--++--+"
// );
public static int[] bitmask(boolean hiBit, String pattern) {
int[] out = new int[pattern.length()/7];
int place = 1;
int pos = 0;
int value = hiBit ? 128 : 0;
for (int i=0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (c == '1' || c == '+') {
value |= place;
}
out[pos] = value;
place <<= 1;
if (place >= 128) {
place = 1;
value = hiBit ? 128 : 0;
pos++;
}
}
return out;
}
public static Integer[] buildPattern(boolean hiBit, String... pattern) {
List<Integer> out = new ArrayList<>();
for (String s : pattern) {
for (int i : bitmask(hiBit, s)) {
out.add(i);
}
}
return out.toArray(new Integer[0]);
}
public static void buildMenu(Menu target, final DataObserver<FillPattern> dataObserver) {
target.getItems().clear();
for (final FillPattern fill : FillPattern.values()) {
MenuItem i = new MenuItem(fill.name(), new ImageView(fill.getPreview()));
i.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
dataObserver.observedObjectChanged(fill);
}
});
target.getItems().add(i);
}
}
Integer[] pattern;
int[] bytePattern;
int width;
boolean hiBitMatters;
WritableImage preview;
private FillPattern(boolean hiBitMatters, int width, boolean hiBit, String... pattern) {
this.pattern = buildPattern(hiBit, pattern);
this.width = width;
this.hiBitMatters = hiBitMatters;
this.bytePattern = getBytePattern();
}
public WritableImage getPreview() {
if (preview == null) {
preview = renderPreview(null, bytePattern);
}
return preview;
}
public int[] getBytePattern() {
int[] out = new int[16 * 4];
int pos = 0;
int patternHeight = pattern.length / width;
for (int y = 0; y < 16; y++) {
int yOffset = (y % patternHeight) * width;
for (int x = 0; x < 4; x++) {
out[pos++] = pattern[yOffset + (x % width)];
}
}
return out;
}
public static int[] interleave(int[] pat1, int[] pat2) {
int[] out = Arrays.copyOf(pat1, pat1.length);
for (int y = 1; y < 16; y += 2) {
int offset = y * 4;
for (int x = 0; x < 4; x++) {
out[offset + x] = pat2[offset + x];
}
}
return out;
}
public static int[] fromTile(Tile t) {
byte[] raw = TileUtils.getPlatformData(t, Platform.AppleII);
int[] out = new int[16 * 4];
int pos = 0;
for (int y = 0; y < 16; y++) {
for (int c = 0; c < 2; c++) {
for (int x = 0; x < 2; x++) {
out[pos++] = raw[y * 2 + x] & 0x0ff;
}
}
}
return out;
}
public static WritableImage renderPreview(WritableImage img, int[] pattern) {
if (img == null) {
img = new WritableImage(28, 32);
}
int[] scan = new int[2];
for (int y = 0; y < 16; y++) {
int b1 = pattern[y * 4] & 255;
int b2 = pattern[y * 4 + 1] & 255;
int i = hgrToDhgr[b1][b2];
boolean extraHalfBit = (i & 0x10000000) != 0;
scan[0] = i & 0xfffffff;
b1 = pattern[y * 4 + 2] & 255;
b2 = pattern[y * 4 + 3] & 255;
i = hgrToDhgr[(extraHalfBit) ? b1 | 0x0100 : b1][b2];
scan[1] = i & 0xfffffff;
AppleImageRenderer.renderScanline(img.getPixelWriter(), y * 2, scan, true, false);
AppleImageRenderer.renderScanline(img.getPixelWriter(), y * 2 + 1, scan, true, false);
}
return img;
}
}

View File

@ -0,0 +1,479 @@
package org.badvision.outlaweditor.apple;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.Canvas;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
/* Copyright (c) 2013 the authors listed at the following URL, and/or
the authors of referenced articles or incorporated external code:
http://en.literateprograms.org/Floyd-Steinberg_dithering_(Java)?action=history&offset=20080201121723
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Retrieved from: http://en.literateprograms.org/Floyd-Steinberg_dithering_(Java)?oldid=12476
* Original code by Spoon! (Feb 2008)
* Modified and adapted to work with Apple Game Server by Brendan Robert (2013)
* Some of the original code of this class was migrated over to the Palette class which already manages colors in AGS.
*/
public class FloydSteinbergDither {
static final int totalPasses = 6;
static final int nonErrorPasses = 1;
public static interface DitherCallback {
public void ditherCompleted(byte[] data);
}
public static void floydSteinbergDither(
Image img,
final org.badvision.outlaweditor.Platform platform,
final int startX,
final int startY,
final int width,
final int height,
final DitherCallback callback) {
final AppleImageRenderer renderer = (AppleImageRenderer) platform.imageRenderer;
final int errorWindow = 6;
final int overlap = 2;
final int pixelShift = -2;
final WritableImage source = getScaledImage(img, 560, 192);
AnchorPane pane = new AnchorPane();
Scene s = new Scene(pane);
final ImageView previewImage = new ImageView(source);
previewImage.setLayoutX(0);
previewImage.setLayoutY(0);
pane.getChildren().add(previewImage);
final Text status = new Text("Status");
status.setLayoutX(100);
status.setLayoutY(40);
status.setFont(Font.font("Arial", 18));
status.setStroke(Color.BLACK);
status.setEffect(new DropShadow(10.0, 0, 0, Color.WHITE));
pane.getChildren().add(status);
Stage progress = new Stage();
progress.setScene(s);
progress.show();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
final WritableImage keepScaled = new WritableImage(source.getPixelReader(), 560, 192);
WritableImage tmpScaled = new WritableImage(source.getPixelReader(), 560, 192);
final byte[] screen = renderer.createImageBuffer();
for (int i = 0; i < screen.length; i++) {
screen[i] = (byte) Math.max(255, Math.random() * 256.0);
}
// Platform.runLater(new Runnable() {
// @Override
// public void run() {
// previewImage.setCache(false);
// previewImage.setImage(keepScaled);
// }
// });
// try {
// while (previewImage.getImage() != keepScaled) {
// Thread.sleep(10);
// }
// } catch (InterruptedException ex) {
// Logger.getLogger(FloydSteinbergDither.class.getName()).log(Level.SEVERE, null, ex);
// }
int[] scanline = new int[3];
List<Integer> pixels = new ArrayList<>();
for (int pass = 0; pass < totalPasses; pass++) {
keepScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
tmpScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
final String statusText = "Pass: " + (pass + 1) + " of " + totalPasses;
Platform.runLater(new Runnable() {
@Override
public void run() {
status.setText(statusText);
}
});
System.out.println("Image type: " + platform.name());
for (int y = startY; y < height + startY; y++) {
for (int x = startX; x < startX + width; x += 2) {
Thread.yield();
switch (platform) {
case AppleII:
hiresDither(screen, y, x, scanline, pixels, tmpScaled, pass, keepScaled);
break;
case AppleII_DHGR:
doubleHiresDither(screen, y, x, scanline, pixels, tmpScaled, pass, keepScaled);
break;
}
}
}
Platform.runLater(new Runnable() {
@Override
public void run() {
callback.ditherCompleted(screen);
}
});
}
status.setText("Complete!");
}
void hiresDither(final byte[] screen, int y, int x, int[] scanline, List<Integer> pixels, WritableImage tmpScaled, int pass, final WritableImage keepScaled) {
int bb1 = screen[y * 40 + x] & 255;
int bb2 = screen[y * 40 + x + 1] & 255;
int next = bb2 & 127; // Preserve hi-bit so last pixel stays solid, it is a very minor detail
int prev = 0;
if (x > 0) {
prev = screen[y * 40 + x - 1] & 255;
}
if (x < 38) {
next = screen[y * 40 + x + 2] & 255;
}
// First byte, compared with a sliding window encompassing the previous byte, if any.
int leastError = Integer.MAX_VALUE;
for (int hi = 0; hi < 2; hi++) {
int b1 = (hi << 7) | (bb1 & 0x07f);
int totalError = 0;
for (int c = 0; c < 7; c++) {
// for (int c = 6; c >= 0; c--) {
int on = b1 | (1 << c);
int off = on ^ (1 << c);
// get values for "off"
int i = hgrToDhgr[0][prev];
scanline[0] = i & 0x0fffffff;
i = hgrToDhgr[(i & 0x10000000) != 0 ? off | 0x0100 : off][bb2];
scanline[1] = i & 0x0fffffff;
// scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff;
int errorOff = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
int off1 = pixels.get(c * 2 + 28 + pixelShift);
int off2 = pixels.get(c * 2 + 29 + pixelShift);
// get values for "on"
i = hgrToDhgr[0][prev];
scanline[0] = i & 0x0fffffff;
i = hgrToDhgr[(i & 0x10000000) != 0 ? on | 0x0100 : on][bb2];
scanline[1] = i & 0x0fffffff;
// scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff;
int errorOn = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
int on1 = pixels.get(c * 2 + 28 + pixelShift);
int on2 = pixels.get(c * 2 + 29 + pixelShift);
int[] col1;
int[] col2;
if (errorOff < errorOn) {
totalError += errorOff;
b1 = off;
col1 = Palette.parseIntColor(off1);
col2 = Palette.parseIntColor(off2);
} else {
totalError += errorOn;
b1 = on;
col1 = Palette.parseIntColor(on1);
col2 = Palette.parseIntColor(on2);
}
if (pass >= nonErrorPasses) {
propagateError(x * 14 + c * 2, y, tmpScaled, col1, true, false);
propagateError(x * 14 + c * 2 + 1, y, tmpScaled, col2, false, true);
}
}
if (totalError < leastError) {
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
leastError = totalError;
bb1 = b1;
} else {
tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
}
}
// Second byte, compared with a sliding window encompassing the next byte, if any.
leastError = Integer.MAX_VALUE;
for (int hi = 0; hi < 2; hi++) {
int b2 = (hi << 7) | (bb2 & 0x07f);
int totalError = 0;
for (int c = 0; c < 7; c++) {
// for (int c = 6; c >= 0; c--) {
int on = b2 | (1 << c);
int off = on ^ (1 << c);
// get values for "off"
int i = hgrToDhgr[bb1][off];
scanline[0] = i & 0xfffffff;
scanline[1] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0];
int errorOff = getError(x * 14 + 14 - overlap + c * 2, y, 14 - overlap + c * 2 + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
int off1 = pixels.get(c * 2 + 14 + pixelShift);
int off2 = pixels.get(c * 2 + 15 + pixelShift);
// get values for "on"
i = hgrToDhgr[bb1][on];
scanline[0] = i & 0xfffffff;
scanline[1] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0];
int errorOn = getError(x * 14 + 14 - overlap + c * 2, y, 14 - overlap + c * 2 + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
int on1 = pixels.get(c * 2 + 14 + pixelShift);
int on2 = pixels.get(c * 2 + 15 + pixelShift);
int[] col1;
int[] col2;
if (errorOff < errorOn) {
totalError += errorOff;
b2 = off;
col1 = Palette.parseIntColor(off1);
col2 = Palette.parseIntColor(off2);
} else {
totalError += errorOn;
b2 = on;
col1 = Palette.parseIntColor(on1);
col2 = Palette.parseIntColor(on2);
}
if (pass >= nonErrorPasses) {
propagateError(x * 14 + c * 2 + 14, y, tmpScaled, col1, true, false);
propagateError(x * 14 + c * 2 + 15, y, tmpScaled, col2, false, true);
}
}
if (totalError < leastError) {
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
leastError = totalError;
bb2 = b2;
} else {
tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
}
}
screen[y * 40 + x] = (byte) bb1;
screen[y * 40 + x + 1] = (byte) bb2;
}
void doubleHiresDither(final byte[] screen, int y, int x, int[] scanline, List<Integer> pixels, WritableImage tmpScaled, int pass, final WritableImage keepScaled) {
if (x % 4 != 0) {
return;
}
scanline[0] = 0;
if (x >= 4) {
scanline[0] = screen[y * 80 + x - 1] << 21;
}
scanline[1] = 0;
if (x < 76) {
scanline[2] = screen[y * 80 + x + 4];
}
int bytes[] = new int[]{
screen[y * 80 + x] & 255,
screen[y * 80 + x + 1] & 255,
screen[y * 80 + x + 2] & 255,
screen[y * 80 + x + 3] & 255
};
for (int xx = 0; xx < 4; xx++) {
// First byte, compared with a sliding window encompassing the previous byte, if any.
int leastError = Integer.MAX_VALUE;
int b1 = (bytes[xx] & 0x07f);
for (int c = 0; c < 7; c++) {
int on = b1 | (1 << c);
int off = on ^ (1 << c);
// get values for "off"
int i = (xx == 3) ? off : bytes[3] & 255;
i <<= 7;
i |= (xx == 2) ? off : bytes[2] & 255;
i <<= 7;
i |= (xx == 1) ? off : bytes[1] & 255;
i <<= 7;
i |= (xx == 0) ? off : bytes[0] & 255;
scanline[1] = i;
int errorOff = getError((x + xx) * 7 - overlap + c, y, 28 + (xx * 7) + c - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
int off1 = pixels.get(xx * 7 + c + 28 + pixelShift);
// get values for "on"
i = (xx == 3) ? on : bytes[3] & 255;
i <<= 7;
i |= (xx == 2) ? on : bytes[2] & 255;
i <<= 7;
i |= (xx == 1) ? on : bytes[1] & 255;
i <<= 7;
i |= (xx == 0) ? on : bytes[0] & 255;
scanline[1] = i;
int errorOn = getError((x + xx) * 7 - overlap + c, y, 28 + (xx * 7) + c - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
int on1 = pixels.get(xx * 7 + c + 28 + pixelShift);
int[] col1;
if (errorOff < errorOn) {
// totalError += errorOff;
b1 = off;
col1 = Palette.parseIntColor(off1);
} else {
// totalError += errorOn;
b1 = on;
col1 = Palette.parseIntColor(on1);
}
if (pass >= nonErrorPasses) {
propagateError((x + xx) * 7 + c, y, tmpScaled, col1, false, false);
}
}
// if (totalError < leastError) {
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
// leastError = totalError;
bytes[xx] = b1;
// } else {
// tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
}
screen[y * 80 + x] = (byte) bytes[0];
screen[y * 80 + x + 1] = (byte) bytes[1];
screen[y * 80 + x + 2] = (byte) bytes[2];
screen[y * 80 + x + 3] = (byte) bytes[3];
}
});
t.start();
// progress.close();
}
private static void propagateError(int x, int y, WritableImage img, int[] newColor, boolean propagateLeft, boolean propagateRight) {
int solid = 255 << 24;
for (int i = 0; i < 3; i++) {
if (x + 1 < img.getWidth() && propagateRight) {
int error = Palette.getComponent(img.getPixelReader().getArgb(x + 1, y), i) - newColor[i];
int c = img.getPixelReader().getArgb(x + 1, y);
img.getPixelWriter().setArgb(x + 1, y, solid | Palette.addError(c, i, (error * 7) >> 4));
}
if (y + 1 < img.getHeight()) {
int error = Palette.getComponent(img.getPixelReader().getArgb(x, y), i) - newColor[i];
if (x - 1 > 0 && propagateLeft) {
int c = img.getPixelReader().getArgb(x - 1, y + 1);
img.getPixelWriter().setArgb(x - 1, y + 1, solid | Palette.addError(c, i, (error * 3) >> 4));
}
int c = img.getPixelReader().getArgb(x, y + 1);
img.getPixelWriter().setArgb(x, y + 1, solid | Palette.addError(c, i, (error * 5) >> 4));
if (x + 1 < img.getWidth() && propagateRight) {
c = img.getPixelReader().getArgb(x + 1, y + 1);
img.getPixelWriter().setArgb(x + 1, y + 1, solid | Palette.addError(c, i, error >> 4));
}
}
}
}
private static int getError(int imageXStart, int y, int scanlineXStart, int window, final List<Integer> pixels, PixelReader source, int[] scanline) {
pixels.clear();
PixelWriter fakeWriter = new PixelWriter() {
@Override
public PixelFormat getPixelFormat() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void setArgb(int x, int y, int c) {
pixels.add(c);
}
@Override
public void setColor(int i, int i1, Color color) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public <T extends Buffer> void setPixels(int i, int i1, int i2, int i3, PixelFormat<T> pf, T t, int i4) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<ByteBuffer> pf, byte[] bytes, int i4, int i5) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<IntBuffer> pf, int[] ints, int i4, int i5) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void setPixels(int i, int i1, int i2, int i3, PixelReader reader, int i4, int i5) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
};
AppleImageRenderer.renderScanline(fakeWriter, 0, scanline, true, false);
double max = 0;
double min = Double.MAX_VALUE;
double total = 0;
List<Double> err = new ArrayList<>();
for (int p = 0; p < window; p++) {
if ((imageXStart + p) < 0 || (imageXStart + p) >= 560) {
continue;
}
int[] c1 = Palette.parseIntColor(pixels.get(scanlineXStart + p));
int[] c2 = Palette.parseIntColor(source.getArgb(imageXStart + p, y));
double dist = Palette.distance(c1, c2);
total += dist;
max = Math.max(dist, max);
min = Math.min(dist, min);
err.add(dist);
}
// double avg = total/((double) window);
// double range = max-min;
// double totalDev = 0.0;
// for (Double d : err) {
// totalDev = Math.pow(d-avg, 2);
//// errorTotal += (d-min)/range;
// }
// totalDev /= ((double) window);
// double stdDev = Math.sqrt(totalDev);
// return (int) (min+(avg*(stdDev/range)));
return (int) total;
}
// int currentPixel = source.getRGB(x, y);
//
// for (int i = 0; i < 3; i++) {
// int error = Palette.getComponent(currentPixel, i) - Palette.getComponent(closestColor, i);
// if (x + 1 < source.getWidth()) {
// int c = source.getRGB(x + 1, y);
// source.setRGB(x + 1, y, Palette.addError(c, i, (error * 7) >> 4));
// }
// if (y + 1 < source.getHeight()) {
// if (x - 1 > 0) {
// int c = source.getRGB(x - 1, y + 1);
// source.setRGB(x - 1, y + 1, Palette.addError(c, i, (error * 3) >> 4));
// }
// {
// int c = source.getRGB(x, y + 1);
// source.setRGB(x, y + 1, Palette.addError(c, i, (error * 5) >> 4));
// }
// if (x + 1 < source.getWidth()) {
// int c = source.getRGB(x + 1, y + 1);
// source.setRGB(x + 1, y + 1, Palette.addError(c, i, error >> 4));
// }
// }
// }
// }
// }
// return dest;
// }
private static WritableImage getScaledImage(Image img, int width, int height) {
Canvas c = new Canvas(width, height);
c.getGraphicsContext2D().drawImage(img, 0, 0, width, height);
WritableImage newImg = new WritableImage(width, height);
SnapshotParameters sp = new SnapshotParameters();
c.snapshot(sp, newImg);
return newImg;
}
}

View File

@ -0,0 +1,94 @@
package org.badvision.outlaweditor.apple;
import java.util.ArrayList;
import java.util.List;
public abstract class Palette {
public int MATCH_TOLERANCE = 0;
List colors = null;
public static int COLOR_DISTANCE_MAX = 0x2f708fd;
public Palette() {
MATCH_TOLERANCE = 64;
colors = new ArrayList();
initPalette();
}
protected abstract void initPalette();
public int[] getColor(int col) {
return (int[])colors.get(col);
}
public int getColorInt(int c) {
int col[] = getColor(c);
return toRGBInt(col);
}
public void addColor(int col[]) {
/* 45*/ colors.add(col);
}
public void addColor(int r, int g, int b) {
int col[] = new int[3];
col[0] = r;
col[1] = g;
col[2] = b;
addColor(col);
}
public int findColor(int color) {
int col[] = parseIntColor(color);
return findColor(col);
}
public static int[] parseIntColor(int color) {
return new int[] {getR(color), getG(color), getB(color)};
}
public static int toRGBInt(int[] col) {
return 0x10000 * col[0] + 256 * col[1] + col[2];
}
public int findColor(int color[]) {
int lastDiff = COLOR_DISTANCE_MAX;
int bestFit = 0;
for(int i = 0; i < colors.size(); i++) {
int test[] = (int[])colors.get(i);
int diff = (int)distance(color, test);
if(diff < lastDiff) {
lastDiff = diff;
bestFit = i;
}
}
return bestFit;
}
public static double distance(int color[], int test[]) {
return Math.pow(Math.abs(color[0] - test[0]), 3D) + Math.pow(Math.abs(color[1] - test[1]), 3D) + Math.pow(Math.abs(color[2] - test[2]), 3D);
}
public static int getR(int color) {
return ((color >> 16) & 255);
}
public static int getG(int color) {
return ((color >> 8) & 255);
}
public static int getB(int color) {
return color & 255;
}
public static int getComponent(int color, int component) {
switch (component) {
case 0: return getR(color);
case 1: return getG(color);
case 2: return getB(color);
default: return 0;
}
}
public static int addError(int color, int component, int error) {
int[] sourceColor = parseIntColor(color);
sourceColor[component] = Math.max(0, Math.min(255, sourceColor[component] + error));
return toRGBInt(sourceColor);
}
}

View File

@ -0,0 +1,59 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.apple.dhgr;
import org.badvision.outlaweditor.apple.*;
import javafx.event.EventHandler;
import javafx.scene.control.Menu;
import javafx.scene.input.MouseEvent;
import org.badvision.outlaweditor.Platform;
import org.badvision.outlaweditor.data.DataObserver;
import org.badvision.outlaweditor.data.xml.PlatformData;
/**
*
* @author brobert
*/
public class AppleDHGRImageEditor extends AppleImageEditor implements EventHandler<MouseEvent> {
public AppleDHGRImageEditor() {
xScale = 1;
yScale = 2;
changeCurrentPattern(FillPattern.Magenta);
}
@Override
public Platform getPlatform() {
return Platform.AppleII_DHGR;
}
@Override
public void buildPatternSelector(Menu tilePatternMenu) {
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
@Override
public void observedObjectChanged(FillPattern object) {
changeCurrentPattern(object);
}
});
}
public void changeCurrentPattern(FillPattern pattern) {
currentFillPattern = pattern.getBytePattern();
hiBitMatters = pattern.hiBitMatters;
lastActionX = -1;
lastActionY = -1;
}
@Override
public void redrawScanline(int y) {
currentImage = Platform.AppleII_DHGR.imageRenderer.renderScanline(currentImage, y, getImageData());
}
@Override
public int getWidth() {
return 80;
}
}

View File

@ -0,0 +1,89 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.apple.dhgr;
import org.badvision.outlaweditor.apple.*;
import javafx.scene.image.WritableImage;
import org.badvision.outlaweditor.Platform;
import org.badvision.outlaweditor.data.TileMap;
import org.badvision.outlaweditor.data.TileUtils;
import org.badvision.outlaweditor.data.xml.Tile;
/**
*
* @author brobert
*/
public class AppleDHGRImageRenderer extends AppleImageRenderer {
public static int BLACK = 0xff000000;
public static int WHITE = 0xffffffff;
// scanline is 20 16-bit words
// If mixed-mode is used then useColor needs to be an 80-boolean array indicating which bytes are supposed to be BW
@Override
public byte[] createImageBuffer() {
return new byte[80 * 192];
}
@Override
public WritableImage renderPreview(TileMap map, int startX, int startY) {
byte[] buffer = createImageBuffer();
int pos = 0;
for (int y = 0; y < 12; y++) {
for (int yy = 0; yy < 16; yy++) {
for (int x = 0; x < 20; x++) {
Tile t = map.get(x + startX, y + startY);
if (t == null) {
buffer[pos++] = 0;
buffer[pos++] = 0;
buffer[pos++] = 0;
buffer[pos++] = 0;
} else {
byte[] tileData = TileUtils.getPlatformData(t, Platform.AppleII_DHGR);
buffer[pos++] = tileData[yy * 2];
buffer[pos++] = tileData[yy * 2 + 1];
buffer[pos++] = tileData[yy * 2 + 2];
buffer[pos++] = tileData[yy * 2 + 3];
}
}
}
}
return renderImage(null, buffer);
}
// @Override
// public WritableImage renderImage(WritableImage img, byte[] rawImage) {
// if (img == null) {
// img = new WritableImage(560, 384);
// }
// for (int y = 0; y < 192; y++) {
// renderScanline(img, y, rawImage);
// }
// return img;
// }
@Override
public WritableImage renderScanline(WritableImage img, int y, byte[] rawImage) {
if (y < 0) return img;
int[] scanline = new int[20];
for (int x = 0; x < 80; x += 4) {
int scan = rawImage[y * 80 + x + 3] & 255;
scan <<=7;
scan |= rawImage[y * 80 + x + 2] & 255;
scan <<=7;
scan |= rawImage[y * 80 + x + 1] & 255;
scan <<=7;
scan |= rawImage[y * 80 + x] & 255;
scanline[x / 4] = scan;
}
renderScanline(img.getPixelWriter(), y * 2, scanline, true, false);
renderScanline(img.getPixelWriter(), y * 2 + 1, scanline, true, false);
return img;
}
//
// public static void renderScanline(PixelWriter img, int y, int[] scanline, boolean hiresMode, boolean mixedMode, boolean... useColor) {
// AppleImageRenderer.renderScanline(img, y, scanline, hiresMode, mixedMode, useColor);
// }
}

View File

@ -0,0 +1,248 @@
package org.badvision.outlaweditor.apple.dhgr;
import org.badvision.outlaweditor.apple.*;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.control.Menu;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import org.badvision.outlaweditor.Platform;
import org.badvision.outlaweditor.TileEditor;
import org.badvision.outlaweditor.data.DataObserver;
import org.badvision.outlaweditor.data.xml.Tile;
import org.badvision.outlaweditor.data.TileUtils;
/**
*
* @author brobert
*/
public class AppleDHGRTileEditor extends TileEditor {
FillPattern currentPattern = FillPattern.Magenta;
DrawMode drawMode = DrawMode.Toggle;
@Override
public void setEntity(Tile t) {
super.setEntity(t);
if (TileUtils.getPlatformData(t, Platform.AppleII_DHGR) == null) {
TileUtils.setPlatformData(t, Platform.AppleII_DHGR, new byte[64]);
}
}
@Override
public void buildEditorUI(AnchorPane tileEditorAnchorPane) {
grid = new Rectangle[28][16];
gridGroup = new Group();
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 28; x++) {
final int xx = x;
final int yy = y;
Rectangle rect = new Rectangle((zoom/2) * x + 5, zoom * y + 5, zoom/2 - 2, zoom - 2);
rect.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
performDragAction((int) (t.getX() / (zoom/2)), (int) (t.getY() / zoom));
}
});
rect.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
handleMouse(t, xx, yy);
lastActionX=-1;
lastActionY=-1;
}
});
grid[x][y] = rect;
gridGroup.getChildren().add(rect);
rect.setStrokeWidth(2);
}
}
Group mainGroup = new Group();
Rectangle background = new Rectangle(0, 0, zoom * 14 + 10, zoom * 16 + 10);
background.setFill(Color.BLACK);
mainGroup.getChildren().add(background);
mainGroup.getChildren().add(gridGroup);
tileEditorAnchorPane.getChildren().add(mainGroup);
TileUtils.redrawTile(getEntity());
observedObjectChanged(getEntity());
}
private void handleMouse(MouseEvent t, int x, int y) {
t.consume();
if (t.getButton() == null || t.getButton() == MouseButton.NONE) {
return;
}
performAction(t.isShiftDown() || t.isSecondaryButtonDown(), x, y);
}
int lastActionX = -1;
int lastActionY = -1;
public void performDragAction(int x, int y) {
performAction(false, x, y);
}
private void performAction(boolean alt, int x, int y) {
y = Math.min(Math.max(y,0), 15);
x = Math.min(Math.max(x,0), 27);
if (lastActionX == x && lastActionY == y) {
return;
}
lastActionX = x;
lastActionY = y;
switch (drawMode) {
case Toggle:
if (alt) {
toggleHiBit(x, y);
} else {
toggle(x, y);
}
break;
case Pencil1px:
int pat = currentPattern.getBytePattern()[y * 4 + (x / 7)];
set((pat & (1 << (x % 7))) != 0, x, y);
if (currentPattern.hiBitMatters) {
setHiBit(pat >= 128, x, y);
}
break;
case Pencil3px:
for (int xx = x-1; xx <= x+1; xx++) {
if (xx < 0 || xx >= 28) continue;
pat = currentPattern.getBytePattern()[y * 4 + (xx / 7)];
set((pat & (1 << (xx % 7))) != 0, xx, y);
if (currentPattern.hiBitMatters) {
setHiBit(pat >= 128, xx, y);
}
}
break;
}
observedObjectChanged(getEntity());
}
@Override
public void observedObjectChanged(Tile tile) {
recolorGrid(
TileUtils.getPlatformData(tile, Platform.AppleII_DHGR),
grid,
TileUtils.getImage(tile, Platform.AppleII_DHGR));
}
@Override
public void showShiftUI() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void setDrawMode(DrawMode drawMode) {
this.drawMode = drawMode;
lastActionX = -1;
lastActionY = -1;
}
@Override
public void buildPatternSelector(Menu tilePatternMenu) {
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
@Override
public void observedObjectChanged(FillPattern object) {
changeCurrentPattern(object);
}
});
}
public void changeCurrentPattern(FillPattern pat) {
currentPattern = pat;
lastActionX = -1;
lastActionY = -1;
}
@Override
public void unregister() {
}
int zoom = 25;
Group gridGroup;
Rectangle[][] grid;
public void toggleHiBit(int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
data[y * 4 + (x / 7)] ^= 128;
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
TileUtils.redrawTile(getEntity());
}
public void setHiBit(boolean on, int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
if (on) {
data[y * 4 + (x / 7)] |= 128;
} else {
data[y * 4 + (x / 7)] &= 127;
}
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
TileUtils.redrawTile(getEntity());
}
public void toggle(int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
data[y * 4 + (x / 7)] ^= (1 << (x % 7));
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
TileUtils.redrawTile(getEntity());
}
public void set(boolean on, int x, int y) {
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
data[y * 4 + (x / 7)] |= (1 << (x % 7));
if (!on) {
data[y * 4 + (x / 7)] ^= (1 << (x % 7));
}
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
TileUtils.redrawTile(getEntity());
}
public void recolorGrid(byte[] spriteData, Shape[][] grid, WritableImage img) {
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 28; x++) {
grid[x][y].setFill(img.getPixelReader().getColor(x, y * 2));
if ((spriteData[y * 4 + x / 7] & (1 << (x % 7))) != 0) {
grid[x][y].setStroke(Color.ANTIQUEWHITE);
} else {
Color stroke = Color.ANTIQUEWHITE;
switch (x%4) {
case 0: stroke=Color.BROWN;
break;
case 1: stroke=Color.BLUE;
break;
case 2: stroke=Color.GREEN;
break;
case 3: stroke=Color.CHOCOLATE;
break;
}
grid[x][y].setStroke(stroke);
}
}
}
}
@Override
public void copy() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void paste() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void select() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void selectNone() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

View File

@ -0,0 +1,64 @@
package org.badvision.outlaweditor.apple.dhgr;
import org.badvision.outlaweditor.apple.*;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import org.badvision.outlaweditor.TileRenderer;
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.*;
/**
*
* @author brobert
*/
public class AppleDHGRTileRenderer extends AppleTileRenderer {
@Override
public WritableImage redrawSprite(byte[] spriteData, WritableImage img) {
if (img == null) {
img = new WritableImage(28, 32);
}
if (spriteData == null) return img;
int[][] palette = useSolidPalette ? solidPalette : textPalette;
for (int y = 0; y < 16; y++) {
// int bleedOver = (spriteData[y * 2 + 1] & 128)==128? 256 : 0;
int scan = spriteData[y * 4 + 3] & 255;
scan <<=7;
scan |= spriteData[y * 4 + 2] & 255;
scan <<=7;
scan |= spriteData[y * 4 + 1] & 255;
scan <<=7;
scan |= spriteData[y * 4] & 255;
int last = (scan >> 26) & 3;
int keep = scan & 0xff;
scan <<= 2;
scan |= last;
for (int x = 0; x < 14; x++) {
boolean isHiBit = ((spriteData[y * 2 + x / 7] & 128) != 0);
int col1 = palette[ (x & 1) << 1][scan & 0x07f];
Color color1 = Color.rgb(getRed(col1), getGreen(col1), getBlue(col1));
scan >>= 1;
if (x == 12) {
scan = scan & (isHiBit ? 0x07f : 0x01f) | (keep << 5);
}
int col2 = palette[ ((x & 1) << 1) + 1][scan & 0x07f];
Color color2 = Color.rgb(getRed(col2), getGreen(col2), getBlue(col2));
scan >>= 1;
img.getPixelWriter().setColor(x * 2, y * 2, color1);
img.getPixelWriter().setColor(x * 2, y * 2 + 1, color1);
img.getPixelWriter().setColor(x * 2 + 1, y * 2, color2);
img.getPixelWriter().setColor(x * 2 + 1, y * 2 + 1, color2);
}
}
return img;
}
@Override
public int getWidth() {
return 28;
}
@Override
public int getHeight() {
return 32;
}
}

View File

@ -0,0 +1,173 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.apple.dhgr;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import org.badvision.outlaweditor.Platform;
import org.badvision.outlaweditor.data.DataObserver;
import org.badvision.outlaweditor.data.TileUtils;
import org.badvision.outlaweditor.data.xml.Tile;
/**
*
* @author brobert
*/
public enum FillPattern {
Magenta(true, 4, false,
"+---+---+---+---+---+---+---"),
Pink(true, 4, false,
"++-+++-+++-+++-+++-+++-+++-+"),
Purple(true, 4, false,
"++--++--++--++--++--++--++--"),
LtBlue(true, 4, false,
"+++-+++-+++-+++-+++-+++-+++-"),
MedBlue(true, 4, false,
"-++--++--++--++--++--++--++-"),
Blue(true, 4, false,
"-+---+---+---+---+---+---+--"),
Green(true, 4, false,
"--+---+---+---+---+---+---+-"),
LtGreen(true, 4, false,
"--++--++--++--++--++--++--++"),
Aqua(true, 4, false,
"-+++-+++-+++-+++-+++-+++-+++"),
Yellow(true, 4, false,
"+-+++-+++-+++-+++-+++-+++-++"),
Orange(true, 4, false,
"+--++--++--++--++--++--++--+"),
Brown(true, 4, false,
"---+---+---+---+---+---+---+"),
Black(false, 1, false, "-------"),
White(false, 1, false, "+++++++"),
Grey1(false, 2, false,
"+-+-+-+-+-+-+-"),
Grey2(false, 2, false,
"-+-+-+-+-+-+-+");
// ,
// BW3(false, 4, false,
// "+--++--++--++--++--++--++--+",
// "-++--++--++--++--++--++--++-"
// ),
// BW4(false, 4, false,
// "-++--++--++--++--++--++--++-",
// "+--++--++--++--++--++--++--+"
// );
public static int[] bitmask(boolean hiBit, String pattern) {
int[] out = new int[pattern.length()/7];
int place = 1;
int pos = 0;
int value = hiBit ? 128 : 0;
for (int i=0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (c == '1' || c == '+') {
value |= place;
}
out[pos] = value;
place <<= 1;
if (place >= 128) {
place = 1;
value = hiBit ? 128 : 0;
pos++;
}
}
return out;
}
public static Integer[] buildPattern(boolean hiBit, String... pattern) {
List<Integer> out = new ArrayList<>();
for (String s : pattern) {
for (int i : bitmask(hiBit, s)) {
out.add(i);
}
}
return out.toArray(new Integer[0]);
}
public static void buildMenu(Menu target, final DataObserver<FillPattern> dataObserver) {
target.getItems().clear();
for (final FillPattern fill : FillPattern.values()) {
MenuItem i = new MenuItem(fill.name(), new ImageView(fill.getPreview()));
i.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
dataObserver.observedObjectChanged(fill);
}
});
target.getItems().add(i);
}
}
Integer[] pattern;
int[] bytePattern;
int width;
boolean hiBitMatters;
WritableImage preview;
private FillPattern(boolean hiBitMatters, int width, boolean hiBit, String... pattern) {
this.pattern = buildPattern(hiBit, pattern);
this.width = width;
this.hiBitMatters = hiBitMatters;
this.bytePattern = getBytePattern();
}
public WritableImage getPreview() {
if (preview == null) {
preview = renderPreview(null, bytePattern);
}
return preview;
}
public int[] getBytePattern() {
int[] out = new int[16 * 4];
int pos = 0;
int patternHeight = pattern.length / width;
for (int y = 0; y < 16; y++) {
int yOffset = (y % patternHeight) * width;
for (int x = 0; x < 4; x++) {
out[pos++] = pattern[yOffset + (x % width)];
}
}
return out;
}
public static int[] interleave(int[] pat1, int[] pat2) {
int[] out = Arrays.copyOf(pat1, pat1.length);
for (int y = 1; y < 16; y += 2) {
int offset = y * 4;
for (int x = 0; x < 4; x++) {
out[offset + x] = pat2[offset + x];
}
}
return out;
}
public static int[] fromTile(Tile t) {
byte[] raw = TileUtils.getPlatformData(t, Platform.AppleII_DHGR);
int[] out = new int[16 * 4];
int pos = 0;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 4; x++) {
out[pos++] = raw[y * 4 + x] & 0x0ff;
}
}
return out;
}
public static WritableImage renderPreview(WritableImage img, int[] pattern) {
byte[] b = new byte[pattern.length];
for (int i=0; i < pattern.length; i++) {
b[i]=(byte) pattern[i];
}
return Platform.AppleII_DHGR.tileRenderer.redrawSprite(b, null);
}
}

View File

@ -0,0 +1,11 @@
package org.badvision.outlaweditor.data;
import org.badvision.outlaweditor.*;
/**
*
* @author brobert
*/
public interface DataObserver<T> {
public void observedObjectChanged(T object);
}

View File

@ -0,0 +1,46 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.data;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.badvision.outlaweditor.data.TileUtils.clear;
/**
*
* @author brobert
*/
public class DataProducer {
static Map<Object, List<WeakReference<DataObserver>>> observers;
static {
clear();
}
public static void clear() {
observers = new ConcurrentHashMap<>();
}
public static List<WeakReference<DataObserver>> getObservers(Object o) {
if (observers.get(o) == null) {
observers.put(o, new ArrayList<WeakReference<DataObserver>>());
}
return observers.get(o);
}
public static void addObserver(Object o, DataObserver observer) {
getObservers(o).add(new WeakReference<>(observer));
}
public static void notifyObservers(Object o) {
for (WeakReference<DataObserver> ref : getObservers(o)) {
DataObserver observer = ref.get();
if (observer != null) {
observer.observedObjectChanged(o);
}
}
}
}

View File

@ -0,0 +1,111 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.badvision.outlaweditor.data;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.adapter.JavaBeanBooleanProperty;
import javafx.beans.property.adapter.JavaBeanBooleanPropertyBuilder;
import javafx.beans.property.adapter.JavaBeanIntegerProperty;
import javafx.beans.property.adapter.JavaBeanIntegerPropertyBuilder;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import org.badvision.outlaweditor.ApplicationUIController;
/**
*
* @author brobert
*/
public class PropertyHelper {
public static JavaBeanIntegerProperty intProp(Object t, String fieldName) throws NoSuchMethodException {
return new JavaBeanIntegerPropertyBuilder().bean(t).name(fieldName).build();
}
public static JavaBeanBooleanProperty boolProp(Object t, String fieldName) throws NoSuchMethodException {
return new JavaBeanBooleanPropertyBuilder().bean(t).name(fieldName).build();
}
public static Property categoryProp(final Object t, String fieldName) throws NoSuchMethodException {
final String camel = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
SimpleStringProperty prop = new SimpleStringProperty() {
@Override
public String get() {
try {
Method getter = t.getClass().getMethod("get" + camel);
List<String> list = (List<String>) getter.invoke(t);
String out = "";
for (String s : list) {
if (out.length() > 0) {
out += ",";
}
out += s;
}
return out;
} catch (NoSuchMethodException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalArgumentException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
@Override
public void set(String string) {
try {
Method getter = t.getClass().getMethod("get" + camel);
List<String> list = (List<String>) getter.invoke(t);
list.clear();
Collections.addAll(list, string.split(","));
} catch (NoSuchMethodException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalArgumentException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
return prop;
}
public static JavaBeanStringProperty stringProp(Object t, String fieldName) throws NoSuchMethodException {
return new JavaBeanStringPropertyBuilder().bean(t).name(fieldName).build();
}
static private Map<Property, Property> boundProperties = new HashMap<>();
static public void bind(Property formProp, Property sourceProp) {
if (boundProperties.containsKey(formProp)) {
formProp.unbindBidirectional(boundProperties.get(formProp));
boundProperties.get(formProp).unbindBidirectional(formProp);
boundProperties.get(formProp).unbind();
boundProperties.remove(formProp);
}
formProp.unbind();
if (sourceProp != null) {
formProp.bindBidirectional(sourceProp);
boundProperties.put(formProp, sourceProp);
}
}
}

View File

@ -0,0 +1,28 @@
package org.badvision.outlaweditor.data;
import java.io.Serializable;
import java.util.Collection;
import org.badvision.outlaweditor.data.xml.Script;
import org.badvision.outlaweditor.data.xml.Tile;
/**
*
* @author brobert
*/
public class TileInstance implements Serializable {
transient Tile tile;
String tileId;
public TileInstance(Tile t) {
tile = t;
if (t != null) {
tileId = t.getId();
}
}
public Tile getTile() {
if (tile == null && tileId != null) {
tile = TilesetUtils.getTileById(tileId);
}
return tile;
}
}

View File

@ -0,0 +1,132 @@
package org.badvision.outlaweditor.data;
import org.badvision.outlaweditor.Platform;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javafx.scene.image.WritableImage;
import javax.swing.JOptionPane;
import javax.xml.bind.JAXBElement;
import org.badvision.outlaweditor.Application;
import org.badvision.outlaweditor.UIAction;
import org.badvision.outlaweditor.data.xml.Map;
import org.badvision.outlaweditor.data.xml.Map.Chunk;
import org.badvision.outlaweditor.data.xml.ObjectFactory;
import org.badvision.outlaweditor.data.xml.Tile;
/**
*
* @author brobert
*/
public class TileMap extends ArrayList<ArrayList<Tile>> implements Serializable {
public static final long serialVersionUID = 6486309334559843742L;
Map backingMap;
boolean backingMapStale;
int width;
int height;
public TileMap(Map m) {
backingMapStale = false;
width = 0;
height = 0;
loadFromMap(m);
}
public Tile get(int x, int y) {
if (size() <= y || get(y) == null) {
return null;
}
if (get(y).size() <= x) {
return null;
}
return get(y).get(x);
}
public void put(int x, int y, Tile t) {
width = Math.max(x+1, width);
height = Math.max(y+1, height);
for (int i = size(); i <= y; i++) {
add(null);
}
if (get(y) == null) {
set(y, new ArrayList<Tile>());
}
List<Tile> row = get(y);
for (int i = row.size(); i <= x; i++) {
row.add(null);
}
row.set(x, t);
backingMapStale = true;
}
public Map getBackingMap() {
return backingMap;
}
public void updateBackingMap() {
ObjectFactory f = new ObjectFactory();
backingMap.getChunk().clear();
Chunk c = new Map.Chunk();
c.setX(0);
c.setY(0);
for (int y = 0; y < height; y++) {
List<String> row = new ArrayList<>();
for (int x = 0; x < width; x++) {
Tile t = get(x, y);
if (t == null) {
row.add(NULL_TILE_ID);
} else {
row.add(TileUtils.getId(t));
}
}
c.getRow().add(f.createMapChunkRow(row));
}
backingMap.getChunk().add(c);
backingMapStale = false;
}
private void loadFromMap(Map m) {
clear();
width = 0;
height = 0;
Set<Tile> unknownTiles = new HashSet<>();
for (Chunk c : m.getChunk()) {
int y = c.getY();
for (JAXBElement<List<String>> row : c.getRow()) {
int x = c.getX();
for (String tileId : row.getValue()) {
Tile t = null;
if (!isNullTile(tileId)) {
t = TilesetUtils.getTileById(tileId);
if (t == null) {
t = new Tile();
unknownTiles.add(t);
Platform p = Application.currentPlatform;
WritableImage img = UIAction.getBadImage(p.tileRenderer.getWidth(), p.tileRenderer.getHeight());
TileUtils.setImage(t, p, img);
}
}
put(x, y, t);
x++;
}
y++;
}
}
if (!unknownTiles.isEmpty()) {
int numMissing = unknownTiles.size();
JOptionPane.showMessageDialog(null, (numMissing > 1
? "There were " + numMissing + " missing tiles." : "There was a missing tile.")
+ "Blank placeholders have been added.");
}
backingMap = m;
backingMapStale = false;
}
public static String NULL_TILE_ID = "_";
public static boolean isNullTile(String tileId) {
return tileId.equalsIgnoreCase(NULL_TILE_ID);
}
}

View File

@ -0,0 +1,94 @@
package org.badvision.outlaweditor.data;
import org.badvision.outlaweditor.Platform;
import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javafx.scene.image.WritableImage;
import org.badvision.outlaweditor.data.xml.*;
/**
*
* @author brobert
*/
public class TileUtils {
static Map<String, Map<Platform, WritableImage>> display;
static {
clear();
}
public static void clear() {
display = new ConcurrentHashMap<>();
}
public static Tile newTile() {
Tile t = new Tile();
t.setObstruction(false);
updateId(t);
return t;
}
public static void updateId(Tile t) {
if (t.getId() == null) {
t.setId("TILE" + Integer.toHexString((int) (Math.random() * 1000000.0)));
}
}
public static String getId(Tile t) {
if (t.getId() == null) {
updateId(t);
}
return t.getId();
}
public static Map<Platform, WritableImage> getDisplay(Tile t) {
if (display.get(getId(t)) == null) {
display.put(getId(t), new EnumMap<Platform, WritableImage>(Platform.class));
}
return display.get(getId(t));
}
public static void redrawTile(Tile t) {
Map<Platform, WritableImage> displays = getDisplay(t);
for (PlatformData d : t.getDisplayData()) {
Platform p = Platform.valueOf(d.getPlatform());
displays.put(p, p.tileRenderer.redrawSprite(d.getValue(), displays.get(p)));
}
DataProducer.notifyObservers(t);
}
public static byte[] getPlatformData(Tile t, Platform p) {
for (PlatformData d : t.getDisplayData()) {
if (d.getPlatform().equalsIgnoreCase(p.name())) {
return d.getValue();
}
}
byte[] out = new byte[p.dataHeight*p.dataWidth];
setPlatformData(t, p, out);
return out;
}
public static WritableImage getImage(Tile t, Platform p) {
Map<Platform, WritableImage> displays = getDisplay(t);
byte[] data = getPlatformData(t, p);
return displays.put(p, p.tileRenderer.redrawSprite(data, displays.get(p)));
}
public static void setImage(Tile t, Platform p, WritableImage img) {
Map<Platform, WritableImage> displays = getDisplay(t);
displays.put(p, img);
}
public static void setPlatformData(Tile t, Platform p, byte[] b) {
for (PlatformData d : t.getDisplayData()) {
if (d.getPlatform().equalsIgnoreCase(p.name())) {
d.setValue(b);
return;
}
}
PlatformData d = new PlatformData();
d.setPlatform(p.name());
d.setValue(b);
t.getDisplayData().add(d);
}
}

View File

@ -0,0 +1,50 @@
package org.badvision.outlaweditor.data;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.badvision.outlaweditor.Application;
import org.badvision.outlaweditor.data.xml.Tile;
/**
*
* @author brobert
*/
public class TilesetUtils implements Serializable {
public static void addObserver(DataObserver o) {
DataProducer.addObserver(TilesetUtils.class, o);
}
public static boolean add(Tile e) {
boolean output = Application.gameData.getTile().add(e);
DataProducer.notifyObservers(TilesetUtils.class);
return output;
}
public static void clear() {
Application.gameData.getTile().clear();
DataProducer.notifyObservers(TilesetUtils.class);
}
public static void remove(Tile t) {
Application.gameData.getTile().remove(t);
DataProducer.notifyObservers(TilesetUtils.class);
}
// The tileset should have been a map in retrospect but now it has to
// stay this way to preserve compatibility with existing files.
static Map<String, Tile> lookup;
public static Tile getTileById(String tileId) {
if (lookup == null || (lookup.get(tileId) == null && !lookup.containsKey(tileId))) {
lookup = new HashMap<>();
for (Tile t : Application.gameData.getTile()) {
lookup.put(TileUtils.getId(t), t);
}
if (lookup.get(tileId) == null) {
lookup.put(tileId, null);
}
}
return lookup.get(tileId);
}
}

View File

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.collections.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?scenebuilder-stylesheet applicationui.css?>
<AnchorPane id="AnchorPane" prefHeight="500.0" prefWidth="800.0" styleClass="mainFxmlClass" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="org.badvision.outlaweditor.ApplicationUIControllerImpl">
<children>
<VBox prefHeight="500.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<MenuBar>
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" onAction="#onFileOpen" text="Open" />
<MenuItem mnemonicParsing="false" onAction="#onFileSave" text="Save" />
<MenuItem mnemonicParsing="false" onAction="#onFileSaveAs" text="Save As..." />
<MenuItem mnemonicParsing="false" onAction="#onFileQuit" text="Quit" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" onAction="#onEditSelect" text="Select" />
<MenuItem mnemonicParsing="false" onAction="#onEditCopy" text="Copy" />
<MenuItem mnemonicParsing="false" onAction="#onEditPaste" text="Paste" />
<Menu mnemonicParsing="false" text="Change Platform">
<items>
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleSolid" text="Apple (solid)" />
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleText" text="Apple (text-friendly)" />
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleDHGRSolid" text="Apple (DHGR solid)" />
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleDHGRText" text="Apple (DHGR text)" />
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformC64" text="C64" />
</items>
</Menu>
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" onAction="#onHelpAbout" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<TabPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="455.0" prefWidth="676.9998779296875" rotateGraphic="false" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
<tabs>
<Tab onSelectionChanged="#tileTabActivated" text="Tiles">
<content>
<AnchorPane id="tilesTab" minHeight="0.0" minWidth="0.0" prefHeight="420.0000999999975" prefWidth="677.0">
<children>
<VBox prefHeight="420.0000999999975" prefWidth="677.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<ToolBar prefWidth="686.0">
<items>
<Label text="Tile:" />
<ComboBox fx:id="tileSelector" minWidth="125.0" onAction="#onCurrentTileSelected">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Item 1" />
<String fx:value="Item 2" />
<String fx:value="Item 3" />
</FXCollections>
</items>
</ComboBox>
<Button mnemonicParsing="false" onAction="#onTileCreatePressed" text="Create new" />
<Button mnemonicParsing="false" onAction="#onTileExportPressed" text="Export" />
<Button mnemonicParsing="false" onAction="#onTileClonePressed" prefWidth="64.9998779296875" text="Clone" />
<Button mnemonicParsing="false" onAction="#onTileDeletePressed" text="Delete" />
<MenuButton mnemonicParsing="false" text="Tools">
<items>
<Menu mnemonicParsing="false" text="Pattern" fx:id="tilePatternMenu" />
<Menu mnemonicParsing="false" text="Draw mode">
<items>
<MenuItem mnemonicParsing="false" onAction="#tileBitMode" text="Bit Toggle" />
<MenuItem mnemonicParsing="false" onAction="#tileDraw1BitMode" text="1 bit-wide" />
<MenuItem mnemonicParsing="false" onAction="#tileDraw3BitMode" text="3 bit wide" />
</items>
</Menu>
<MenuItem mnemonicParsing="false" onAction="#tileShift" text="Shift..." />
</items>
</MenuButton>
</items>
</ToolBar>
<HBox prefHeight="387.0" prefWidth="677.0" VBox.vgrow="ALWAYS">
<children>
<AnchorPane id="imageDetailsPane" opacity="1.0" prefHeight="200.0" prefWidth="200.0" rotate="0.0" style="">
<children>
<Label layoutX="4.0" layoutY="14.0" text="Name" />
<TextField id="" fx:id="tileNameField" layoutX="53.0" layoutY="11.0" prefWidth="147.0" />
<Label layoutX="4.0" layoutY="36.0" text="ID" />
<TextField fx:id="tileIdField" layoutX="53.0" layoutY="33.0" prefWidth="147.0" />
<Label layoutX="4.0" layoutY="58.0" text="Category" />
<TextField fx:id="tileCategoryField" layoutX="74.0" layoutY="55.0" prefWidth="126.0" />
<CheckBox fx:id="tileObstructionField" layoutX="4.0" layoutY="78.0" mnemonicParsing="false" text="Physical Obstruction" />
</children>
</AnchorPane>
<AnchorPane fx:id="tileEditorAnchorPane" prefHeight="387.0" prefWidth="477.0000999999975" HBox.hgrow="ALWAYS" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
</content>
</Tab>
<Tab onSelectionChanged="#mapTabActivated" text="Maps">
<content>
<AnchorPane id="mapsTab" minHeight="0.0" minWidth="0.0" prefHeight="-1.0" prefWidth="-1.0">
<children>
<VBox prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<ToolBar prefWidth="677.0">
<items>
<Label text="Map:" />
<ComboBox fx:id="mapSelect" minWidth="125.0" onAction="#onMapSelected">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Item 1" />
<String fx:value="Item 2" />
<String fx:value="Item 3" />
</FXCollections>
</items>
</ComboBox>
<Button mnemonicParsing="false" onAction="#onMapCreatePressed" text="Create new" />
<Button mnemonicParsing="false" onAction="#onMapClonePressed" text="Clone" />
<Button mnemonicParsing="false" onAction="#onMapExportPressed" text="Export" />
<Button mnemonicParsing="false" onAction="#onMapDeletePressed" text="Delete" />
<Button mnemonicParsing="false" onAction="#onMapPreviewPressed" text="Preview" />
<MenuButton mnemonicParsing="false" text="Tools">
<items>
<Menu mnemonicParsing="false" text="Change tile" fx:id="mapSelectTile" />
<Menu mnemonicParsing="false" text="Draw mode">
<items>
<MenuItem mnemonicParsing="false" onAction="#mapDraw1" text="Radius 1" />
<MenuItem mnemonicParsing="false" onAction="#mapDraw3" text="Radius 3" />
<MenuItem mnemonicParsing="false" onAction="#mapDraw5" text="Radius 5" />
<MenuItem mnemonicParsing="false" onAction="#mapDrawFilledRectMode" text="Filled Rectangle" />
</items>
</Menu>
<MenuItem mnemonicParsing="false" onAction="#mapTogglePanZoom" text="Toggle pan/zoom controls" />
</items>
</MenuButton>
</items>
</ToolBar>
<HBox prefHeight="389.0" prefWidth="677.0" VBox.vgrow="ALWAYS">
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="NEVER">
<children>
<Label text="Name" AnchorPane.leftAnchor="4.0" AnchorPane.topAnchor="14.0" />
<TextField id="mapNameFiled" fx:id="mapNameField" layoutX="53.0" layoutY="11.0" prefWidth="147.0" />
<TextField fx:id="mapWidthField" layoutX="53.0" layoutY="33.0" prefWidth="147.0" />
<Label layoutX="4.0" layoutY="36.0" text="Width" />
<TextField fx:id="mapHeightField" layoutX="53.0" layoutY="55.0" prefWidth="147.0" />
<Label layoutX="4.0" layoutY="58.0" text="Height" />
<CheckBox fx:id="mapWrapAround" alignment="CENTER_LEFT" contentDisplay="RIGHT" layoutX="4.0" layoutY="77.0" mnemonicParsing="false" text="Wrap at edges" />
<Separator layoutX="4.0" layoutY="101.0" prefWidth="189.0" />
<Label layoutX="4.0" layoutY="108.0" text="Scripts" />
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="232.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="157.0">
<content>
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="200.0" prefWidth="200.0">
<children>
<ListView fx:id="mapScriptsList" orientation="VERTICAL" prefHeight="217.0" prefWidth="199.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</content>
</ScrollPane>
<ToolBar layoutX="0.0" layoutY="124.0" prefWidth="200.0">
<items>
<Button mnemonicParsing="false" onAction="#onMapScriptAddPressed" text="+" />
<Button mnemonicParsing="false" onAction="#onMapScriptDeletePressed" text="-" />
<Button mnemonicParsing="false" onAction="#onMapScriptClonePressed" text="Clone" />
</items>
</ToolBar>
</children>
</AnchorPane>
<AnchorPane fx:id="mapEditorAnchorPane" prefHeight="389.0" prefWidth="477.0000999999975" HBox.hgrow="SOMETIMES">
<children>
<Button alignment="TOP_CENTER" layoutX="265.0" mnemonicParsing="false" onAction="#scrollMapUp" styleClass="moveButton" text="Up" AnchorPane.topAnchor="5.0" />
<Button layoutY="185.0" mnemonicParsing="false" onAction="#scrollMapLeft" rotate="270.0" styleClass="moveButton" text="Left" AnchorPane.leftAnchor="-20.0" />
<Button layoutX="265.0" mnemonicParsing="false" onAction="#scrollMapDown" rotate="180.0" styleClass="moveButton" text="Down" AnchorPane.bottomAnchor="5.0" />
<Button layoutY="175.0" mnemonicParsing="false" onAction="#scrollMapRight" rotate="90.0" styleClass="moveButton" text="Right" AnchorPane.rightAnchor="-15.0" />
<Button mnemonicParsing="false" onAction="#mapZoomIn" styleClass="zoomInButton" text="+" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="5.0" />
<Button mnemonicParsing="false" onAction="#mapZoomOut" prefHeight="23.999908447265625" styleClass="zoomOutButton" text="-" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="30.0" />
</children>
</AnchorPane>
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
</content>
</Tab>
<Tab onSelectionChanged="#imageTabActivated" text="Images">
<content>
<AnchorPane id="tilesTab" minHeight="0.0" minWidth="0.0" prefHeight="420.0" prefWidth="677.0">
<children>
<VBox prefHeight="420.0000999999975" prefWidth="677.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<ToolBar prefWidth="686.0">
<items>
<Label text="Image:" />
<ComboBox id="tileSelect" fx:id="imageSelector" onAction="#onImageSelected">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Item 1" />
<String fx:value="Item 2" />
<String fx:value="Item 3" />
</FXCollections>
</items>
</ComboBox>
<Button mnemonicParsing="false" onAction="#onImageCreatePressed" text="Create new" />
<Button mnemonicParsing="false" onAction="#onImageClonePressed" text="Clone" />
<Button mnemonicParsing="false" onAction="#onImageExportPressed" text="Export" />
<Button mnemonicParsing="false" onAction="#onImageDeletePressed" text="Delete" />
<MenuButton mnemonicParsing="false" text="Tools">
<items>
<Menu mnemonicParsing="false" text="Pattern" fx:id="imagePatternMenu" />
<Menu mnemonicParsing="false" text="Draw mode">
<items>
<MenuItem mnemonicParsing="false" onAction="#imageBitMode" text="Bit Toggle mode" />
<MenuItem mnemonicParsing="false" onAction="#imageDraw1BitMode" text="1 bit-wide" />
<MenuItem mnemonicParsing="false" onAction="#imageDraw3BitMode" text="3 bit wide" />
<MenuItem mnemonicParsing="false" onAction="#imageDraw5BitMode" text="5 bit wide" />
<MenuItem mnemonicParsing="false" onAction="#imageDrawFilledRectMode" text="Filled Rectangle" />
</items>
</Menu>
<MenuItem mnemonicParsing="false" onAction="#imageTogglePanZoom" text="Toggle pan/zoom controls" />
<MenuItem mnemonicParsing="false" onAction="#imageShift" text="Shift..." />
</items>
</MenuButton>
</items>
</ToolBar>
<HBox prefHeight="387.0" prefWidth="677.0" VBox.vgrow="ALWAYS">
<children>
<AnchorPane id="imageDetailsPane" opacity="1.0" prefHeight="200.0" prefWidth="200.0" rotate="0.0" style="">
<children>
<Label layoutX="4.0" layoutY="14.0" text="Name" />
<TextField id="" fx:id="imageNameField" layoutX="53.0" layoutY="11.0" prefWidth="147.0" />
<Label layoutX="4.0" layoutY="36.0" text="Category" />
<TextField id="tileCategoryField" fx:id="imageCategoryField" layoutX="74.0" layoutY="33.0" prefWidth="126.0" />
<TextField fx:id="imageWidthField" layoutY="55.0" prefWidth="145.0" AnchorPane.leftAnchor="53.0" AnchorPane.rightAnchor="0.0" />
<Label layoutX="4.0" layoutY="58.0" text="Width" />
<TextField id="imageWidthField" fx:id="imageHeightField" layoutX="53.0" layoutY="77.0" prefWidth="147.0" />
<Label layoutX="4.0" layoutY="83.0" text="Height" />
</children>
</AnchorPane>
<AnchorPane id="mapEditorAnchorPane" fx:id="imageEditorAnchorPane" prefHeight="389.0" prefWidth="477.0" HBox.hgrow="ALWAYS">
<children>
<Button layoutX="236.0" mnemonicParsing="false" onAction="#scrollImageUp" styleClass="moveButton" text="Up" AnchorPane.topAnchor="5.0" />
<Button layoutY="185.0" mnemonicParsing="false" onAction="#scrollImageLeft" rotate="270.0" styleClass="moveButton" text="Left" AnchorPane.leftAnchor="-20.0" />
<Button layoutX="236.0" mnemonicParsing="false" onAction="#scrollImageDown" rotate="180.0" styleClass="moveButton" text="Down" AnchorPane.bottomAnchor="5.0" />
<Button layoutY="175.0" mnemonicParsing="false" onAction="#scrollImageRight" rotate="90.0" styleClass="moveButton" text="Right" AnchorPane.rightAnchor="-15.0" />
<Button mnemonicParsing="false" onAction="#imageZoomIn" styleClass="zoomInButton" text="+" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="5.0" />
<Button mnemonicParsing="false" onAction="#imageZoomOut" prefHeight="23.999908447265625" styleClass="zoomOutButton" text="-" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="30.0" />
</children>
</AnchorPane>
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
</content>
</Tab>
</tabs>
</TabPane>
</children>
</VBox>
</children>
<stylesheets>
<URL value="@styles/applicationui.css" />
</stylesheets>
</AnchorPane>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import org.tbee.javafx.scene.layout.fxml.MigPane?>
<?import layouttests.migfxml.sample1.*?>
<MigPane id="rootPane" fx:controller="org.badvision.outlaweditor.HelloController"
styleClass="main-panel"
layout="insets 20"
cols="[label, pref!][grow, 50::]"
rows=""
xmlns:fx="http://javafx.com/fxml">
<Label text="First Name:" /> <TextField fx:id="firstNameField" prefColumnCount="30" MigPane.cc="growx, wrap" />
<Label text="Last Name:" /> <TextField fx:id="lastNameField" prefColumnCount="30" MigPane.cc="growx, wrap" />
<Button text="Say Hello" onAction="#sayHello" MigPane.cc="skip, gap :push, gaptop 15, wrap" />
<Label fx:id="messageLabel" styleClass="hello-message" MigPane.cc="span, growx, gaptop 15" />
</MigPane >

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

View File

@ -0,0 +1,99 @@
<?xml version="1.0"?>
<!--
To change this template, choose Tools | Templates
and open the template in the editor.
-->
<xs:schema version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" targetNamespace="outlaw" xmlns:tns="outlaw">
<xs:complexType name="image">
<xs:sequence>
<xs:element name="category" type="xs:string" maxOccurs="unbounded" minOccurs="0"/>
<xs:element name="displayData" type="tns:platformData" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN"/>
</xs:complexType>
<xs:complexType name="tile">
<xs:complexContent>
<xs:extension base="tns:image">
<xs:attribute name="id" type="xs:string" use="required"/>
<xs:attribute name="obstruction" type="xs:boolean"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="platformData">
<xs:simpleContent>
<xs:extension base="xs:hexBinary">
<xs:attribute name="platform" type="xs:string" use="required"/>
<xs:attribute name="width" type="xs:int" use="required"/>
<xs:attribute name="height" type="xs:int" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="script">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="description" type="xs:string" minOccurs="0"/>
<xs:element name="script" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="locationScript">
<xs:complexContent>
<xs:extension base="tns:script">
<xs:attribute name="x" type="xs:int"/>
<xs:attribute name="y" type="xs:int"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="intervalScript">
<xs:complexContent>
<xs:extension base="tns:script">
<xs:attribute name="start" type="xs:int" use="optional"/>
<xs:attribute name="end" type="xs:int" use="optional"/>
<xs:attribute name="period" type="xs:int" use="optional"/>
<xs:attribute name="ifTrue" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="map">
<xs:sequence>
<xs:element name="chunk" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="row" maxOccurs="unbounded" type="xs:NMTOKENS"/>
</xs:sequence>
<xs:attribute name="x" type="xs:int"/>
<xs:attribute name="y" type="xs:int"/>
</xs:complexType>
</xs:element>
<xs:element name="scripts">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="enter" type="tns:script" minOccurs="0"/>
<xs:element name="exit" type="tns:script" minOccurs="0"/>
<xs:element name="stepOn" type="tns:locationScript" minOccurs="0"/>
<xs:element name="stepOff" type="tns:locationScript" minOccurs="0" />
<xs:element name="interact" type="tns:locationScript" minOccurs="0"/>
<xs:element name="interval" type="tns:intervalScript" minOccurs="0"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="width" type="xs:int"/>
<xs:attribute name="height" type="xs:int"/>
<xs:attribute name="wrap" type="xs:boolean" default="false"/>
<xs:attribute name="startX" type="xs:int" default="0"/>
<xs:attribute name="startY" type="xs:int" default="0"/>
</xs:complexType>
<xs:element name="gameData">
<xs:complexType>
<xs:sequence>
<xs:element name="image" minOccurs="0" maxOccurs="unbounded" type="tns:image"/>
<xs:element name="tile" minOccurs="0" maxOccurs="unbounded" type="tns:tile"/>
<xs:element name="map" minOccurs="0" maxOccurs="unbounded" type="tns:map"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,25 @@
/*
* Empty Stylesheet file.
*/
.mainFxmlClass {
}
.moveButton {
-fx-graphic: url("arrowUp.png");
}
.zoomOutButton {
-fx-graphic: url("zoomOut.png");
}
.zoomInButton {
-fx-graphic: url("zoomIn.png");
}
.moveButton, .zoomInButton, .zoomOutButton{
-fx-background-color: rgba(0,0,0,0);
-fx-content-display: graphic-only;
-fx-effect: dropshadow(gaussian, navy, 10, 0.5, 0, 0);
}

View File

@ -0,0 +1,34 @@
/* Application wide styles */
.label {
-fx-font-size: 12px;
-fx-font-weight: bold;
-fx-text-fill: #333333;
-fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
}
.button {
-fx-text-fill: white;
-fx-font-family: "Arial Narrow";
-fx-font-weight: bold;
-fx-background-color: linear-gradient(#61a2b1, #2A5058);
-fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
}
.button:hover{
-fx-base: #395bae;
}
/* Component specific styles */
.main-panel {
-fx-background-image: url("../images/background.jpg");
}
.hello-message {
-fx-text-fill: #AA0000;
-fx-font-weight: bold;
-fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
}