Created
June 18, 2019 18:12
-
-
Save tatarize/e91c077f8759d9a9b5165b782ec6a6fd to your computer and use it in GitHub Desktop.
Sample BlackWhiteRaster with PixelWindow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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