Push to BitBucket
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group 'games.rednblack'
|
||||
version '0.0.1'
|
||||
|
||||
ext {
|
||||
gdxVersion = '1.9.10'
|
||||
box2DLightsVersion = '1.5'
|
||||
ashleyVersion = '1.7.3'
|
||||
visUIVersion = '1.4.4'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.badlogicgames.gdx:gdx:$gdxVersion"
|
||||
implementation "com.badlogicgames.ashley:ashley:$ashleyVersion"
|
||||
implementation "com.kotcrab.vis:vis-ui:$visUIVersion"
|
||||
|
||||
implementation 'net.mountainblade:modular:1.0'
|
||||
|
||||
implementation project(":hyperlap2d-common-api")
|
||||
implementation project(":hyperlap2d-runtime-libgdx")
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
|
||||
task dist(type: Jar) {
|
||||
from files(sourceSets.main.output.classesDirs)
|
||||
from files(sourceSets.main.output.resourcesDir)
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package games.rednblack.editor.plugin.ninepatch;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.math.Circle;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack;
|
||||
|
||||
/**
|
||||
* Created by azakhary on 8/18/2015.
|
||||
*/
|
||||
public class EditingZone extends Actor {
|
||||
|
||||
private ShapeRenderer shapeRenderer;
|
||||
private TextureRegion texture;
|
||||
|
||||
private static final Color BG = new Color(43f / 255f, 43f / 255f, 43f / 255f, 1f);
|
||||
private static final Color GUIDE_COLOR = new Color(255f/255f, 94f/255f, 0f/255f, 0.5f);
|
||||
private static final Color OVER_GUIDE_COLOR = new Color(255f/255f, 173f/255f, 125f/255f, 1f);
|
||||
|
||||
private float currZoom = 1f;
|
||||
private Vector2 shift = new Vector2(0, 0);
|
||||
|
||||
private int mouseOverSplit = -1;
|
||||
|
||||
private float[] splitPositions = new float[4];
|
||||
private int[] splits = new int[4];
|
||||
|
||||
public interface PatchChangeListener {
|
||||
public void changed(int[] splits);
|
||||
}
|
||||
|
||||
private PatchChangeListener listener;
|
||||
|
||||
public EditingZone() {
|
||||
shapeRenderer = new ShapeRenderer();
|
||||
|
||||
addListener(new InputListener() {
|
||||
private Vector2 lastPoint;
|
||||
private int selectedSplit = -1;
|
||||
|
||||
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
|
||||
selectedSplit = splitCollision(x, y);
|
||||
if(selectedSplit >= 0) {
|
||||
|
||||
} else {
|
||||
lastPoint = new Vector2(x, y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void touchDragged(InputEvent event, float x, float y, int pointer) {
|
||||
if (selectedSplit >= 0) {
|
||||
if(selectedSplit == 0) { //left
|
||||
splits[0] = (int) ((x-shift.x-getWidth()/2f)/currZoom+texture.getRegionWidth()/2f);
|
||||
if(splits[0] > texture.getRegionWidth()-splits[1]) {
|
||||
int tmp = splits[1]; splits[1] = texture.getRegionWidth()-splits[0]; splits[0] = texture.getRegionWidth() - tmp;
|
||||
selectedSplit = 1;
|
||||
mouseOverSplit = selectedSplit;
|
||||
}
|
||||
splitUpdate();
|
||||
return;
|
||||
}
|
||||
if(selectedSplit == 1) {
|
||||
splits[1] = -(int) ((x-shift.x-getWidth()/2f)/currZoom-texture.getRegionWidth()/2f);
|
||||
if(texture.getRegionWidth()-splits[1] < splits[0]) {
|
||||
int tmp = splits[0]; splits[0] = texture.getRegionWidth() - splits[1]; splits[1] = texture.getRegionWidth() - tmp;
|
||||
selectedSplit = 0;
|
||||
mouseOverSplit = selectedSplit;
|
||||
}
|
||||
splitUpdate();
|
||||
return;
|
||||
}
|
||||
if(selectedSplit == 2) { // top
|
||||
splits[2] = -(int) ((y-shift.y-getHeight()/2f)/currZoom-texture.getRegionHeight()/2f);
|
||||
if(texture.getRegionHeight()-splits[2] < splits[3]) {
|
||||
int tmp = splits[2]; splits[2] = texture.getRegionHeight()-splits[3]; splits[3] = texture.getRegionHeight()-tmp;
|
||||
selectedSplit = 3;
|
||||
mouseOverSplit = selectedSplit;
|
||||
}
|
||||
splitUpdate();
|
||||
return;
|
||||
}
|
||||
if(selectedSplit == 3) {
|
||||
splits[3] = (int) ((y-shift.y-getHeight()/2f)/currZoom+texture.getRegionHeight()/2f);
|
||||
if(splits[3] > texture.getRegionHeight()-splits[2]) {
|
||||
int tmp = splits[3]; splits[3] = texture.getRegionHeight()-splits[2]; splits[2] = texture.getRegionHeight()-tmp;
|
||||
selectedSplit = 2;
|
||||
mouseOverSplit = selectedSplit;
|
||||
}
|
||||
splitUpdate();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Vector2 diff = new Vector2(x - lastPoint.x, y - lastPoint.y);
|
||||
shiftBy(diff);
|
||||
lastPoint = new Vector2(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
|
||||
selectedSplit = -1;
|
||||
|
||||
if(listener != null) {
|
||||
listener.changed(splits.clone());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mouseMoved(InputEvent event, float x, float y) {
|
||||
mouseOverSplit = splitCollision(x, y);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setListener(PatchChangeListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void splitUpdate() {
|
||||
if(splits[0] < 0) splits[0] = 0;
|
||||
if(splits[1] < 0) splits[1] = 0;
|
||||
if(splits[2] < 0) splits[2] = 0;
|
||||
if(splits[3] < 0) splits[3] = 0;
|
||||
}
|
||||
|
||||
public int[] getSplits() {
|
||||
return splits.clone();
|
||||
}
|
||||
|
||||
public void setTexture(TextureRegion texture) {
|
||||
this.texture = texture;
|
||||
|
||||
splits[0] = ((TextureAtlas.AtlasRegion)texture).splits[0];
|
||||
splits[1] = ((TextureAtlas.AtlasRegion)texture).splits[1];
|
||||
splits[2] = ((TextureAtlas.AtlasRegion)texture).splits[2];
|
||||
splits[3] = ((TextureAtlas.AtlasRegion)texture).splits[3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw (Batch batch, float parentAlpha) {
|
||||
Rectangle scissors = new Rectangle();
|
||||
Rectangle clipBounds = new Rectangle(getX(),getY(),getWidth(),getHeight());
|
||||
ScissorStack.calculateScissors(getStage().getCamera(), batch.getTransformMatrix(), clipBounds, scissors);
|
||||
ScissorStack.pushScissors(scissors);
|
||||
|
||||
drawBg(batch, parentAlpha);
|
||||
|
||||
batch.draw(texture,
|
||||
getX() + getWidth() / 2 - texture.getRegionWidth() / 2 + shift.x,
|
||||
getY() + getHeight() / 2 - texture.getRegionHeight() / 2 + shift.y,
|
||||
texture.getRegionWidth() / 2f,
|
||||
texture.getRegionHeight() / 2f,
|
||||
texture.getRegionWidth(), texture.getRegionHeight(),
|
||||
currZoom, currZoom, 0);
|
||||
|
||||
drawSplits(batch, parentAlpha);
|
||||
|
||||
batch.flush();
|
||||
ScissorStack.popScissors();
|
||||
}
|
||||
|
||||
public void drawBg(Batch batch, float parentAlpha) {
|
||||
batch.end();
|
||||
Gdx.gl.glLineWidth(1.0f);
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND);
|
||||
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
|
||||
Matrix4 matrix = batch.getTransformMatrix();
|
||||
shapeRenderer.setTransformMatrix(matrix);
|
||||
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
|
||||
BG.a = parentAlpha;
|
||||
shapeRenderer.setColor(BG);
|
||||
shapeRenderer.rect(getX(), getY(), getWidth(), getHeight());
|
||||
shapeRenderer.end();
|
||||
|
||||
Gdx.gl.glDisable(GL20.GL_BLEND);
|
||||
batch.begin();
|
||||
batch.setColor(Color.WHITE.r, Color.WHITE.g, Color.WHITE.b, Color.WHITE.a * parentAlpha);
|
||||
}
|
||||
|
||||
public void drawSplits(Batch batch, float parentAlpha) {
|
||||
batch.end();
|
||||
Gdx.gl.glLineWidth(1.0f);
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND);
|
||||
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
|
||||
Matrix4 matrix = batch.getTransformMatrix();
|
||||
shapeRenderer.setTransformMatrix(matrix);
|
||||
|
||||
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
|
||||
|
||||
// left, right, top , bottom
|
||||
Color guideColor = new Color(GUIDE_COLOR);
|
||||
guideColor.a*=parentAlpha;
|
||||
Color overColor = new Color(OVER_GUIDE_COLOR);
|
||||
overColor.a*=parentAlpha;
|
||||
|
||||
splitPositions[0] = shift.x + getWidth() / 2f + (-texture.getRegionWidth() / 2f + splits[0]) * currZoom;
|
||||
splitPositions[1] = shift.x + getWidth() / 2f + (texture.getRegionWidth() / 2f - splits[1]) * currZoom;
|
||||
splitPositions[2] = shift.y + getHeight()/2f + (texture.getRegionHeight()/2 - splits[2])*currZoom;
|
||||
splitPositions[3] = shift.y + getHeight() / 2f + (-texture.getRegionHeight() / 2 + splits[3]) * currZoom;
|
||||
|
||||
|
||||
if(mouseOverSplit == 0) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
|
||||
shapeRenderer.line(getX() + splitPositions[0], getY(), getX() + splitPositions[0], getY() + getHeight());
|
||||
|
||||
if(mouseOverSplit == 1) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
|
||||
shapeRenderer.line(getX() + splitPositions[1], getY(), getX() + splitPositions[1], getY() + getHeight());
|
||||
|
||||
if(mouseOverSplit == 2) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
|
||||
shapeRenderer.line(getX(), getY() + splitPositions[2], getX() + getWidth(), getY() + splitPositions[2]);
|
||||
|
||||
if(mouseOverSplit == 3) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
|
||||
shapeRenderer.line(getX(), getY() + splitPositions[3], getX() + getWidth(), getY() + splitPositions[3]);
|
||||
|
||||
shapeRenderer.end();
|
||||
Gdx.gl.glDisable(GL20.GL_BLEND);
|
||||
batch.begin();
|
||||
batch.setColor(Color.WHITE.r, Color.WHITE.g, Color.WHITE.b, Color.WHITE.a * parentAlpha);
|
||||
}
|
||||
|
||||
public void zoomBy(int amount) {
|
||||
float defaultSize = 1.0f - amount*0.12f;
|
||||
currZoom*=defaultSize;
|
||||
}
|
||||
|
||||
public void shiftBy(Vector2 diff) {
|
||||
shift.add(diff);
|
||||
}
|
||||
|
||||
public int splitCollision(float x, float y) {
|
||||
Circle touchCircle = new Circle();
|
||||
touchCircle.radius = 5f;
|
||||
touchCircle.setPosition(x, y);
|
||||
|
||||
if(touchCircle.contains(splitPositions[0], touchCircle.y)) {
|
||||
return 0;
|
||||
}
|
||||
if(touchCircle.contains(splitPositions[1], touchCircle.y)) {
|
||||
return 1;
|
||||
}
|
||||
if(touchCircle.contains(touchCircle.x, splitPositions[2])) {
|
||||
return 2;
|
||||
}
|
||||
if(touchCircle.contains(touchCircle.x, splitPositions[3])) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package games.rednblack.editor.plugin.ninepatch;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by various artists on 8/18/2015.
|
||||
*/
|
||||
public class ImageUtils {
|
||||
private static final int NINEPATCH_PADDING = 1;
|
||||
private static final String OUTPUT_TYPE = "png";
|
||||
|
||||
|
||||
/** Returns the pads, or null if the image had no pads or the pads match the splits. Pads are an int[4] that has left, right,
|
||||
* top, bottom. */
|
||||
public int[] getPads (BufferedImage image, String name, int[] splits) {
|
||||
WritableRaster raster = image.getRaster();
|
||||
|
||||
int bottom = raster.getHeight() - 1;
|
||||
int right = raster.getWidth() - 1;
|
||||
|
||||
int startX = getSplitPoint(raster, name, 1, bottom, true, true);
|
||||
int startY = getSplitPoint(raster, name, right, 1, true, false);
|
||||
|
||||
// No need to hunt for the end if a start was never found.
|
||||
int endX = 0;
|
||||
int endY = 0;
|
||||
if (startX != 0) endX = getSplitPoint(raster, name, startX + 1, bottom, false, true);
|
||||
if (startY != 0) endY = getSplitPoint(raster, name, right, startY + 1, false, false);
|
||||
|
||||
// Ensure pixels after the end are not invalid.
|
||||
getSplitPoint(raster, name, endX + 1, bottom, true, true);
|
||||
getSplitPoint(raster, name, right, endY + 1, true, false);
|
||||
|
||||
// No pads.
|
||||
if (startX == 0 && endX == 0 && startY == 0 && endY == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// -2 here is because the coordinates were computed before the 1px border was stripped.
|
||||
if (startX == 0 && endX == 0) {
|
||||
startX = -1;
|
||||
endX = -1;
|
||||
} else {
|
||||
if (startX > 0) {
|
||||
startX--;
|
||||
endX = raster.getWidth() - 2 - (endX - 1);
|
||||
} else {
|
||||
// If no start point was ever found, we assume full stretch.
|
||||
endX = raster.getWidth() - 2;
|
||||
}
|
||||
}
|
||||
if (startY == 0 && endY == 0) {
|
||||
startY = -1;
|
||||
endY = -1;
|
||||
} else {
|
||||
if (startY > 0) {
|
||||
startY--;
|
||||
endY = raster.getHeight() - 2 - (endY - 1);
|
||||
} else {
|
||||
// If no start point was ever found, we assume full stretch.
|
||||
endY = raster.getHeight() - 2;
|
||||
}
|
||||
}
|
||||
|
||||
int[] pads = new int[] {startX, endX, startY, endY};
|
||||
|
||||
if (splits != null && Arrays.equals(pads, splits)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return pads;
|
||||
}
|
||||
|
||||
/** Returns the splits, or null if the image had no splits or the splits were only a single region. Splits are an int[4] that
|
||||
* has left, right, top, bottom. */
|
||||
public int[] getSplits (BufferedImage image, String name) {
|
||||
WritableRaster raster = image.getRaster();
|
||||
|
||||
int startX = getSplitPoint(raster, name, 1, 0, true, true);
|
||||
int endX = getSplitPoint(raster, name, startX, 0, false, true);
|
||||
int startY = getSplitPoint(raster, name, 0, 1, true, false);
|
||||
int endY = getSplitPoint(raster, name, 0, startY, false, false);
|
||||
|
||||
// Ensure pixels after the end are not invalid.
|
||||
getSplitPoint(raster, name, endX + 1, 0, true, true);
|
||||
getSplitPoint(raster, name, 0, endY + 1, true, false);
|
||||
|
||||
// No splits, or all splits.
|
||||
if (startX == 0 && endX == 0 && startY == 0 && endY == 0) return null;
|
||||
|
||||
// Subtraction here is because the coordinates were computed before the 1px border was stripped.
|
||||
if (startX != 0) {
|
||||
startX--;
|
||||
endX = raster.getWidth() - 2 - (endX - 1);
|
||||
} else {
|
||||
// If no start point was ever found, we assume full stretch.
|
||||
endX = raster.getWidth() - 2;
|
||||
}
|
||||
if (startY != 0) {
|
||||
startY--;
|
||||
endY = raster.getHeight() - 2 - (endY - 1);
|
||||
} else {
|
||||
// If no start point was ever found, we assume full stretch.
|
||||
endY = raster.getHeight() - 2;
|
||||
}
|
||||
|
||||
return new int[] {startX, endX, startY, endY};
|
||||
}
|
||||
|
||||
/** Hunts for the start or end of a sequence of split pixels. Begins searching at (startX, startY) then follows along the x or y
|
||||
* axis (depending on value of xAxis) for the first non-transparent pixel if startPoint is true, or the first transparent pixel
|
||||
* if startPoint is false. Returns 0 if none found, as 0 is considered an invalid split point being in the outer border which
|
||||
* will be stripped. */
|
||||
static private int getSplitPoint (WritableRaster raster, String name, int startX, int startY, boolean startPoint, boolean xAxis) {
|
||||
int[] rgba = new int[4];
|
||||
|
||||
int next = xAxis ? startX : startY;
|
||||
int end = xAxis ? raster.getWidth() : raster.getHeight();
|
||||
int breakA = startPoint ? 255 : 0;
|
||||
|
||||
int x = startX;
|
||||
int y = startY;
|
||||
while (next != end) {
|
||||
if (xAxis)
|
||||
x = next;
|
||||
else
|
||||
y = next;
|
||||
|
||||
raster.getPixel(x, y, rgba);
|
||||
if (rgba[3] == breakA) return next;
|
||||
|
||||
if (!startPoint && (rgba[0] != 0 || rgba[1] != 0 || rgba[2] != 0 || rgba[3] != 255)) {
|
||||
// error
|
||||
}
|
||||
|
||||
next++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public BufferedImage extractImage(TextureAtlas.TextureAtlasData atlas, String regionName, int[] splits) {
|
||||
for (TextureAtlas.TextureAtlasData.Region region : atlas.getRegions()) {
|
||||
if(region.name.equals(regionName)) {
|
||||
TextureAtlas.TextureAtlasData.Page page = region.page;
|
||||
BufferedImage img = null;
|
||||
try {
|
||||
img = ImageIO.read(page.textureFile.file());
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
region.splits = splits;
|
||||
return extractNinePatch(img, region);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private BufferedImage extractImage (BufferedImage page, TextureAtlas.TextureAtlasData.Region region, int padding) {
|
||||
BufferedImage splitImage = null;
|
||||
|
||||
// get the needed part of the page and rotate if needed
|
||||
if (region.rotate) {
|
||||
BufferedImage srcImage = page.getSubimage(region.left, region.top, region.height, region.width);
|
||||
splitImage = new BufferedImage(region.width, region.height, page.getType());
|
||||
|
||||
AffineTransform transform = new AffineTransform();
|
||||
transform.rotate(Math.toRadians(90.0));
|
||||
transform.translate(0, -region.width);
|
||||
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
|
||||
op.filter(srcImage, splitImage);
|
||||
} else {
|
||||
splitImage = page.getSubimage(region.left, region.top, region.width, region.height);
|
||||
}
|
||||
|
||||
// draw the image to a bigger one if padding is needed
|
||||
if (padding > 0) {
|
||||
BufferedImage paddedImage = new BufferedImage(splitImage.getWidth() + padding * 2, splitImage.getHeight() + padding * 2,
|
||||
page.getType());
|
||||
Graphics2D g2 = paddedImage.createGraphics();
|
||||
g2.drawImage(splitImage, padding, padding, null);
|
||||
g2.dispose();
|
||||
return paddedImage;
|
||||
} else {
|
||||
return splitImage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private BufferedImage extractNinePatch (BufferedImage page, TextureAtlas.TextureAtlasData.Region region) {
|
||||
BufferedImage splitImage = extractImage(page, region, NINEPATCH_PADDING);
|
||||
Graphics2D g2 = splitImage.createGraphics();
|
||||
g2.setColor(Color.BLACK);
|
||||
|
||||
// Draw the four lines to save the ninepatch's padding and splits
|
||||
int startX = region.splits[0] + NINEPATCH_PADDING;
|
||||
int endX = region.width - region.splits[1] + NINEPATCH_PADDING - 1;
|
||||
int startY = region.splits[2] + NINEPATCH_PADDING;
|
||||
int endY = region.height - region.splits[3] + NINEPATCH_PADDING - 1;
|
||||
if (endX >= startX) g2.drawLine(startX, 0, endX, 0);
|
||||
if (endY >= startY) g2.drawLine(0, startY, 0, endY);
|
||||
if (region.pads != null) {
|
||||
int padStartX = region.pads[0] + NINEPATCH_PADDING;
|
||||
int padEndX = region.width - region.pads[1] + NINEPATCH_PADDING - 1;
|
||||
int padStartY = region.pads[2] + NINEPATCH_PADDING;
|
||||
int padEndY = region.height - region.pads[3] + NINEPATCH_PADDING - 1;
|
||||
g2.drawLine(padStartX, splitImage.getHeight() - 1, padEndX, splitImage.getHeight() - 1);
|
||||
g2.drawLine(splitImage.getWidth() - 1, padStartY, splitImage.getWidth() - 1, padEndY);
|
||||
}
|
||||
g2.dispose();
|
||||
|
||||
return splitImage;
|
||||
}
|
||||
|
||||
public void saveImage(BufferedImage image, String path) {
|
||||
try {
|
||||
ImageIO.write(image, OUTPUT_TYPE, new File(path));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package games.rednblack.editor.plugin.ninepatch;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.Stage;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
import com.kotcrab.vis.ui.widget.VisLabel;
|
||||
import com.kotcrab.vis.ui.widget.VisTable;
|
||||
import com.kotcrab.vis.ui.widget.VisTextButton;
|
||||
import com.puremvc.patterns.facade.SimpleFacade;
|
||||
import games.rednblack.h2d.common.H2DDialog;
|
||||
|
||||
/**
|
||||
* Created by azakhary on 8/18/2015.
|
||||
*/
|
||||
public class MainPanel extends H2DDialog {
|
||||
public static final String CLASS_NAME = "ames.rednblack.editor.plugin.ninepatch.MainPanel";
|
||||
|
||||
public static final String SAVE_CLICKED = CLASS_NAME + ".SAVE_CLICKED";
|
||||
|
||||
private SimpleFacade facade;
|
||||
|
||||
private VisTable mainTable;
|
||||
private TextureRegion texture;
|
||||
|
||||
private VisTable editingTable;
|
||||
private VisTable previewTable;
|
||||
|
||||
private EditingZone editingZone;
|
||||
private PreviewWidget previewWidget;
|
||||
|
||||
public MainPanel() {
|
||||
super("Nine Patch");
|
||||
addCloseButton();
|
||||
|
||||
facade = SimpleFacade.getInstance();
|
||||
|
||||
mainTable = new VisTable();
|
||||
add(mainTable).width(520).height(310).padBottom(7);
|
||||
editingTable = new VisTable();
|
||||
previewTable = new VisTable();
|
||||
|
||||
mainTable.add(editingTable).width(310).expandY();
|
||||
mainTable.add(previewTable).expandX().expandY();
|
||||
mainTable.row();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
editingTable.clear();
|
||||
editingZone = new EditingZone();
|
||||
editingZone.setTexture(texture);
|
||||
editingTable.add(editingZone);
|
||||
|
||||
editingZone.setWidth(310);
|
||||
editingZone.setHeight(310);
|
||||
|
||||
editingZone.setListener(new EditingZone.PatchChangeListener() {
|
||||
@Override
|
||||
public void changed(int[] splits) {
|
||||
previewWidget.update((TextureAtlas.AtlasRegion) texture, splits);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initPreView() {
|
||||
previewTable.clear();
|
||||
previewWidget = new PreviewWidget();
|
||||
previewWidget.setHeight(205);
|
||||
previewTable.add(previewWidget).width(200).height(205).top();
|
||||
previewTable.row();
|
||||
previewWidget.update((TextureAtlas.AtlasRegion) texture, ((TextureAtlas.AtlasRegion) texture).splits);
|
||||
|
||||
VisLabel label = new VisLabel("Note: after saving, your \n scene will reload to \n apply changes.");
|
||||
label.setAlignment(Align.center);
|
||||
previewTable.add(label).pad(10).fillY().expandY();
|
||||
previewTable.row();
|
||||
|
||||
VisTextButton saveBtn = new VisTextButton("apply and save");
|
||||
previewTable.add(saveBtn).pad(5);
|
||||
previewTable.row();
|
||||
|
||||
saveBtn.addListener(new ClickListener() {
|
||||
public void clicked (InputEvent event, float x, float y) {
|
||||
facade.sendNotification(SAVE_CLICKED);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setTexture(TextureRegion texture) {
|
||||
this.texture = texture;
|
||||
|
||||
initView();
|
||||
initPreView();
|
||||
}
|
||||
|
||||
public void setListeners(Stage stage) {
|
||||
stage.addListener(new InputListener() {
|
||||
@Override
|
||||
public boolean scrolled(InputEvent event, float x, float y, int amount) {
|
||||
editingZone.zoomBy(amount);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int[] getSplits() {
|
||||
return editingZone.getSplits();
|
||||
}
|
||||
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
package games.rednblack.editor.plugin.ninepatch;
|
||||
|
||||
import com.badlogic.ashley.core.Entity;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.g2d.NinePatch;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.puremvc.patterns.mediator.SimpleMediator;
|
||||
import com.puremvc.patterns.observer.Notification;
|
||||
import games.rednblack.editor.renderer.components.MainItemComponent;
|
||||
import games.rednblack.editor.renderer.components.NinePatchComponent;
|
||||
import games.rednblack.editor.renderer.components.TextureRegionComponent;
|
||||
import games.rednblack.editor.renderer.factory.EntityFactory;
|
||||
import games.rednblack.editor.renderer.utils.ComponentRetriever;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by azakhary on 8/18/2015.
|
||||
*/
|
||||
public class MainPanelMediator extends SimpleMediator<MainPanel> {
|
||||
private static final String TAG = MainPanelMediator.class.getCanonicalName();
|
||||
public static final String NAME = TAG;
|
||||
|
||||
private NinePatchPlugin plugin;
|
||||
|
||||
private ImageUtils imageUtils = new ImageUtils();
|
||||
|
||||
public MainPanelMediator(NinePatchPlugin plugin) {
|
||||
super(NAME, new MainPanel());
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listNotificationInterests() {
|
||||
return new String[]{
|
||||
NinePatchPlugin.EDIT_NINE_PATCH,
|
||||
NinePatchPlugin.CONVERT_TO_NINE_PATCH,
|
||||
MainPanel.SAVE_CLICKED
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNotification(Notification notification) {
|
||||
super.handleNotification(notification);
|
||||
switch (notification.getName()) {
|
||||
case NinePatchPlugin.EDIT_NINE_PATCH:
|
||||
loadNinePatch();
|
||||
break;
|
||||
case NinePatchPlugin.CONVERT_TO_NINE_PATCH:
|
||||
convertImageToNinePatch();
|
||||
loadNinePatch();
|
||||
break;
|
||||
case MainPanel.SAVE_CLICKED:
|
||||
Entity entity = plugin.currEditingEntity;
|
||||
NinePatchComponent ninePatchComponent = ComponentRetriever.get(entity, NinePatchComponent.class);
|
||||
applyNewSplits(ninePatchComponent.textureRegionName, viewComponent.getSplits());
|
||||
viewComponent.hide();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void convertImageToNinePatch() {
|
||||
Entity entity = plugin.currEditingEntity;
|
||||
MainItemComponent mainItemComponent = ComponentRetriever.get(entity, MainItemComponent.class);
|
||||
mainItemComponent.entityType = EntityFactory.NINE_PATCH;
|
||||
TextureRegionComponent textureRegionComponent = ComponentRetriever.get(entity, TextureRegionComponent.class);
|
||||
String regionName = textureRegionComponent.regionName;
|
||||
NinePatchComponent ninePatchComponent = new NinePatchComponent();
|
||||
ninePatchComponent.textureRegionName = regionName;
|
||||
TextureAtlas.AtlasRegion newRegion = (TextureAtlas.AtlasRegion) textureRegionComponent.region;
|
||||
int[] splits = {0, 0, 0, 0};
|
||||
newRegion.splits = splits;
|
||||
ninePatchComponent.ninePatch = new NinePatch(textureRegionComponent.region, 0, 0, 0, 0);
|
||||
entity.add(ninePatchComponent);
|
||||
|
||||
//remove original image
|
||||
File originalImg = new File(plugin.getAPI().getProjectPath() + "/assets/orig/images/"+regionName+".png");
|
||||
originalImg.delete();
|
||||
|
||||
//save project
|
||||
plugin.getAPI().saveProject();
|
||||
|
||||
//save split data
|
||||
addSplitsToImageInAtlas(regionName, splits);
|
||||
applyNewSplits(regionName, splits);
|
||||
}
|
||||
|
||||
private void loadNinePatch() {
|
||||
Entity entity = plugin.currEditingEntity;
|
||||
NinePatchComponent ninePatchComponent = ComponentRetriever.get(entity, NinePatchComponent.class);
|
||||
loadRegion(ninePatchComponent.textureRegionName);
|
||||
viewComponent.show(plugin.getAPI().getUIStage());
|
||||
}
|
||||
|
||||
private void addSplitsToImageInAtlas(String textureRegionName, int[] splits) {
|
||||
FileHandle packAtlas = Gdx.files.internal(plugin.getAPI().getProjectPath() + "/assets/orig/pack/pack.atlas");
|
||||
String content = packAtlas.readString();
|
||||
int regionIndex = content.indexOf(textureRegionName);
|
||||
int splitEnd = content.indexOf("orig: ", regionIndex);
|
||||
String splitStr = "split: "+splits[0]+", "+splits[1]+", "+splits[2]+", "+splits[3]+"\n ";
|
||||
String newContent = content.substring(0, splitEnd) + splitStr + content.substring(splitEnd, content.length());
|
||||
File test = new File(plugin.getAPI().getProjectPath() + "/assets/orig/pack/pack.atlas");
|
||||
writeFile(newContent, test);
|
||||
}
|
||||
|
||||
private void applyNewSplits(String textureRegionName, int[] splits) {
|
||||
// first need to modify original image
|
||||
FileHandle packAtlas = Gdx.files.internal(plugin.getAPI().getProjectPath() + "/assets/orig/pack/pack.atlas");
|
||||
FileHandle imagesDir = Gdx.files.internal(plugin.getAPI().getProjectPath() + "/assets/orig/pack/");
|
||||
TextureAtlas.TextureAtlasData atlas = new TextureAtlas.TextureAtlasData(packAtlas, imagesDir, false);
|
||||
BufferedImage finalImage = imageUtils.extractImage(atlas, textureRegionName, splits);
|
||||
imageUtils.saveImage(finalImage, plugin.getAPI().getProjectPath() + "/assets/orig/images/"+textureRegionName+".9.png");
|
||||
|
||||
// now need to modify the pack
|
||||
String content = packAtlas.readString();
|
||||
int regionIndex = content.indexOf(textureRegionName);
|
||||
int splitStart = content.indexOf("split: ", regionIndex) + "split: ".length();
|
||||
int splitEnd = content.indexOf("orig: ", splitStart);
|
||||
String splitStr = splits[0]+", "+splits[1]+", "+splits[2]+", "+splits[3]+"\n ";
|
||||
String newContent = content.substring(0, splitStart) + splitStr + content.substring(splitEnd, content.length());
|
||||
File test = new File(plugin.getAPI().getProjectPath() + "/assets/orig/pack/pack.atlas");
|
||||
writeFile(newContent, test);
|
||||
|
||||
// reload
|
||||
plugin.getAPI().reLoadProject();
|
||||
}
|
||||
|
||||
private void writeFile(String content, File file) {
|
||||
BufferedWriter output = null;
|
||||
try {
|
||||
output = new BufferedWriter(new FileWriter(file));
|
||||
output.write(content);
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if ( output != null ) try {
|
||||
output.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRegion(String name) {
|
||||
TextureAtlas atlas = plugin.getAPI().getProjectTextureAtlas();
|
||||
viewComponent.setTexture(atlas.findRegion(name));
|
||||
|
||||
viewComponent.setListeners(plugin.getAPI().getUIStage());
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package games.rednblack.editor.plugin.ninepatch;
|
||||
|
||||
import com.badlogic.ashley.core.Entity;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import games.rednblack.editor.renderer.components.MainItemComponent;
|
||||
import games.rednblack.editor.renderer.factory.EntityFactory;
|
||||
import games.rednblack.editor.renderer.utils.ComponentRetriever;
|
||||
import games.rednblack.h2d.common.plugins.H2DPluginAdapter;
|
||||
import net.mountainblade.modular.annotations.Implementation;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by azakhary on 8/18/2015.
|
||||
* Plugin to edit Nine Patch data on imported texture region
|
||||
*/
|
||||
@Implementation(authors = "azakhary", version = "0.0.1")
|
||||
public class NinePatchPlugin extends H2DPluginAdapter {
|
||||
public static final String CLASS_NAME = "games.rednblack.editor.plugin.ninepatch.NinePatchPlugin";
|
||||
|
||||
public static final String EDIT_NINE_PATCH = CLASS_NAME + ".EDIT_NINE_PATCH";
|
||||
public static final String CONVERT_TO_NINE_PATCH = CLASS_NAME + ".CONVERT_TO_NINE_PATCH";
|
||||
|
||||
private MainPanelMediator performancePanelMediator;
|
||||
|
||||
public Entity currEditingEntity;
|
||||
|
||||
public NinePatchPlugin() {
|
||||
performancePanelMediator = new MainPanelMediator(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initPlugin() {
|
||||
facade.registerMediator(performancePanelMediator);
|
||||
pluginAPI.setDropDownItemName(EDIT_NINE_PATCH, "Edit NinePatch");
|
||||
pluginAPI.setDropDownItemName(CONVERT_TO_NINE_PATCH, "Convert to NinePatch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDropDownOpen(Set<Entity> selectedEntities, Array<String> actionsSet) {
|
||||
if(selectedEntities.size() == 1) {
|
||||
Entity entity = selectedEntities.stream().findFirst().get();
|
||||
MainItemComponent mainItemComponent = ComponentRetriever.get(entity, MainItemComponent.class);
|
||||
|
||||
if(mainItemComponent.entityType == EntityFactory.NINE_PATCH) {
|
||||
// it's our guy
|
||||
currEditingEntity = entity;
|
||||
actionsSet.add(EDIT_NINE_PATCH);
|
||||
}
|
||||
if(mainItemComponent.entityType == EntityFactory.IMAGE_TYPE) {
|
||||
// it's our guy
|
||||
currEditingEntity = entity;
|
||||
actionsSet.add(CONVERT_TO_NINE_PATCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package games.rednblack.editor.plugin.ninepatch;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.NinePatch;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
|
||||
/**
|
||||
* Created by azakhary on 8/19/2015.
|
||||
*/
|
||||
public class PreviewWidget extends Group {
|
||||
|
||||
private TextureAtlas.AtlasRegion region;
|
||||
|
||||
float horizontalHeight, horizontalWidth;
|
||||
float verticalHeight, verticalWidth;
|
||||
float squareWidth, squareHeight;
|
||||
|
||||
private Image horizontal, vertical, square;
|
||||
|
||||
public PreviewWidget() {
|
||||
horizontalWidth = 200f;
|
||||
horizontalHeight = 50f;
|
||||
|
||||
verticalWidth = 50f;
|
||||
verticalHeight = 150f;
|
||||
|
||||
squareWidth = 145f;
|
||||
squareHeight = 150f;
|
||||
}
|
||||
|
||||
public void update(TextureAtlas.AtlasRegion region, int[] splits) {
|
||||
this.region = region;
|
||||
clear();
|
||||
NinePatch horizontalPatch = new NinePatch(region, splits[0], splits[1], splits[2], splits[3]);
|
||||
NinePatch verticalPatch = new NinePatch(region, splits[0], splits[1], splits[2], splits[3]);
|
||||
NinePatch squarePatch = new NinePatch(region, splits[0], splits[1], splits[2], splits[3]);
|
||||
|
||||
float minSclH = getMinScale(horizontalPatch, horizontalWidth, horizontalHeight);
|
||||
float minSclV = getMinScale(verticalPatch, verticalWidth, verticalHeight);
|
||||
float minSclS = getMinScale(squarePatch, squareWidth, squareHeight);
|
||||
|
||||
float minScl = Math.min(minSclH, minSclV);
|
||||
minScl = Math.min(minScl, minSclS);
|
||||
|
||||
horizontal = fitNinePatch(horizontalPatch, horizontalWidth, horizontalHeight, minScl);
|
||||
addActor(horizontal);
|
||||
|
||||
vertical = fitNinePatch(verticalPatch, verticalWidth, verticalHeight, minScl);
|
||||
addActor(vertical);
|
||||
|
||||
square = fitNinePatch(squarePatch, squareWidth, squareHeight, minScl);
|
||||
addActor(square);
|
||||
|
||||
horizontal.setY(getHeight() - horizontalHeight);
|
||||
vertical.setY(horizontal.getY() - verticalHeight - 5);
|
||||
square.setX(verticalWidth + 5);
|
||||
square.setY(vertical.getY());
|
||||
}
|
||||
|
||||
private float getMinScale(NinePatch horizontalPatch, float width, float height) {
|
||||
float scaleX = width/horizontalPatch.getTotalWidth();
|
||||
float scaleY = height/horizontalPatch.getTotalHeight();
|
||||
float scl = Math.min(scaleX, scaleY);
|
||||
if(scl > 1f) scl = 1f;
|
||||
|
||||
return scl;
|
||||
}
|
||||
|
||||
private Image fitNinePatch(NinePatch horizontalPatch, float width, float height, float scl) {
|
||||
horizontalPatch.scale(scl, scl);
|
||||
|
||||
Image img = new Image(horizontalPatch);
|
||||
img.setScaleX(width / horizontalPatch.getTotalWidth());
|
||||
img.setScaleY(height / horizontalPatch.getTotalHeight());
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user