Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added actualImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractal1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractal2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractal4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractal8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractalWithCorrection1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractalWithCorrection2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractalWithCorrection4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fractalWithCorrection8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions src/main/java/edu/FractalImage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package edu;

public class FractalImage {

Pixel[][] data;
int width;
int height;

FractalImage(Pixel[][] data, int width, int height) {
this.data = data;
this.width = width;
this.height = height;

}

FractalImage(int width, int height) {
this.data = new Pixel[width][height];
this.width = width;
this.height = height;

for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
data[x][y] = new Pixel(0, 0, 0, 0);
}
}
}

boolean contains(int x, int y) {
return x >= 0 && x < width && y >= 0 && y < height;
}

Pixel pixel(int x, int y) {
return data[x][y];
}

void updatePixel(int x, int y, Pixel pixel) {
if (contains(x, y)) {
data[x][y] = pixel;
}
}

public int width() {
return width;
}

public int height() {
return height;
}
}
34 changes: 34 additions & 0 deletions src/main/java/edu/ImageProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package edu;

import static java.lang.Math.log10;
import static java.lang.Math.pow;

public
interface ImageProcessor {

static FractalImage gammaCorrection(FractalImage image) {
double max = 0.0;
double gamma = 4.2;
double[][] normals = new double[image.width()][image.height()];
for (int x = 0; x < image.width(); x++) {
for (int y = 0; y < image.height(); y++) {
if (image.pixel(x, y).hitCount() != 0) {
normals[x][y] = log10(image.pixel(x, y).hitCount());
if (normals[x][y] > max) {
max = normals[x][y];
}
}
}
}
for (int x = 0; x < image.width(); x++) {
for (int y = 0; y < image.height(); y++) {
normals[x][y] /= max;
int red = (int) (image.pixel(x, y).r() * pow(normals[x][y], (1.0 / gamma)));
int green = (int) (image.pixel(x, y).g() * pow(normals[x][y], (1.0 / gamma)));
int blue = (int) (image.pixel(x, y).b() * pow(normals[x][y], (1.0 / gamma)));
image.updatePixel(x, y, new Pixel(red, green, blue, image.pixel(x, y).hitCount()));
}
}
return image;
}
}
40 changes: 40 additions & 0 deletions src/main/java/edu/ImageUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package edu;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public final class ImageUtils {
private ImageUtils() {
}

enum ImageFormat { JPEG, BMP, PNG }

static void save(FractalImage image, String filename, ImageFormat format) {

BufferedImage bufferedImage = new BufferedImage(
image.width(),
image.height(),
BufferedImage.TYPE_INT_RGB
);

for (int x = 0; x < image.width(); x++) {
for (int y = 0; y < image.height(); y++) {
Pixel pixel = image.pixel(x, y);
Color col = new Color(pixel.r(), pixel.g(), pixel.b());
bufferedImage.setRGB(x, y, col.getRGB());
}
}

try {
File file = new File(filename);
ImageIO.write(bufferedImage, format.name().toLowerCase(), file);
} catch (IOException e) {
throw new RuntimeException(e);
}

}

}
91 changes: 91 additions & 0 deletions src/main/java/edu/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package edu;

import java.util.List;
import java.util.Random;
import static edu.ImageProcessor.gammaCorrection;
import static edu.ImageUtils.ImageFormat.PNG;
import static edu.Renderer.renderConcurrent;

public class Main {

private Main() {
}

static final int WIDTH = 2000;
static final int HEIGHT = 2000;
static final int SAMPLES = 500000;
static final int ITER_PER_SAMPLE = 800;

static final int SYMMETRY = 1;

static final String NAME = "fractal";

static final ImageUtils.ImageFormat IMAGE_FORMAT = PNG;

static final int[] THREADS_NUMBERS = new int[] {1, 2, 4, 8};

@SuppressWarnings("RegexpSinglelineJava")
public static void main(String[] args) {

List<Transformation> allTransformations = List.of(
new TransformationSin(),
new TransformationSin(),
new TransformationSin(),
new TransformationSphere(),
new TransformationSphere(),
new TransformationSphere(),
new TransformationSphere(),
new TransformationHeart(),
new TransformationHeart(),
new TransformationDisk(),
new TransformationDisk(),
new TransformationDisk()
);

Random random = new Random();

for (int i : THREADS_NUMBERS) {

System.out.println("Choosing transformations....");

List<Transformation> transformations = List.of(
allTransformations.get(random.nextInt(allTransformations.size())),
allTransformations.get(random.nextInt(allTransformations.size())),
allTransformations.get(random.nextInt(allTransformations.size())),
allTransformations.get(random.nextInt(allTransformations.size()))
);

System.out.println("Generating image....");

long startTime = System.currentTimeMillis();

FractalImage result =
renderConcurrent(
new FractalImage(WIDTH, HEIGHT),
transformations,
SAMPLES,
Main.ITER_PER_SAMPLE,
SYMMETRY,
i
);

System.out.println("Generation time: " + (System.currentTimeMillis() - startTime));
System.out.println("Treads number: " + i);

String fileName = NAME + i + "." + IMAGE_FORMAT.toString().toLowerCase();
String fileNameWithCorrection =
NAME + "WithCorrection" + i + "." + IMAGE_FORMAT.toString().toLowerCase();

ImageUtils.save(result, fileName, IMAGE_FORMAT);
ImageUtils.save(
gammaCorrection(result),
fileNameWithCorrection,
IMAGE_FORMAT
);
System.out.println("Generated image in file " + fileName);
System.out.println("Generated image with gamma correction in file " + fileNameWithCorrection);
System.out.println();

}
}
}
3 changes: 3 additions & 0 deletions src/main/java/edu/Pixel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package edu;

public record Pixel(int r, int g, int b, int hitCount) { }
3 changes: 3 additions & 0 deletions src/main/java/edu/Point.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package edu;

public record Point(double x, double y) {}
96 changes: 96 additions & 0 deletions src/main/java/edu/Renderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package edu;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;

public class Renderer {

private Renderer() {
}

static FractalImage renderConcurrent(
FractalImage canvas,
List<Transformation> variations,
int samples,
int iterPerSample,
int symmetry,
int threadsNumber
) {
Runnable onePixel = () -> {
Point pw;
double x = ThreadLocalRandom.current().nextDouble(-1, 1);
double y = ThreadLocalRandom.current().nextDouble(-1, 1);
pw = new Point(x, y);

for (short step = 0; step < iterPerSample; ++step) {
Transformation variation = variations.get(ThreadLocalRandom.current().nextInt(variations.size()));

pw = variation.apply(pw);

double theta2 = 0.0;
for (int s = 0; s < symmetry; theta2 += Math.PI * 2 / symmetry, ++s) {

var pwr = rotate(pw, theta2);

pwr = new Point((pwr.x() + 1) * canvas.width() / 2, (pwr.y() + 1) * canvas.height() / 2);

int newX = (int) pwr.x();
int newY = (int) pwr.y();

if (!canvas.contains(newX, newY)) {
continue;
}

Pixel pixel = generatePixel(canvas, newX, newY, variation);

synchronized (canvas.pixel(newX, newY)) {
canvas.updatePixel(newX, newY, pixel);

}

}
}

};

ExecutorService executor = Executors.newFixedThreadPool(threadsNumber);
for (int i = 0; i < samples; i++) {
executor.execute(onePixel);
}

executor.shutdown();
executor.close();
return canvas;
}

private static Pixel generatePixel(FractalImage canvas, int newX, int newY, Transformation variation) {
int r;
int g;
int b;
if (canvas.pixel(newX, newY).hitCount() == 0) {
r = variation.r();
g = variation.g();
b = variation.b();
} else {
r = (canvas.pixel(newX, newY).r() + variation.r()) / 2;
g = (canvas.pixel(newX, newY).g() + variation.g()) / 2;
b = (canvas.pixel(newX, newY).b() + variation.b()) / 2;
}

return new Pixel(
r,
g,
b,
canvas.pixel(newX, newY).hitCount() + 1
);
}

private static Point rotate(Point point, double angle) {
double x = point.x() * Math.cos(angle) + point.y() * Math.sin(angle);
double y = -point.x() * Math.sin(angle) + point.y() * Math.cos(angle);
return new Point(x, y);
}

}
69 changes: 69 additions & 0 deletions src/main/java/edu/Transformation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package edu;

import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Function;

public abstract class Transformation implements Function<Point, Point> {


static final int RGB_MAX_COLOUR = 255;

double aCoef;
double bCoef;
double cCoef;
double dCoef;
double eCoef;
double fCoef;

int rColor;
int gColor;
int bColor;

Transformation() {
while (true) {
aCoef = ThreadLocalRandom.current().nextDouble(-1, 1);
bCoef = ThreadLocalRandom.current().nextDouble(-1, 1);
cCoef = ThreadLocalRandom.current().nextDouble(-1, 1);
dCoef = ThreadLocalRandom.current().nextDouble(-1, 1);
eCoef = ThreadLocalRandom.current().nextDouble(-1, 1);
fCoef = ThreadLocalRandom.current().nextDouble(-1, 1);
if ((aCoef * aCoef + dCoef * dCoef < 1) && (bCoef * bCoef + eCoef * eCoef < 1)
&& (aCoef * aCoef + bCoef * bCoef + dCoef * dCoef + eCoef * eCoef
< 1 + (aCoef * eCoef - bCoef * dCoef) * (aCoef * eCoef - bCoef * dCoef))) {
generateColour();
return;
}
}
}

public Point apply(Point point) {
return doNonlinearTransformation(doLinearTransformation(point));
}

Point doLinearTransformation(Point point) {
double x = point.x();
double y = point.y();
return new Point(aCoef * x + bCoef * y + cCoef, dCoef * x + eCoef * y + fCoef);
}

public int r() {
return rColor;
}

public int g() {
return gColor;
}

public int b() {
return bColor;
}

protected abstract Point doNonlinearTransformation(Point point);

void generateColour() {
rColor = ThreadLocalRandom.current().nextInt(RGB_MAX_COLOUR);
gColor = ThreadLocalRandom.current().nextInt(RGB_MAX_COLOUR);
bColor = ThreadLocalRandom.current().nextInt(RGB_MAX_COLOUR);

}
}
Loading