Skip to content

Instantly share code, notes, and snippets.

@tatarize
Created June 18, 2019 18:12
Show Gist options
  • Save tatarize/e91c077f8759d9a9b5165b782ec6a6fd to your computer and use it in GitHub Desktop.
Save tatarize/e91c077f8759d9a9b5165b782ec6a6fd to your computer and use it in GitHub Desktop.
Sample BlackWhiteRaster with PixelWindow
/**
* This file is part of LibLaserCut.
* Copyright (C) 2011 - 2014 Thomas Oster <[email protected]>
*
* LibLaserCut is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LibLaserCut is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* 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.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
*
* @author Thomas Oster <[email protected]>
*/
public class BlackWhiteRaster extends TimeIntensiveOperation implements GreyscaleRaster
{
public static enum DitherAlgorithm
{
FLOYD_STEINBERG,
AVERAGE,
RANDOM,
ORDERED,
GRID,
HALFTONE,
BRIGHTENED_HALFTONE
}
private int width;
private int scanline;
private int height;
private int[] raster;
public static DitheringAlgorithm getDitheringAlgorithm(DitherAlgorithm alg)
{
switch (alg)
{
case FLOYD_STEINBERG:
return new FloydSteinberg();
case AVERAGE:
return new Average();
case RANDOM:
return new Random();
case ORDERED:
return new Ordered();
case GRID:
return new Grid();
case HALFTONE:
return new Halftone();
case BRIGHTENED_HALFTONE:
return new BrightenedHalftone();
default:
throw new IllegalArgumentException("Desired Dithering Algorithm (" + alg + ") does not exist");
}
}
public BlackWhiteRaster(GreyscaleRaster src, DitheringAlgorithm alg, ProgressListener listener) throws InterruptedException
{
this(src.getWidth(), src.getHeight());
if (listener != null)
{
this.addProgressListener(listener);
}
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);
}
public BlackWhiteRaster(GreyscaleRaster src, DitherAlgorithm dither_algorithm) throws InterruptedException
{
this(src, dither_algorithm, null);
}
public BlackWhiteRaster(GreyscaleRaster src, DitheringAlgorithm alg) throws InterruptedException
{
this(src, alg, null);
}
public BlackWhiteRaster(int width, int height, int[] raster)
{
this.width = width;
this.height = height;
this.raster = raster;
}
public BlackWhiteRaster(int width, int height)
{
this.width = width;
this.height = height;
this.scanline = (int) Math.ceil(width / 32.0);
this.raster = new int[scanline * height];
}
public boolean isBlack(int x, int y)
{
int index = y * scanline + (x / 32);
int ix = 31 - (x % 32);
int mask = 1 << ix;
return (raster[index] & mask) != 0;
}
public void setBlack(int x, int y, boolean black)
{
int index = y * scanline + (x / 32);
int ix = 31 - (x % 32);
int mask = 1 << ix;
raster[index] &= ~mask;
if (black)
{
raster[index] |= mask;
}
}
/**
* Returns the 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.
*
* @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
*/
public byte getByte(int x, int y)
{
int index = y * scanline + (x / 4);
int ix = (x % 4);
return (byte) ((raster[index] >> (8 * ix)) & 0xFF);
}
public byte getByte(int index)
{
index = index / 4;
int ix = (index % 4);
return (byte) ((raster[index] >> (8 * ix)) & 0xFF);
}
public void setByte(int index, byte pixels)
{
index = index / 4;
int ix = (index % 4);
int mask = 0xFF << (8 * ix);
raster[index] &= ~mask;
int pxset = (((int) pixels) & 0xFF) << (8 * ix);
raster[index] |= pxset;
}
/**
* Returns the integer 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.
*
* If this is the last integer in a line, note that the pixels value to the
* width point, and the remaining values are considered garbage.
*
* @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
*/
public int getInteger(int x, int y)
{
int index = y * scanline + x;
return raster[index];
}
public PixelWindow getRasterLine(int line)
{
return new PixelWindow(line * scanline, (line + 1) * scanline);
}
public boolean isLineBlank(int y)
{
for (int i = y * scanline, ie = (y + 1) * scanline; i < ie; i++)
{
if (raster[i] != 0)
{
return false;
}
}
return true;
}
private int mostSignificantBit(int i)
{
int mask = 1 << 31;
for (int bitIndex = 31; bitIndex >= 0; bitIndex--)
{
if ((i & mask) != 0)
{
return bitIndex;
}
mask >>>= 1;
}
return -1;
}
private int leastSignificantBit(int i)
{
for (int bitIndex = 0; bitIndex < 32; bitIndex++)
{
if ((i & 1) != 0)
{
return bitIndex;
}
i >>>= 1;
}
return -1;
}
public int leftmostBlackPixel(int y)
{
for (int i = y * scanline, ie = (y + 1) * scanline; i < ie; i++)
{
if (raster[i] != 0)
{
return (i % scanline) * 32 + (31 - mostSignificantBit(raster[i]));
}
}
return width;
}
public int rightmostBlackPixel(int y)
{
for (int i = ((y + 1) * scanline) - 1, ie = y * scanline; i >= ie; i--)
{
if (raster[i] != 0)
{
return (i % scanline) * 32 + (31 - leastSignificantBit(raster[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;
}
@Override
public void setGreyScale(int x, int y, int color)
{
this.setBlack(x, y, color < 128);
}
@Override
public int getWidth()
{
return width;
}
@Override
public int getHeight()
{
return height;
}
public int getScanline()
{
return scanline;
}
public class PixelWindow implements List<Byte>
{
boolean reverse = false;
int start = 0;
int end = 0;
public PixelWindow(int start, int end)
{
this.start = start;
this.end = end;
}
public PixelWindow(int start, int end, boolean reverse)
{
this.start = start;
this.end = end;
this.reverse = reverse;
}
public void reverse()
{
reverse = !reverse;
}
public void nextScanline()
{
start += scanline;
end += scanline;
}
public void nextScanlineReversed()
{
nextScanline();
reverse();
}
@Override
public int size()
{
return end - start;
}
@Override
public boolean isEmpty()
{
return end == start;
}
@Override
public boolean contains(Object o)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public Iterator<Byte> iterator()
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public Object[] toArray()
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public <T> T[] toArray(T[] a)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public boolean add(Byte e)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public boolean remove(Object o)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public boolean containsAll(Collection<?> c)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public boolean addAll(Collection<? extends Byte> c)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public boolean addAll(int index, Collection<? extends Byte> c)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public boolean removeAll(Collection<?> c)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public boolean retainAll(Collection<?> c)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public void clear()
{
start = 0;
end = 0;
reverse = false;
}
@Override
public Byte get(int index)
{
int si = (reverse) ? (end - 1) - index : start + index;
return getByte(si);
}
@Override
public Byte set(int index, Byte element)
{
int si = (reverse) ? (end - 1) - index : start + index;
byte b = getByte(si);
setByte(si, element);
return b;
}
@Override
public void add(int index, Byte element)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public Byte remove(int index)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public int indexOf(Object o)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public int lastIndexOf(Object o)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public ListIterator<Byte> listIterator()
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public ListIterator<Byte> listIterator(int index)
{
throw new UnsupportedOperationException("Not applicable");
}
@Override
public List<Byte> subList(int fromIndex, int toIndex)
{
return new PixelWindow(start + fromIndex, start + toIndex, reverse);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment