Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimized BlackWhiteRaster/Added Needed RasterablePart Function #102

Closed
wants to merge 13 commits into from
220 changes: 185 additions & 35 deletions src/com/t_oster/liblasercut/BlackWhiteRaster.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
* along with LibLaserCut. If not, see <http://www.gnu.org/licenses/>.
*
**/

package com.t_oster.liblasercut;

import com.t_oster.liblasercut.dithering.*;
import java.util.Arrays;

/**
*
Expand All @@ -37,9 +39,6 @@ public static enum DitherAlgorithm
HALFTONE,
BRIGHTENED_HALFTONE
}
private int width;
private int height;
private byte[][] raster;

public static DitheringAlgorithm getDitheringAlgorithm(DitherAlgorithm alg)
{
Expand All @@ -60,26 +59,33 @@ public static DitheringAlgorithm getDitheringAlgorithm(DitherAlgorithm alg)
case BRIGHTENED_HALFTONE:
return new BrightenedHalftone();
default:
throw new IllegalArgumentException("Desired Dithering Algorithm ("+alg+") does not exist");
throw new IllegalArgumentException("Desired Dithering Algorithm (" + alg + ") does not exist");
}
}

private byte[] imageData;
private int stride;
private int width;
private int height;
private int bitDepth;
private int samplesPerPixel;


public BlackWhiteRaster(GreyscaleRaster src, DitheringAlgorithm alg, ProgressListener listener) throws InterruptedException
{
this(src.getWidth(), src.getHeight());
if (listener != null)
{
this.addProgressListener(listener);
}
this.width = src.getWidth();
this.height = src.getHeight();
raster = new byte[(src.getWidth() + 7) / 8][src.getHeight()];

if (listener != null)
{
alg.addProgressListener(listener);
}
alg.ditherDirect(src, this);
}

public BlackWhiteRaster(GreyscaleRaster src, DitherAlgorithm dither_algorithm, ProgressListener listener) throws InterruptedException
{
this(src, BlackWhiteRaster.getDitheringAlgorithm(dither_algorithm), listener);
Expand All @@ -89,77 +95,221 @@ public BlackWhiteRaster(GreyscaleRaster src, DitherAlgorithm dither_algorithm) t
{
this(src, dither_algorithm, null);
}

public BlackWhiteRaster(GreyscaleRaster src, DitheringAlgorithm alg) throws InterruptedException
{
this(src, alg, null);
}

public BlackWhiteRaster(int width, int height, byte[][] raster)

public BlackWhiteRaster(int width, int height)
{
this.width = width;
this.height = height;
this.raster = raster;
this(width,height,1,1);
}

public BlackWhiteRaster(int width, int height)


public BlackWhiteRaster(int width, int height, int bitDepth)
{
this(width,height,bitDepth,1);
}


public BlackWhiteRaster(int width, int height, int bitDepth, int samplesPerPixel) {
this.width = width;
this.height = height;
this.raster = new byte[(width + 7) / 8][height];
this.bitDepth = bitDepth;
this.samplesPerPixel = samplesPerPixel;
this.stride = (int)Math.ceil(bitDepth * samplesPerPixel * ((float)width) / 8.0);
this.imageData = new byte[stride * height];
}



public int getPixel(int x, int y) {
return getPixel(x,y,0,false);
}
public int setPixel(int x, int y, int v) {
return getPixel(x,y,v,true);
}

private int getPixel(int x, int y, int replace, boolean set) {
int offset = stride * y;
int pixelLengthInBits = samplesPerPixel * bitDepth;
int startPosInBits = (offset * 8) + x * pixelLengthInBits;
int endPosInBits = startPosInBits + pixelLengthInBits - 1;
int startPosInBytes = startPosInBits / 8;
int endPosInBytes = endPosInBits / 8;
int value = 0;
for (int i = startPosInBytes; i <= endPosInBytes; i++) {
value <<= 8;
value |= (imageData[i] & 0xFF);
}
int unusedBitsRightOfSample = (8 - (endPosInBits + 1) % 8) % 8;
int maskSampleBits = (1 << pixelLengthInBits) - 1;
int pixel = (value >> unusedBitsRightOfSample) & maskSampleBits;
if (!set) return pixel;

value &= ~(maskSampleBits << unusedBitsRightOfSample);
value |= (replace & maskSampleBits) << unusedBitsRightOfSample;
for (int i = endPosInBytes; i >= startPosInBytes; i--) {
imageData[i] = (byte)(value & 0xff);
value >>= 8;
}
return pixel;
}

public boolean isBlack(int x, int y)
{
int bx = x / 8;
int ix = 7 - (x % 8);
return ((raster[bx][y] & 0xFF) & (int) Math.pow(2,ix)) != 0;
int pixel = getPixel(x,y);
return (pixel != 0);
}

public void setBlack(int x, int y, boolean black)
{
int bx = x / 8;
int ix = 7 - (x % 8);
raster[bx][y] = (byte) (((raster[bx][y] & 0xFF) & ~((int) Math.pow(2,ix))) | (black ? (int) Math.pow(2,ix) : 0 ));
if (black) {
setPixel(x,y,-1);
}
else {
setPixel(x,y,0);
}
}

/**
* Returns the Byte where every bit represents one pixel 0=white and 1=black
* NOTE THAT THE BITORDER IS [BBBBBBWW] = 0b11111100;
* @param x the x index of the byte, meaning 0 is the first 8 pixels (0-7), 1
* the pixels 8-15 ...
* In bitDepth 1.
* Returns byte where every bit represents one pixel 0=white and 1=black
* Note: the bit order is bigendian. Meaning the most significant digit is the
* first pixel.
*
* In bitDepth 8.
* Returns the byte representing the specific grey.
*
* @param x the x index of the byte, meaning 0 is the first 8 pixels (0-7), 1
* the pixels 8-15...
* @param y the y offset
* @return
* @return
*/
public byte getByte(int x, int y)
public byte getByte(int x, int y)
{
return raster[x][y];
int index = y * stride + x;
return imageData[index];
}


public byte[] getRasterLine(int y, byte[] bytes) {
if ((bytes == null) || (bytes.length < stride)) {
return Arrays.copyOfRange(imageData, y * stride, (y+1) * stride);
}
System.arraycopy(imageData, y * stride, bytes, 0, stride);
return bytes;
}

public boolean isLineBlank(int y)
{
for (int i = y * stride, ie = (y + 1) * stride; i < ie; i++)
{
if (imageData[i] != 0)
{
return false;
}
}
return true;
}

private int mostSignificantBit(byte i)
{
int mask = 1 << 7;
for (int bitIndex = 7; bitIndex >= 0; bitIndex--)
{
if ((i & mask) != 0)
{
return bitIndex;
}
mask >>>= 1;
}
return -1;
}

private int leastSignificantBit(byte i)
{
for (int bitIndex = 0; bitIndex < 8; bitIndex++)
{
if ((i & 1) != 0)
{
return bitIndex;
}
i >>>= 1;
}
return -1;
}

public int leftmostBlackPixel(int y)
{
int offset = y * stride;
for (int i = 0; i < stride; i++)
{
if (imageData[offset + i] != 0)
{
return (i * 8) + (7-mostSignificantBit(imageData[offset + i]));
}
}
return width;
}

public int rightmostBlackPixel(int y)
{
int offset = y * stride;
for (int i = stride-1; i >= 0; i--)
{
if (imageData[offset + i] != 0)
{
return (i * 8) + (7-leastSignificantBit(imageData[offset + i]));
}
}
return -1;
}

/**
* Convenience function to pretend this B&W image is greyscale
*
* @param x
* @param y
* @return 0 for black, 255 for white
*/
@Override
public int getGreyScale(int x, int y)
{
return isBlack(x, y) ? 0 : 255;
return getPixel(x,y);
}


@Override
public void setGreyScale(int x, int y, int color)
{
this.setBlack(x, y, color < 128);
setPixel(x,y, color);
}

@Override
public int getWidth()
{
return width;
}

@Override
public int getHeight()
{
return height;
}

public byte[] getImageData()
{
return imageData;
}

public int getBitDepth()
{
return bitDepth;
}

public int getSamplesPerPixel()
{
return samplesPerPixel;
}

}
}
56 changes: 56 additions & 0 deletions src/com/t_oster/liblasercut/RasterizableJobPart.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,63 @@ protected int rightMostNonWhitePixel(int y)
return x;
return 0;
}

/**
* Finds the last x coordinate for last non-white pixel on this or the next
* line
* @param y
* @return x coordinate of last non-white pixel on this or the next line
*/
public int lastNonWhitePixelOnThisOrNextLine(int y) {
return cutDirectionleftToRight
? rightMostNonWhitePixelOnThisOrNextLine(y)
: leftMostNonWhitePixelOnThisOrNextLine(y);
}

/**
* Finds the first x coordinate first non-white pixel on this or the next
* line
* @param y
* @return x coordinate of first non-white pixel on this or the next line
*/
public int firstNonWhitePixelOnThisOrNextLine(int y) {
return cutDirectionleftToRight
? leftMostNonWhitePixelOnThisOrNextLine(y)
: rightMostNonWhitePixelOnThisOrNextLine(y);
}

/**
* Finds the rightmost non-white pixel on this or the next line
* @param y
* @return x coordinate of first non-white pixel on this or the next line
*/
public int rightMostNonWhitePixelOnThisOrNextLine(int y)
{
int max = rightMostNonWhitePixel(y);
if (y >= this.getMaxY())
{
return max;
}
return Math.max(max, rightMostNonWhitePixel(y + 1));
}


/**
* Finds the leftmost non-white pixel on this or the next line
* @param y
* @return x coordinate of first non-white pixel on this or the next line
*/
public int leftMostNonWhitePixelOnThisOrNextLine(int y)
{
int min = leftMostNonWhitePixel(y);
if (y >= this.getMaxY())
{
return min;
}
return Math.max(min, rightMostNonWhitePixel(y + 1));
}


/**
* Given a pixel in a row of an image, finds the next pixel that has a different
* color. If no more color changes take place, returns the last interesting pixel.
Expand Down
Loading