Initial commit

This commit is contained in:
Klemek
2018-04-21 14:34:36 +02:00
commit 088054817a
12 changed files with 666 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+5
View File
@@ -0,0 +1,5 @@
/.git/
/.idea/
/out/
/task/
*.iml
+1
View File
@@ -0,0 +1 @@
# MarbleGenerator
+3
View File
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: fr.klemek.marble.WallpaperGenerator
+86
View File
@@ -0,0 +1,86 @@
package fr.klemek.marble;
import java.util.Random;
class Color {
final byte r;
final byte g;
final byte b;
Color(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
}
Color(int r, int g, int b) {
this.r = (byte) r;
this.g = (byte) g;
this.b = (byte) b;
}
static Color fromRgb(int r, int g, int b){
return new Color(r + Byte.MIN_VALUE, g + Byte.MIN_VALUE, b + Byte.MIN_VALUE);
}
static Color random(Random rand) {
return new Color(
Utils.randomByte(rand),
Utils.randomByte(rand),
Utils.randomByte(rand));
}
Color plus(Color other) {
return new Color(Utils.bound((short) r + other.r),
Utils.bound((short) g + other.g),
Utils.bound((short) b + other.b));
}
Color times(float factor) {
return new Color((byte) (r * factor),
(byte) (g * factor),
(byte) (b * factor));
}
static Color add(float[] factors, Color[] colors) {
float r = 0f;
float g = 0f;
float b = 0f;
for (int i = 0; i < factors.length; i++) {
r += factors[i] * colors[i].r;
g += factors[i] * colors[i].g;
b += factors[i] * colors[i].b;
}
return new Color(Utils.bound(Math.round(r)),Utils.bound(Math.round(g)), Utils.bound(Math.round(b)));
}
static Color add(Color... colors) {
float[] factors = new float[colors.length];
for (int i = 0; i < colors.length; i++)
factors[i] = 1f;
return add(factors, colors);
}
Color diverge(Random rand) {
return new Color(Utils.bound(Utils.div(rand, r)),
Utils.div(rand, g),
Utils.div(rand, b));
}
int sum() {
return r + g + b;
}
public java.awt.Color toColor() {
return new java.awt.Color(r - Byte.MIN_VALUE, g - Byte.MIN_VALUE, b - Byte.MIN_VALUE);
}
@Override
public String toString() {
return toString(false);
}
public String toString(boolean unsigned) {
return "(" + (unsigned ? r - Byte.MIN_VALUE : r) + "," + (unsigned ? g - Byte.MIN_VALUE : g) + "," + (unsigned ? b - Byte.MIN_VALUE : b) + ")";
}
}
+190
View File
@@ -0,0 +1,190 @@
package fr.klemek.marble;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
class Generator {
private final int width;
private final int height;
private final int width2;
private final int height2;
private final int size;
private float slope;
private Color source;
private Color divergence;
private long seed = 0L;
private Color[][] table;
private Random rand;
Generator(int width, int height) {
this(width, height, Utils.randInt(3, 12));
}
Generator(int width, int height, int size) {
this(width, height, size, (float) Utils.randInt(40, 60) / 100f);
}
Generator(int width, int height, int size, float slope) {
this(width, height, size, slope, new Color(Utils.randInt(0, 30),
Utils.randInt(0, 30),
Utils.randInt(0, 30)));
}
Generator(int width, int height, int size, float slope, Color divergence){
this(width, height, size, slope, divergence, null);
}
Generator(int width, int height, int size, float slope, Color divergence, Color source) {
this.width = width;
this.height = height;
this.size = size;
this.width2 = width % size == 0 ? (width / size) : (width / size) + 1;
this.height2 = height % size == 0 ? (height / size) : (height / size) + 1;
this.slope = slope;
this.divergence = divergence;
this.source = source;
}
public Color[][] getTable() {
return table;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getWidth2() {
return width2;
}
public int getHeight2() {
return height2;
}
public int getSize() {
return size;
}
void generate() {
if (seed == 0L)
seed = ThreadLocalRandom.current().nextLong();
System.out.println("\t\tseed : " + seed);
System.out.println("\t\tsize : " + size);
System.out.println("\t\tslope : " + slope);
System.out.println("\t\tdivergence : " + divergence + " = " + divergence.sum());
rand = new Random(seed);
table = new Color[width2][height2];
table[0][0] = source == null ? Color.random(rand) : source;
System.out.println("\t\tsource : " + source);
for (int y = 0; y < height2; y++)
generateLine(y);
}
private static Color getDivergence(int width, int height, int size) {
int size2 = (int) Math.min(Math.max(width / size, height / size), Byte.MAX_VALUE * 1.5f);
Color c;
do {
c = new Color(Utils.randInt(0, Byte.MAX_VALUE),
Utils.randInt(0, Byte.MAX_VALUE),
Utils.randInt(0, Byte.MAX_VALUE));
} while (c.sum() * 2 < size2 || c.sum() > Byte.MAX_VALUE * 2);
return c;
}
private void generateLine(int y) {
for (int x = 0; x < width2; x++) {
Color div = divergence.diverge(rand);
if (x > 0 && y == 0) {
table[x][y] = Color.add(table[x - 1][y], div);
} else if (x == 0 && y > 0) {
table[x][y] = Color.add(table[x][y - 1], div);
} else if (x > 0 && y > 0) {
table[x][y] = Color.add(new float[]{slope, 1f - slope, 1f}, new Color[]{table[x][y - 1], table[x - 1][y], div});
}
}
}
void inspect(int x, int y, int size, boolean unsigned) {
System.out.println(String.format("Inspect area : %d-%d x %d-%d", x, x + size, y, y + size));
int sumr = 0;
int sumg = 0;
int sumb = 0;
for (int i = x; i < x + size; i++) {
for (int j = y; j < y + size; j++) {
System.out.print(String.format("%1$-12s %2$-4d ", table[i][j].toString(unsigned), table[i][j].sum()));
sumr += unsigned ? table[i][j].r - Byte.MIN_VALUE : table[i][j].r;
sumg += unsigned ? table[i][j].g - Byte.MIN_VALUE : table[i][j].g;
sumb += unsigned ? table[i][j].b - Byte.MIN_VALUE : table[i][j].b;
}
System.out.println();
}
System.out.println(String.format("mean : (%d,%d,%d) %d", sumr / (size * size), sumg / (size * size), sumb / (size * size), (sumr + sumg + sumb) / (size * size)));
}
void inspectDivergence(int x0, int y0, int size) {
System.out.println(String.format("Inspect divergence in area : %d-%d x %d-%d", x0, x0 + size, y0, y0 + size));
int sumr = 0;
int sumg = 0;
int sumb = 0;
for (int x = x0; x < x0 + size; x++) {
for (int y = y0; y < y0 + size; y++) {
Color div = table[x][y];
if (x > 0 && y == 0) {
div = Color.add(new float[]{1f,-1f},new Color[]{table[x][y], table[x - 1][y]});
} else if (x == 0 && y > 0) {
div = Color.add(new float[]{1f,-1f},new Color[]{table[x][y], table[x][y-1]});
} else if (x > 0 && y > 0) {
div = Color.add(new float[]{1f,-slope,slope-1f},new Color[]{table[x][y], table[x][y - 1], table[x - 1][y]});
}
System.out.print(String.format("%1$-12s %2$-4d ", div, div.sum()));
sumr += div.r;
sumg += div.g;
sumb += div.b;
}
System.out.println();
}
System.out.println(String.format("mean : (%d,%d,%d) %d", sumr / (size * size), sumg / (size * size), sumb / (size * size), (sumr + sumg + sumb) / (size * size)));
}
byte[] getData() {
byte[] data = new byte[width * height * 3];
int k = 0;
for (int y = 0; y < height2; y++) {
for (int x = 0; x < width2; x++) {
for (int j = 0; j < Math.min(size, width - x * size); j++) {
if (k >= data.length) {
System.err.println("\t\toverflow at x:" + x + " y:" + y + " j:" + j + " k:" + k);
return data;
}
data[k++] = table[x][y].r;
data[k++] = table[x][y].g;
data[k++] = table[x][y].b;
}
}
for (int i = 0; i < Math.min(size, height - y * size) - 1; i++) {
Utils.writeArray(data, data, k, k + width * 3, k - width * 3);
k += width * 3;
}
}
return data;
}
}
+87
View File
@@ -0,0 +1,87 @@
package fr.klemek.marble;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
final class ImageUtils {
private static final int HEADER_SIZE = 54;
private ImageUtils() {
}
static byte[] generateBmpFile(int width, byte[] data) {
int height = data.length / (width * 3);
data = fixBmpData(data, width, height);
byte[] output = new byte[HEADER_SIZE + data.length];
Utils.writeArray(output, getBmpHeader(data.length, width, height), 0);
Utils.writeArray(output, data, HEADER_SIZE);
return output;
}
private static byte[] fixBmpData(byte[] data, int width, int height) {
if (data.length > height * width * 3)
data = Utils.subArray(data, 0, height * width * 3);
Utils.translateUnsigned(data);
int linePadding = (width * 3) % 4;
if (linePadding > 0) {
byte[] tail = new byte[4-linePadding];
tail[tail.length-1] = (byte) 255;
data = Utils.interlaceArrays(data, tail, width * 3, height);
}
return data;
}
private static byte[] getBmpHeader(int dataSize, int width, int height) {
byte[] header = new byte[HEADER_SIZE];
Utils.writeArray(header, new byte[]{(int) 66, (int) 77}, 0); // BM
Utils.writeArray(header, Utils.num2bytes(dataSize + HEADER_SIZE), 2); // file size
//4 bytes application reserved
Utils.writeArray(header, Utils.num2bytes(HEADER_SIZE), 10); // offset of data
Utils.writeArray(header, Utils.num2bytes(40), 14); // real size of header
Utils.writeArray(header, Utils.num2bytes(width), 18); //width
Utils.writeArray(header, Utils.num2bytes(height), 22); //height
Utils.writeArray(header, Utils.num2bytes(1, 2), 26); //color panes
Utils.writeArray(header, Utils.num2bytes(24, 2), 28); //color depth
//4 bytes compression method
//4 bytes optional image size
Utils.writeArray(header, Utils.num2bytes(3780), 38); //horizontal resolution
Utils.writeArray(header, Utils.num2bytes(3780), 42); //vertical resolution
//4 bytes number of colors
//4 bytes number of important colors
return header;
}
static boolean saveBmpFile(byte[] data, File bmpFile){
try (FileOutputStream fos = new FileOutputStream(bmpFile.getPath())) {
fos.write(data);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
static boolean convertBmpToJpg(File bmpFile, File jpgFile){
try {
BufferedImage inputImage = ImageIO.read(bmpFile);
ImageIO.write(inputImage, "JPG", jpgFile);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
+12
View File
@@ -0,0 +1,12 @@
package fr.klemek.marble;
public class LocalTests {
public static void main(String[] args) {
Generator gen = new Generator(800, 800, 2);
gen.generate();
new MarbleViewer(gen);
}
}
+44
View File
@@ -0,0 +1,44 @@
package fr.klemek.marble;
import javax.swing.*;
import java.awt.*;
public class MarbleViewer extends JFrame {
public MarbleViewer(Generator generator){
this.setLocation(0,0);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setResizable(false);
this.add(new Panel(generator));
this.pack();
this.setVisible(true);
}
private class Panel extends JPanel{
private Generator generator;
Panel(Generator generator){
this.generator = generator;
Dimension size= new Dimension(generator.getWidth(), generator.getHeight());
this.setMinimumSize(size);
this.setPreferredSize(size);
this.setMaximumSize(size);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int size = generator.getSize();
for(int x = 0; x < generator.getWidth2(); x++){
for(int y = 0; y < generator.getHeight2(); y++){
g.setColor(generator.getTable()[x][y].toColor());
g.fillRect(x*size, y*size, size, size);
}
}
}
}
}
+91
View File
@@ -0,0 +1,91 @@
package fr.klemek.marble;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
final class Utils {
private Utils() {
}
/*
* byte utils
*/
static byte randomByte(Random rand) {
return (byte) (rand.nextInt(Byte.MAX_VALUE - Byte.MIN_VALUE) + Byte.MIN_VALUE);
}
static byte bound(int value) {
if (value > Byte.MAX_VALUE)
return Byte.MAX_VALUE;
if (value < Byte.MIN_VALUE)
return Byte.MIN_VALUE;
return (byte) value;
}
/*
* byte array utils
*/
static byte[] subArray(byte[] array, int start, int stop) {
byte[] out = new byte[stop - start];
for (int i = start; i < stop; i++) {
out[i - start] = array[i];
}
return out;
}
static void translateUnsigned(byte[] data){
for(int i = 0; i < data.length; i++){
data[i] = (byte) (data[i] - Byte.MIN_VALUE);
}
}
static byte[] interlaceArrays(byte[] data, byte[] added, int size, int times){
byte[] out = new byte[data.length + added.length*times];
int size2 = size+added.length;
for(int i = 0; i < times; i++){
writeArray(out, data, size2*i, size2*i+size, size*i);
writeArray(out, added, size2*i+size, size2*(i+1), 0);
}
writeArray(out, data, size2*times, out.length-size2*times, size*times);
return out;
}
static void writeArray(byte[] out, byte[] data, int start, int stop, int padding){
for(int i = 0; i < Math.min(stop, out.length)-start; i++){
out[i+start] = data[i+padding];
}
}
static void writeArray(byte[] out, byte[] data, int start, int padding){
writeArray(out, data, start, start+data.length-padding, padding);
}
static void writeArray(byte[] out, byte[] data, int start){
writeArray(out, data, start, 0);
}
static byte[] num2bytes(int number, int nbyte){
byte[] b = new byte[nbyte];
for(int i = 0; i < nbyte; i++){
b[i] = (byte)(number%256);
number = number/256;
}
return b;
}
static byte[] num2bytes(int number){
return num2bytes(number, 4);
}
static int randInt(int min, int max){
return ThreadLocalRandom.current().nextInt(max-min)+min;
}
static int div(Random rand, byte src){
return Math.round(rand.nextFloat()*2*src-src);
}
}
@@ -0,0 +1,73 @@
package fr.klemek.marble;
import java.awt.*;
import java.io.File;
class WallpaperGenerator {
public static void main(String[] args) {
String file = "wallpaper";
Dimension screen = getScreenSizes();
int width = screen.width;
int height = screen.height;
int size = 0;
if (args.length > 0) {
file = args[0];
}
if (args.length > 1) {
width = Integer.parseInt(args[1]);
height = Integer.parseInt(args[1]);
}
if (args.length > 2) {
height = Integer.parseInt(args[2]);
}
if (args.length > 3) {
size = Integer.parseInt(args[3]);
}
makeWallpaper(file, width, height, size);
}
static Dimension getScreenSizes() {
Dimension dim = new Dimension(0, 0);
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
dim.width = Math.max(dim.width, gd.getDisplayMode().getWidth());
dim.height = Math.max(dim.height, gd.getDisplayMode().getHeight());
}
return dim;
}
static void makeWallpaper(String name, int width, int height, int size) {
System.out.println("Making wallpaper '" + name + "' " + width + "x" + height + "px");
File bmpFile = new File(name + ".bmp");
File outputFile = new File(name + ".jpg");
long t0 = System.currentTimeMillis();
Generator gen = size == 0 ? new Generator(width, height) : new Generator(width, height, size);
long t1 = System.currentTimeMillis();
gen.generate();
System.out.println("\tGeneration done in " + (System.currentTimeMillis() - t1) + " ms");
t1 = System.currentTimeMillis();
byte[] file = ImageUtils.generateBmpFile(width, gen.getData());
System.out.println("\t\t" + file.length + " bytes");
System.out.println("\tData writing done in " + (System.currentTimeMillis() - t1) + " ms");
t1 = System.currentTimeMillis();
if(!ImageUtils.saveBmpFile(file, bmpFile))
return;
System.out.println("\tFile writing done in " + (System.currentTimeMillis() - t1) + " ms");
t1 = System.currentTimeMillis();
if(!ImageUtils.convertBmpToJpg(bmpFile, outputFile))
return;
System.out.println("\tFile converting done in " + (System.currentTimeMillis() - t1) + " ms");
if (bmpFile.delete())
System.out.println("\tFile '" + bmpFile.getName() + "' deleted");
System.out.println("Wallpaper done in " + (System.currentTimeMillis() - t0) + " ms");
}
}
+72
View File
@@ -0,0 +1,72 @@
package fr.klemek.marble;
import static org.junit.Assert.*;
public class UtilsTest {
@org.junit.Test
public void bound() {
assertEquals((byte)-128, Utils.bound(-200));
assertEquals((byte)-128, Utils.bound(-129));
assertEquals((byte)-128, Utils.bound(-128));
assertEquals((byte)-64, Utils.bound(-64));
assertEquals((byte)0, Utils.bound(0));
assertEquals((byte)64, Utils.bound(64));
assertEquals((byte)127, Utils.bound(127));
assertEquals((byte)127, Utils.bound(128));
assertEquals((byte)127, Utils.bound(200));
}
@org.junit.Test
public void subArray() {
byte[] array0 = new byte[]{0,1,2,3,4,5,6,7,8,9};
assertArrayEquals(new byte[]{2,3,4}, Utils.subArray(array0, 2, 5));
}
@org.junit.Test
public void translateUnsigned() {
byte[] array0 = new byte[]{-128,-64,0,64,127};
Utils.translateUnsigned(array0);
assertArrayEquals(new byte[]{0,64,(byte)128,(byte)192,(byte)255}, array0);
}
@org.junit.Test
public void interlaceArrays() {
byte[] array0 = new byte[10];
byte[] array1 = new byte[]{1,1};
assertArrayEquals(new byte[]{0,0,0,1,1,0,0,0,1,1,0,0,0,0}, Utils.interlaceArrays(array0, array1, 3,2));
}
@org.junit.Test
public void writeArray() {
byte[] array0 = new byte[10];
byte[] array1 = new byte[]{0,1,2,3,4,5,6,7,8,9};
Utils.writeArray(array0,array1,2,5,3);
assertArrayEquals(new byte[]{0,0,3,4,5,0,0,0,0,0}, array0);
}
@org.junit.Test
public void writeArray1() {
byte[] array0 = new byte[10];
byte[] array1 = new byte[]{0,1,2,3,4,5,6,7,8,9};
Utils.writeArray(array0,array1,2,3);
assertArrayEquals(new byte[]{0,0,3,4,5,6,7,8,9,0}, array0);
}
@org.junit.Test
public void writeArray2() {
byte[] array0 = new byte[10];
byte[] array1 = new byte[]{0,1,2,3,4,5,6,7,8,9};
Utils.writeArray(array0,array1,2);
assertArrayEquals(new byte[]{0,0,0,1,2,3,4,5,6,7}, array0);
}
@org.junit.Test
public void num2bytes() {
}
@org.junit.Test
public void num2bytes1() {
}
}