Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package com.photoembroidery.tat.olsennoise;
- //MIT License, use for anything you want commercial or open, with or without attribution.
- import java.util.Arrays;
- public class OlsenNoise2D {
- public static final int MAX_ITERATIONS = 7;
- //just definition for other classes to use.
- public static final int SCALE_FACTOR = 2;
- //The scale factor is kind of arbitrary, but the code is only consistent for 2 currently. Gives noise for other scale but not location proper.
- private static final int[][] blur3x3 = new int[][]{
- {1, 1, 1},
- {1, 1, 1},
- {1, 1, 1}
- }; // Matrix for the blur.
- private static final int blurEdge = 2; //extra pixels are needed for the blur (3 - 1).
- //This function has a soft coded iteration.
- /**
- * Function adds all the required pixels into the pixels array.
- * Note that the scanline should not actually equal the width.
- * It should be larger as per the getRequiredDim function.
- *
- * @param iterations Number of iterations to perform.
- * @param pixels pixel array to be used to insert values. (Pass by reference)
- * @param stride distance in the array to the next y value.
- * @param x requested X location.
- * @param y requested Y location.
- * @param width width of the image.
- * @param height height of the image.
- */
- public static void olsennoise( int iterations, int[] pixels, int stride, int x, int y, int width, int height) {
- olsennoise(pixels, stride, x, y, width, height, iterations); //Calls the main routine.
- applyColor(pixels, stride, width, height);
- }
- /**
- * Places all the grayscale colors of Olsen Noise into the array, provided.
- * @param pixels pixel array to be used to insert values. (Pass by reference)
- * @param stride distance in the array to the next y value.
- * @param x requested X location.
- * @param y requested Y location.
- * @param width width of the image.
- * @param height height of the image.
- */
- public static void olsennoise(int[] pixels, int stride, int x, int y, int width, int height) {
- olsennoise(pixels, stride, x, y, width, height, MAX_ITERATIONS);
- applyColor(pixels, stride, width, height);
- }
- /**
- * Converts a dimension into the dimension required by the algorithm.
- * Due to the blurring, to get valid data the array must be slightly larger.
- * Due to the interpixel location at lowest levels it needs to be bigger by
- * the max value that can be. (SCALE_FACTOR)
- * @param dim
- * @return
- */
- public static int getRequiredDim(int dim) {
- return dim + blurEdge + SCALE_FACTOR;
- }
- //Function inserts the values into the given pixels array (pass by reference)
- //The results will be within 0-255 assuming the requested iterations are 7.
- private static void olsennoise(int[] pixels, int stride, int x_within_field, int y_within_field, int width, int height, int iteration) {
- if (iteration == 0) {
- //Base case. If we are at the bottom. Do not run the rest of the function. Return random values.
- clearValues(pixels, stride, width, height); //base case needs zero, apply Noise will not eat garbage.
- applyNoise(pixels, stride, x_within_field, y_within_field, width, height, iteration);
- return;
- }
- int x_remainder = x_within_field & 1; //Adjust the x_remainder so we know how much more into the pixel are.
- int y_remainder = y_within_field & 1; //Math.abs(y_within_field % SCALE_FACTOR) - Would be assumed for larger scalefactors.
- /*
- Pass the pixels, and the stride for that set of pixels.
- Recurse the call to the function moving the x_within_field forward if we actaully want half a pixel at the start.
- Same for the y.
- The width should expanded by the x_remainder, and then half the size, with enough extra to store the extra pixels from the blur.
- If the width is too long, it'll just run more stuff than it needs to.
- */
- olsennoise(pixels, stride,
- ((x_within_field + x_remainder) / SCALE_FACTOR) - x_remainder,
- ((y_within_field + y_remainder) / SCALE_FACTOR) - y_remainder,
- ((width + x_remainder) / SCALE_FACTOR) + blurEdge,
- ((height + y_remainder) / SCALE_FACTOR) + blurEdge, iteration - 1);
- //This will scale the image from half the width and half the height. bounds.
- //The scale function assumes you have at least width/2 and height/2 good pixels.
- //We requested those from olsennoise above, so we should have that.
- applyScale(pixels, stride, width + blurEdge, height + blurEdge, SCALE_FACTOR);
- //We shift the pixels over. By the amount within those pixels we want.
- //We sometimes want half a pixel into a scaled pixel.
- applyShift(pixels, stride, x_remainder, y_remainder, width + blurEdge, height + blurEdge);
- //This applies the blur and uses the given bounds.
- //Since the blur loses two at the edge, this will result
- //in us having width x height of good pixels and required
- // width + blurEdge of good pixels. height + blurEdge of good pixels.
- applyBlur(pixels, stride, width + blurEdge, height + blurEdge);
- //Applies noise to all the given pixels. Does not require more or less than pixels. Just offsets them all randomly.
- applyNoise(pixels, stride, x_within_field, y_within_field, width, height, iteration);
- }
- private static void applyNoise(int[] pixels, int stride, int x_within_field, int y_within_field, int width, int height, int iteration) {
- int index = 0;
- for (int k = 0, n = height - 1; k <= n; k++, index += stride) { //iterate the y positions. Offsetting the index by stride each time.
- for (int j = 0, m = width - 1; j <= m; j++) { //iterate the x positions through width.
- int current = index + j; // The current position of the pixel is the index which will have added stride each, y iteration
- pixels[current] += (hashrandom(j + x_within_field, k + y_within_field, iteration) & (1 << (7 - iteration)));
- //add on to this pixel the hash function with the set reduction.
- //The amount of randomness here somewhat arbitary. Just have it give self-normalized results 0-255.
- //It simply must scale down with the larger number of iterations.
- }
- }
- }
- private static void applyScale(int[] pixels, int stride, int width, int height, int factor) {
- int index = (height - 1) * stride; //We must iteration backwards to scale so index starts at last Y position.
- for (int k = 0, n = height - 1; k <= n; n--, index -= stride) { // we iterate the y, removing stride from index.
- for (int j = 0, m = width - 1; j <= m; m--) { // iterate the x positions from width to 0.
- int current = index + m; //current position is the index (position of that scanline of Y) plus our current iteration in scale.
- int lower = ((n / factor) * stride) + (m / factor); //We find the position that is half that size. From where we scale them out.
- pixels[current] = pixels[lower]; // Set the outer position to the inner position. Applying the scale.
- }
- }
- }
- //This function is to insure position stability with interpixel locations.
- //the pixels at the lowest iteration get scaled up, and we need positions within this pixels at the higher level.
- //We'd still get noise without this function, it would just line up with the lower pixels.
- private static void applyShift(int[] pixels, int stride, int shiftX, int shiftY, int width, int height) {
- if ((shiftX == 0) && (shiftY == 0)) { //if we aren't actually trying to move it.
- return; //return
- }
- int index;
- int indexoffset = shiftX + (shiftY * stride); //The offset within the array that that shift would correspond to.
- //Since down and to the right is still (stride + 1) every loop. We preset it.
- index = 0;
- for (int k = 0, n = height - 1; k <= n; k++, index += stride) { // iterate the y values, add stride to index.
- for (int j = 0, m = width - 1; j <= m; j++) { //iterate the x values with j.
- int current = index + j; // current position is all our added up stride values, and our position in the X.
- pixels[current] = pixels[current + indexoffset]; //set the current pixel equal to the one shifted by offset.
- }
- }
- }
- //colorizes the image. This takes our values between 0-255 and makes them pixels with alpha.
- //0xAARRGGBB
- private static void applyColor(int[] pixels, int stride, int width, int height) {
- int index;
- index = 0;
- for (int k = 0, n = height - 1; k <= n; k++, index += stride) { //iterate the y values.
- for (int j = 0, m = width - 1; j <= m; j++) { //iterate the x values.
- int current = index + j; // current position is the sum of the strides plus the position in the x.
- int pixel = pixels[current]; //get the current pixel.
- pixels[current] = 0xFF000000 | pixel << 16 | pixel << 8 | pixel; //turn it into a greyscale.
- }
- }
- }
- private static void clearValues(int[] pixels, int stride, int width, int height) {
- int index;
- index = 0;
- for (int k = 0, n = height - 1; k <= n; k++, index += stride) { //iterate the y values.
- for (int j = 0, m = width - 1; j <= m; j++) { //iterate the x values.
- int current = index + j; // current position is the sum of the strides plus the position in the x.
- int pixel = pixels[current]; //get the current pixel.
- pixels[current] = 0; //clears those values.
- }
- }
- }
- //Applies the blur.
- private static void applyBlur(int[] pixels, int stride, int width, int height) {
- //Calls my convolve routine, with the matrix we setup initially.
- convolve(pixels, 0, stride, 0, 0, width, height, blur3x3);
- }
- /**
- * Memory Free In-Place Convolution.
- *
- * It is modified to not actually do all the color blending work.
- * The values passed to it are between 0-255
- * So it does a proper average.
- *
- * @param pixels pixels to be modified (pass by reference).
- * @param offset offset within the pixel array to call zero.
- * @param stride width of the memory block to next Y.
- * @param x the start x value.
- * @param y the start y value.
- * @param width the width of blocks to be used for the convolution.
- * @param height the height of the convolution area.
- * @param matrix matrix of the convolution.
- */
- public static void convolve(int[] pixels, int offset, int stride, int x, int y, int width, int height, int[][] matrix) {
- int index = offset + x + (y*stride); //index is where we are in the pixels. All equal 0 for our use. Y=0, X=0, offset = 0.
- for (int j = 0; j < height; j++, index += stride) { // iterate the y values. adding stride to index each time.
- for (int k = 0; k < width; k++) {//iterate the x values
- int pos = index + k; //current position sum of the strides and the position in the x.
- pixels[pos] = convolve(pixels,stride,pos, matrix); //convolves the matrix down and to the right from the current position.
- //this is somewhat non-standard, but that's because everybody's been doing it wrong for basically ever.
- }
- }
- }
- //crimps the values between 255 and 0. Required for some other convolutions like emboss where they go out of register.
- private static int crimp(int color) {
- return (color >= 0xFF) ? 0xFF : (color < 0) ? 0 : color;
- }
- //performs the convolution on that pixel by the given matrix.
- //Note all values within the matrix are down and to the right from the current pixel.
- //None are up or to the left. This is by design.
- private static int convolve(int[] pixels, int stride, int index, int[][] matrix) {
- int parts = 0;
- int sum = 0;
- int factor;
- for (int j = 0, m = matrix.length; j < m; j++, index+=stride) { //iterates the matrix
- for (int k = 0, n = matrix[j].length; k < n; k++) { //iterates the matrix[] within.
- factor = matrix[j][k]; //gets the multiple from that matrix.
- parts += factor; //keeps a running total for the parts.
- sum += factor * pixels[index + k]; //keeps a total of the sum of the factors and the pixels they correspond to.
- }
- }
- if (parts == 0) return crimp(sum);
- return crimp(sum/parts);
- }
- /**
- * XOR hash the hashed values of each element, in elements
- * @param elements elements to be hashed and xor'ed together.
- * @return
- */
- public static int hashrandom(int... elements) {
- long hash = 0;
- for (int i = 0; i < elements.length; i++) {
- hash ^= elements[i];
- hash = hash(hash);
- }
- return (int) hash;
- }
- private static long hash(long v) {
- long hash = v;
- long h = hash;
- switch ((int) hash & 3) {
- case 3:
- hash += h;
- hash ^= hash << 32;
- hash ^= h << 36;
- hash += hash >> 22;
- break;
- case 2:
- hash += h;
- hash ^= hash << 22;
- hash += hash >> 34;
- break;
- case 1:
- hash += h;
- hash ^= hash << 20;
- hash += hash >> 2;
- }
- hash ^= hash << 6;
- hash += hash >> 10;
- hash ^= hash << 8;
- hash += hash >> 34;
- hash ^= hash << 50;
- hash += hash >> 12;
- return hash;
- }
- /**
- * Trim off the edge pixels if you need such a function.
- * Gets rid of the side bits.
- *
- * @param pixels destination array
- * @param width Width of the new image
- * @param height height of the new image
- * @param workingpixels source array
- * @param workingstride stride of source array
- */
- public static void trim(int[] pixels, int width, int height, int[] workingpixels, int workingstride) {
- for (int k = 0; k < height; k++) {
- for (int j = 0; j < width; j++) {
- int index = j + (k * width);
- int workingindex = j + (k * workingstride);
- pixels[index] = workingpixels[workingindex];
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement