有些大图像直接存储会很浪费磁盘空间,因此可以将普通图像转换成RunLengthEncoding编码,以压缩存储空间。将图像看成是一个二维数组,数组的每个元素代表图像的一个像素,其中存储了R(Red)、G(Green)、B(Blue)的具体值。RunLengthEncoding就是首先将图像的每行像素连接到一起,形成一个长的一维数组。然后将相邻的具有相同RGB值的像素合并到一块存储。例如下图是一个图像变成一维数组的样子(这里假设每个像素的RGB值相同):
将该图像采用RunLenthEncoding后变成下面的样子,其中前面的数字代表RGB的具体值,后边数字代表相同的像素个数:
这里RunLengthEncoding采用双向链表实现,遍历RunLenthEncoding采用iterator实现,我叫RunIterator,其中RunIterator只能采用RunLenthEncoding类中的Iterator方法实例化,只有Iterator方法能够调用RunIterator的构造方法,各个类的代码如下:
实例化RunIterator:
public RunIterator iterator() {
RunIterator iterator = new RunIterator(this);
return iterator;
}
双向链表类:
public class DlinkedRunNode {
Run head;
int length;
public DlinkedRunNode(){
head = new Run();
length = 0;
}
public void addAfter(Run runNode){
Run tail = this.head.getPrev();
runNode.setNext(tail.getNext());
tail.setNext(runNode);
runNode.setPrev(tail);
head.setPrev(runNode);
length++;
}
public Run findFirst(){
return head.getNext();
}
public Run getTail() {
return head.getPrev();
}
RunIterator类:
public class RunIterator implements Iterator {
private RunLengthEncoding iterator;
private Run index;
RunIterator(RunLengthEncoding rLE) {
iterator = rLE;
index = iterator.getFirst();
}
public boolean hasNext() {
// Replace the following line with your solution.
if(iterator.getEncoding().getNext(index) != iterator.getEncoding().findFirst()){
return true;
}
return false;
}
public int[] next() {
int[] run = new int[4];
run[0] = index.getRunLength();
run[1] = index.getRed();
run[2] = index.getGreen();
run[3] = index.getBlue();
index = index.getNext();
return run;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
public Run getNext(Run runNode) {
return runNode.getNext();
}
}
首先将普通图像转换成RunLengthEncoding,这里直接使用构造方法并将普通图像作为参数传入直接构造出RunLenthEncoding:
public RunLengthEncoding(PixImage image) {
int hIndex = 0;
int wIndex = 1;
width = image.getWidth();
height = image.getHeight();
encoding = new DlinkedRunNode();
Pixel firstPixel = image.getPixel(0, 0);
Run firstRun = new Run(firstPixel.getRed(), firstPixel.getGreen(), firstPixel.getBlue(), 1);
encoding.addAfter(firstRun);
while(hIndex < height){
while(wIndex < width){
Run tail = encoding.getTail();
Pixel pixel = image.getPixel(wIndex, hIndex);
if((tail.getRed() == pixel.getRed()) &&
(tail.getGreen() == pixel.getGreen()) &&
(tail.getBlue() == pixel.getBlue())){
int runLength = tail.getRunLength() + 1;
tail.setRunLength(runLength);
wIndex++;
}else{
Run newRun = new Run(pixel.getRed(), pixel.getGreen(), pixel.getBlue(), 1);
encoding.addAfter(newRun);
wIndex++;
}
}
wIndex = 0;
hIndex++;
}
}
public PixImage toPixImage() {
int wIndex = 0;
int hIndex = 0;
int width = this.getWidth();
int height = this.getHeight();
PixImage image = new PixImage(width,height);
RunIterator iterator = this.iterator();
while(iterator.hasNext()){
int[] run = iterator.next();
for(int i = 0; i < run[0]; i++){
if((wIndex % width) == (width - 1)){
if(hIndex < height){
toImage(wIndex, hIndex, width, image, run);
hIndex++;
}else{
break;
}
}else{
toImage(wIndex, hIndex, width, image, run);
}
wIndex++;
}
}
return image;
}
private void toImage(int wIndex, int hIndex, int width, PixImage image,
int[] run) {
short red = (short) run[1];
short green = (short) run[2];
short blue = (short) run[3];
image.setPixel(wIndex % width, hIndex, red, green, blue);
}