geotrellis.raster.io.geotiff.compression.LZWDecompression.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of geotrellis-raster_2.11 Show documentation
Show all versions of geotrellis-raster_2.11 Show documentation
GeoTrellis is an open source geographic data processing engine for high performance applications.
The newest version!
package geotrellis.raster.io.geotiff.compression
import geotrellis.raster.io.geotiff.reader._
import geotrellis.raster.io.geotiff.tags._
import geotrellis.raster.io.geotiff.tags.codes.CompressionType._
import monocle.syntax._
import scala.collection.immutable.HashMap
import java.util.BitSet
import spire.syntax.cfor._
object LZWDecompressor {
def apply(segmentSizes: Array[Int]): LZWDecompressor =
new LZWDecompressor(segmentSizes)
}
class TokenTableEntry(val firstByte: Byte, val thisByte: Byte, val length: Int, val prev: TokenTableEntry = null) {
def concat(nextByte: Byte): TokenTableEntry =
new TokenTableEntry(firstByte, nextByte, length + 1, this)
}
object TokenTable {
val tableLimit = 4096
def initial(): Array[TokenTableEntry]= {
val arr = Array.ofDim[TokenTableEntry](tableLimit)
cfor(0)(_ < 256, _ + 1) { i =>
arr(i) = new TokenTableEntry(i.toByte, i.toByte, 1)
}
arr
}
def writeToOutput(entry: TokenTableEntry, outputArray: Array[Byte], outputArrayIndex: Int): Int = {
var curr = entry
while(curr != null) {
outputArray(outputArrayIndex + curr.length - 1) = curr.thisByte
curr = curr.prev
}
entry.length + outputArrayIndex
}
}
class LZWDecompressor(segmentSizes: Array[Int]) extends Decompressor {
def code = LZWCoded
val tableLimit = 4096
val ClearCode = 256
val EoICode = 257
def decompress(segment: Array[Byte], segmentIndex: Int): Array[Byte] = {
val bis = new LZWBitInputStream(segment)
var tokenTable = TokenTable.initial
var tokenTableIndex = 258
var outputArrayIndex = 0
val size = segmentSizes(segmentIndex)
val outputArray = Array.ofDim[Byte](size)
var threshold = 9
def initializeTokenTable = {
tokenTable = TokenTable.initial
tokenTableIndex = 258
threshold = 9
}
def addEntry(entry: TokenTableEntry): Unit = {
tokenTable(tokenTableIndex) = entry
tokenTableIndex += 1
if (tokenTableIndex == 511) threshold = 10
if (tokenTableIndex == 1023) threshold = 11
if (tokenTableIndex == 2047) threshold = 12
}
var code = 0
var oldCode = 0
var break = false
while (!break && { code = bis.get(threshold); code != EoICode } && outputArrayIndex < size) {
if (code == ClearCode) {
initializeTokenTable
code = bis.get(threshold)
if (code == EoICode) {
break = true
} else {
outputArrayIndex =
TokenTable.writeToOutput(tokenTable(code), outputArray, outputArrayIndex)
}
} else if (code < tokenTableIndex) {
// if the code is in the table
val entry = tokenTable(code)
outputArrayIndex =
TokenTable.writeToOutput(entry, outputArray, outputArrayIndex)
val oldEntry = tokenTable(oldCode)
addEntry(oldEntry.concat(entry.firstByte))
} else {
val oldEntry = tokenTable(oldCode)
val newEntry = oldEntry.concat(oldEntry.firstByte)
outputArrayIndex =
TokenTable.writeToOutput(newEntry, outputArray, outputArrayIndex)
addEntry(newEntry)
}
oldCode = code
}
outputArray
}
private class LZWBitInputStream(arr: Array[Byte]) {
val len = arr.length
private var index = 0
def get(next: Int): Int = {
if (next + index > len * 8)
EoICode
else {
val start = index / 8
val end = (index + next - 1) / 8
val ebi = (index + next - 1) % 8
val res =
if (end - start == 2) {
val c = ((arr(start) & 0xff) << 16) | ((arr(end - 1) & 0xff) << 8) | (arr(end) & 0xff)
(c >> (7 - ebi)) & (0xffffff >> (24 - next))
} else {
val c = ((arr(start) & 0xff) << 8) | (arr(end) & 0xff)
(c >> (7 - ebi)) & (0xffff >> (16 - next))
}
index += next
res
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy