Compare commits

...

10 commits

320 changed files with 69167 additions and 431 deletions

View file

@ -7,7 +7,7 @@ plugins {
wrapper {
gradleVersion = libs.versions.gradle.get()
distributionType = Wrapper.DistributionType.ALL
distributionType = Wrapper.DistributionType.BIN
}
ext {

View file

@ -1,5 +1,3 @@
group = org.xbib.graphics
name = graphics
version = 5.3.3
org.gradle.warning.mode = ALL
version = 5.5.1

View file

@ -1,4 +1,11 @@
repositories {
mavenLocal()
mavenCentral()
maven {
//url 'https://www.dcm4che.org/maven2/'
url 'https://maven.scijava.org/content/repositories/public/'
}
maven {
url 'https://raw.githubusercontent.com/nroduit/mvn-repo/master/'
}
}

Binary file not shown.

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

20
gradlew.bat vendored
View file

@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail

View file

@ -2,23 +2,20 @@ package org.xbib.graphics.ghostscript;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import org.xbib.graphics.ghostscript.internal.ErrorCodes;
import org.xbib.graphics.ghostscript.internal.GhostscriptLibrary;
import org.xbib.graphics.ghostscript.internal.GhostscriptLibraryLoader;
import org.xbib.graphics.ghostscript.internal.LoggingOutputStream;
import org.xbib.graphics.ghostscript.internal.NullOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -33,21 +30,12 @@ public class Ghostscript implements AutoCloseable {
private final GhostscriptLibrary libraryInstance;
private InputStream stdIn;
private OutputStream stdOut;
private OutputStream stdErr;
private final AtomicBoolean closed;
public Ghostscript() throws IOException {
prepareTmp();
GhostscriptLibraryLoader libraryLoader = new GhostscriptLibraryLoader();
this.libraryInstance = libraryLoader.getGhostscriptLibrary();
Objects.requireNonNull(this.libraryInstance);
this.stdOut = new LoggingOutputStream(logger);
this.stdErr = new LoggingOutputStream(logger);
this.libraryInstance = Objects.requireNonNull(libraryLoader.getGhostscriptLibrary(), "library instance must not be null");
this.closed = new AtomicBoolean(false);
}
@ -68,63 +56,8 @@ public class Ghostscript implements AutoCloseable {
return result;
}
/**
* Gets the error output stream of the Ghostscript interpreter (may be null
* if not set).
*
* @return The OutputStream or null
*/
public OutputStream getStdErr() {
return stdErr;
}
/**
* Sets the error output stream of the Ghostscript interpreter.
*
* @param outputStream OutputStream object
*/
public void setStdErr(OutputStream outputStream) {
stdErr = outputStream;
}
/**
* Gets the standard output stream of the Ghostscript interpreter.
*
* @return The OutputStream or null
*/
public OutputStream getStdOut() {
return stdOut;
}
/**
* Sets the standard output stream of the Ghostscript interpreter.
*
* @param outputStream OutputStream object
*/
public void setStdOut(OutputStream outputStream) {
stdOut = outputStream;
}
/**
* Gets the standard input stream of the Ghostscript interpreter.
*
* @return The InputStream or null
*/
public InputStream getStdIn() {
return stdIn;
}
/**
* Sets the standard input stream of the Ghostscript interpreter.
*
* @param inputStream InputStream object
*/
public void setStdIn(InputStream inputStream) {
stdIn = inputStream;
}
public synchronized void run(String[] args) throws IOException {
run(args, null, null);
public synchronized int run(List<String> args) throws IOException {
return run(args, null, null);
}
/**
@ -133,78 +66,56 @@ public class Ghostscript implements AutoCloseable {
* @param args Interpreter parameters. Use the same as Ghostscript command line arguments.
* @param runString run a string for the Ghostscript interpreter.
* @param fileName run a postscript file for the Ghostscript interpreter.
* @return the ghostscript return code;
* @throws IOException if initialize fails
*/
public synchronized void run(String[] args,
public synchronized int run(List<String> args,
String runString,
String fileName) throws IOException {
Objects.requireNonNull(args);
List<String> fullArgList = new LinkedList<>();
if (!args.contains("ps2pdf")) {
fullArgList.add("gs"); // gs or ps2pdf must be always present in the arg list
}
fullArgList.addAll(args);
GhostscriptLibrary.gs_main_instance.ByReference nativeInstanceByRef = null;
int ret = 0;
boolean quit = false;
try {
nativeInstanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
int result = libraryInstance.gsapi_new_instance(nativeInstanceByRef.getPointer(), null);
if (result != 0) {
ret = libraryInstance.gsapi_new_instance(nativeInstanceByRef.getPointer(), null);
if (ret != 0) {
nativeInstanceByRef = null;
throw new IOException("can not call Ghostscript gsapi_new_instance, error code " + result);
}
logger.log(Level.INFO, "ghostscript instance " + nativeInstanceByRef + " created");
GhostscriptLibrary.stdin_fn stdinCallback = null;
if (getStdIn() != null) {
stdinCallback = (caller_handle, buf, len) -> {
String encoding = System.getProperty(ENCODING_PARAMETER, "UTF-8");
try {
byte[] buffer = new byte[1024];
int read = getStdIn().read(buffer);
if (read != -1) {
buf.setString(0, new String(buffer, 0, read, encoding));
return read;
}
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
return 0;
};
}
GhostscriptLibrary.stdout_fn stdoutCallback;
if (getStdOut() == null) {
setStdOut(new NullOutputStream());
}
stdoutCallback = (caller_handle, str, len) -> {
try {
getStdOut().write(str.getBytes(), 0, len);
} catch (IOException ex) {
logger.log(Level.WARNING, ex.getMessage(), ex);
throw new IOException("can not call Ghostscript gsapi_new_instance, error code = " + ret);
}
logger.log(Level.FINE, "ghostscript instance " + nativeInstanceByRef + " created");
Pointer pointer = nativeInstanceByRef.getValue();
// always 0, we never use stdin
GhostscriptLibrary.stdin_fn stdinCallback = (caller_handle, buf, len) -> 0;
GhostscriptLibrary.stdout_fn stdoutCallback = (caller_handle, str, len) -> {
logger.log(Level.FINE, str.substring(0, len));
return len;
};
GhostscriptLibrary.stderr_fn stderrCallback;
if (getStdErr() == null) {
setStdErr(new NullOutputStream());
}
stderrCallback = (caller_handle, str, len) -> {
try {
getStdErr().write(str.getBytes(), 0, len);
} catch (IOException ex) {
logger.log(Level.WARNING, ex.getMessage(), ex);
}
GhostscriptLibrary.stderr_fn stderrCallback = (caller_handle, str, len) -> {
logger.log(Level.FINE, str.substring(0, len));
return len;
};
logger.log(Level.FINE, "setting gsapi_set_stdio");
result = libraryInstance.gsapi_set_stdio(nativeInstanceByRef.getValue(), stdinCallback,
stdoutCallback, stderrCallback);
if (result != 0) {
throw new IOException("can not set stdio on Ghostscript interpreter, error code " + result);
ret = libraryInstance.gsapi_set_stdio(pointer, stdinCallback, stdoutCallback, stderrCallback);
if (ret != 0) {
throw new IOException("can not set stdio on Ghostscript interpreter, error code " + ret);
}
logger.log(Level.FINE, "gsapi_init_with_args = " + (args != null ? Arrays.asList(args) : "null"));
result = libraryInstance.gsapi_init_with_args(nativeInstanceByRef.getValue(),
args != null ? args.length : 0, args);
logger.log(Level.FINE, "gsapi_init_with_args result = " + result);
if (result == ErrorCodes.gs_error_Quit) {
result = 0;
logger.log(Level.FINE, "gsapi_init_with_args = " + fullArgList);
ret = libraryInstance.gsapi_init_with_args(pointer, fullArgList.size(), fullArgList.toArray(new String[0]));
logger.log(Level.FINE, "gsapi_init_with_args return code = " + ret);
if (ret == ErrorCodes.gs_error_Quit) {
quit = true;
ret = 0;
}
if (result == 0) {
if (ret == 0) {
if (runString != null) {
IntByReference exitCode = new IntByReference();
libraryInstance.gsapi_run_string_begin(nativeInstanceByRef.getValue(), 0, exitCode);
ret = libraryInstance.gsapi_run_string_begin(pointer, 0, exitCode);
if (exitCode.getValue() != 0) {
throw new IOException("can not run command on Ghostscript interpreter. gsapi_run_string_begin failed with error code "
+ exitCode.getValue());
@ -212,14 +123,13 @@ public class Ghostscript implements AutoCloseable {
String[] slices = runString.split("\n");
for (String slice1 : slices) {
String slice = slice1 + "\n";
libraryInstance.gsapi_run_string_continue(nativeInstanceByRef.getValue(), slice, slice.length(),
0, exitCode);
libraryInstance.gsapi_run_string_continue(pointer, slice, slice.length(), 0, exitCode);
if (exitCode.getValue() != 0) {
throw new IOException("can not run command on Ghostscript interpreter. gsapi_run_string_continue failed with error code "
+ exitCode.getValue());
}
}
libraryInstance.gsapi_run_string_end(nativeInstanceByRef.getValue(), 0, exitCode);
ret = libraryInstance.gsapi_run_string_end(pointer, 0, exitCode);
if (exitCode.getValue() != 0) {
throw new IOException("can not run command on Ghostscript interpreter. gsapi_run_string_end failed with error code "
+ exitCode.getValue());
@ -228,53 +138,45 @@ public class Ghostscript implements AutoCloseable {
}
if (fileName != null) {
IntByReference exitCode = new IntByReference();
libraryInstance.gsapi_run_file(nativeInstanceByRef.getValue(), fileName, 0, exitCode);
ret = libraryInstance.gsapi_run_file(pointer, fileName, 0, exitCode);
if (exitCode.getValue() != 0) {
throw new IOException("can not run file on Ghostscript interpreter, error code " + exitCode.getValue());
throw new IOException("can not run file on Ghostscript interpreter, error code = " + exitCode.getValue());
}
logger.log(Level.FINE, "file completed: " + fileName);
}
return;
return ret;
}
if (result < 0) {
throw new IOException("can not initialize Ghostscript interpreter, error code " + result);
if (ret < 0) {
throw new IOException("can not initialize Ghostscript interpreter, error code = " + ret);
}
} finally {
if (nativeInstanceByRef != null) {
Pointer pointer = nativeInstanceByRef.getValue();
if (pointer != null) {
int result = libraryInstance.gsapi_exit(pointer);
if (result != 0) {
logger.log(Level.SEVERE, "can not call Ghostscript gsapi_exit, error code " + result);
if (!quit && ret >= 0) {
logger.log(Level.FINE, "exiting ghostscript instance " + libraryInstance);
ret = libraryInstance.gsapi_exit(pointer);
if (ret != 0) {
logger.log(Level.SEVERE, "can not call Ghostscript gsapi_exit, error code " + ret);
}
logger.log(Level.FINE, "deleting ghostscript instance " + pointer);
libraryInstance.gsapi_delete_instance(pointer);
}
} else {
logger.log(Level.WARNING, "no pointer to exit");
}
libraryInstance.gsapi_delete_instance(nativeInstanceByRef.getValue());
logger.log(Level.INFO, "ghostscript instance " + nativeInstanceByRef + " deleted");
}
}
return ret;
}
@Override
public synchronized void close() throws IOException {
if (closed.compareAndSet(false, true)) {
if (stdIn != null) {
stdIn.close();
stdIn = null;
}
if (stdOut != null) {
stdOut.close();
stdOut = null;
}
if (stdErr != null) {
stdErr.close();
stdErr = null;
}
if (libraryInstance != null) {
libraryInstance.dispose();
System.gc();
logger.log(Level.INFO, "ghostscript library instance disposed and gc'ed()");
logger.log(Level.FINE, "ghostscript library instance disposed and gc'ed()");
} else {
logger.log(Level.WARNING, "ghostscript library instance is null");
}

View file

@ -1,7 +1,6 @@
package org.xbib.graphics.ghostscript;
import java.security.SecureRandom;
import org.xbib.graphics.ghostscript.internal.LoggingOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -14,6 +13,7 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -75,9 +75,16 @@ public class PDFConverter {
*/
private final PaperSize paperSize;
private final List<String> customGsArgs;
public PDFConverter() {
this(OPTION_AUTOROTATEPAGES_OFF, OPTION_PROCESSCOLORMODEL_RGB,
OPTION_PDFSETTINGS_PRINTER, "1.4", false, PaperSize.A4);
OPTION_PDFSETTINGS_PRINTER, "1.4", false, PaperSize.A4, List.of());
}
public PDFConverter(List<String> customGsArgs) {
this(OPTION_AUTOROTATEPAGES_OFF, OPTION_PROCESSCOLORMODEL_RGB,
OPTION_PDFSETTINGS_PRINTER, "1.4", false, PaperSize.A4, customGsArgs);
}
public PDFConverter(int autoRotatePages,
@ -85,41 +92,45 @@ public class PDFConverter {
int pdfsettings,
String compatibilityLevel,
boolean pdfx,
PaperSize paperSize) {
PaperSize paperSize,
List<String> customGsArgs) {
this.autoRotatePages = autoRotatePages;
this.processColorModel = processColorModel;
this.pdfsettings = pdfsettings;
this.compatibilityLevel = compatibilityLevel;
this.pdfx = pdfx;
this.paperSize = paperSize;
this.customGsArgs = customGsArgs;
}
/**
* Run method called to perform the actual process of the converter.
*
* @param inputStream the input document
* @param outputStream output stream
* @param inputStream the input stream
* @param outputStream the output stream
* @return the Ghostscript return code
* @throws IOException if conversion fails
*/
public synchronized void convert(InputStream inputStream, OutputStream outputStream)
throws IOException {
if (outputStream == null) {
return;
}
public synchronized int convert(InputStream inputStream,
OutputStream outputStream) throws IOException {
Objects.requireNonNull(inputStream, "inputStream must not be null");
Objects.requireNonNull(outputStream, "outputStream must not be null");
int ret;
Path tmpPath = createTmpPath();
try (Ghostscript gs = new Ghostscript()) {
Path input = Files.createTempFile(tmpPath, "pdf", "pdf");
try (OutputStream sourceOutputStream = Files.newOutputStream(input)) {
inputStream.transferTo(sourceOutputStream);
}
Path output = Files.createTempFile(tmpPath, "pdf", "pdf");
List<String> gsArgs = new LinkedList<>();
gsArgs.add("-ps2pdf");
List<String> gsArgs = new LinkedList<>(customGsArgs);
gsArgs.add("-dNOSAFER");
gsArgs.add("-dNOPAUSE");
gsArgs.add("-dBATCH");
gsArgs.add("-dSAFER");
switch (autoRotatePages) {
case OPTION_AUTOROTATEPAGES_NONE -> gsArgs.add("-dAutoRotatePages=/None");
case OPTION_AUTOROTATEPAGES_ALL -> gsArgs.add("-dAutoRotatePages=/All");
case OPTION_AUTOROTATEPAGES_PAGEBYPAGE -> gsArgs.add("-dAutoRotatePages=/PageByPage");
default -> {
}
default -> gsArgs.add("-dAutoRotatePages=/None");
}
switch (processColorModel) {
case OPTION_PROCESSCOLORMODEL_CMYK -> gsArgs.add("-dProcessColorModel=/DeviceCMYK");
@ -134,21 +145,21 @@ public class PDFConverter {
default -> gsArgs.add("-dPDFSETTINGS=/default");
}
gsArgs.add("-dCompatibilityLevel=" + compatibilityLevel);
if (pdfx) {
gsArgs.add("-dPDFX=" + pdfx);
}
gsArgs.add("-dDEVICEWIDTHPOINTS=" + paperSize.getWidth());
gsArgs.add("-dDEVICEHEIGHTPOINTS=" + paperSize.getHeight());
gsArgs.add("-sDEVICE=pdfwrite");
gsArgs.add("-sOutputFile=" + output.toAbsolutePath().toString());
gsArgs.add("-sOutputFile=" + output.toAbsolutePath());
gsArgs.add("-f");
gsArgs.add("-");
gs.setStdIn(inputStream);
gs.setStdOut(new LoggingOutputStream(logger));
gs.setStdErr(new LoggingOutputStream(logger));
gs.run(gsArgs.toArray(new String[0]));
gsArgs.add(input.toString());
ret = gs.run(gsArgs);
Files.copy(output.toAbsolutePath(), outputStream);
} finally {
deleteTmpPath(tmpPath);
}
return ret;
}
private static void deleteTmpPath(Path path) throws IOException {

View file

@ -15,7 +15,6 @@ import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.xbib.graphics.ghostscript.internal.LoggingOutputStream;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
@ -72,8 +71,9 @@ public class PDFRasterizer {
this.subject = subject;
}
public synchronized void convert(Path source, Path target) throws IOException {
logger.info("convert source=" + source + " target=" + target);
public synchronized void convert(Path source,
Path target) throws IOException {
logger.log(Level.FINE, "convert source=" + source + " target=" + target);
if (!Files.exists(source)) {
throw new FileNotFoundException(source.toString());
}
@ -86,18 +86,19 @@ public class PDFRasterizer {
Path tmpPath = createTmpPath();
try {
Path path = Files.createTempDirectory(tmpPath, "pdf-rasterize");
pdfToImage(source, path, "pdf", null);
pdfToImage(source, path, "pdf");
Path tmpTarget = path.resolve(target.getFileName());
mergeImagesToPDF(path, tmpTarget);
scalePDF(tmpTarget, target);
logger.info("convert source=" + source + " done");
logger.log(Level.FINE, "convert source=" + source + " done");
} finally {
deleteTmpPath(tmpPath);
}
}
public synchronized void screenConvert(Path source, Path target) throws IOException {
logger.info("screen convert source=" + source.toAbsolutePath() + " target=" + target);
public synchronized void screenConvert(Path source,
Path target) throws IOException {
logger.log(Level.FINE, "screen convert source=" + source.toAbsolutePath() + " target=" + target);
if (!Files.exists(source.toAbsolutePath())) {
throw new FileNotFoundException(source.toString());
}
@ -115,14 +116,15 @@ public class PDFRasterizer {
mergeImagesToPDF(path, tmpTarget);
//toPDFA(tmpTarget, target);
scalePDF(tmpTarget, target);
logger.info("screen convert source=" + source.toAbsolutePath() + " done");
logger.log(Level.FINE, "screen convert source=" + source.toAbsolutePath() + " done");
} finally {
deleteTmpPath(tmpPath);
}
}
public synchronized void pdfToGrayScreenImage(Path source, Path target) throws IOException {
logger.info("pdfToGrayScreenImage source=" + source + " target=" + target);
public synchronized void pdfToGrayScreenImage(Path source,
Path target) throws IOException {
logger.log(Level.FINE, "pdfToGrayScreenImage source=" + source + " target=" + target);
if (!Files.exists(source.toAbsolutePath())) {
throw new FileNotFoundException("not found: " + source);
}
@ -144,21 +146,25 @@ public class PDFRasterizer {
gsArgs.add("-sOutputFile=" + target.resolve("screen-gray-pdf-%05d.png"));
gsArgs.add("-f");
gsArgs.add(source.toString());
logger.info("pdfToImage args=" + gsArgs);
logger.log(Level.FINE, "pdfToImage args=" + gsArgs);
try (Ghostscript gs = new Ghostscript()) {
gs.setStdIn(null);
gs.setStdOut(new LoggingOutputStream(logger));
gs.setStdErr(new LoggingOutputStream(logger));
gs.run(gsArgs.toArray(new String[0]));
logger.info("pdfToImage done");
gs.run(gsArgs);
logger.log(Level.FINE, "pdfToImage done");
}
}
public void pdfToImage(Path sourceFile,
Path targetDir,
String prefix) throws IOException {
pdfToImage(sourceFile, targetDir, prefix, null, null);
}
public synchronized void pdfToImage(Path sourceFile,
Path targetDir,
String prefix,
String pageRange) throws IOException {
logger.info("pdfToImage source=" + sourceFile + " target=" + targetDir + " started");
String firstPage,
String lastPage) throws IOException {
logger.log(Level.FINE, "pdfToImage source=" + sourceFile + " target=" + targetDir + " started");
List<String> gsArgs = new LinkedList<>();
gsArgs.add("-dNOPAUSE");
gsArgs.add("-dBATCH");
@ -167,9 +173,11 @@ public class PDFRasterizer {
gsArgs.add("-dINTERPOLATE");
// how do we know we have a crop box or not?
gsArgs.add("-dUseCropBox");
// page range, if not null
if (pageRange != null) {
gsArgs.add("-sPageList=" + pageRange);
if (firstPage != null) {
gsArgs.add("-sFirstPage=" + firstPage);
}
if (lastPage != null) {
gsArgs.add("-sLastPage=" + lastPage);
}
gsArgs.add("-sDEVICE=png16m");
gsArgs.add("-r300");
@ -181,23 +189,21 @@ public class PDFRasterizer {
gsArgs.add("100000000 setvmthreshold");
gsArgs.add("-f");
gsArgs.add(sourceFile.toString());
logger.info("pdfToImage args=" + gsArgs);
try (Ghostscript gs = new Ghostscript()) {
// reset stdin
gs.setStdIn(null);
gs.setStdOut(new LoggingOutputStream(logger));
gs.setStdErr(new LoggingOutputStream(logger));
gs.run(gsArgs.toArray(new String[0]));
logger.info("pdfToImage done");
gs.run(gsArgs);
logger.log(Level.FINE, "pdfToImage done");
}
}
public int mergeImagesToPDF(Path sourceDir, Path targetFile) throws IOException {
public int mergeImagesToPDF(Path sourceDir,
Path targetFile) throws IOException {
return mergeImagesToPDF(sourceDir, targetFile, "**/*");
}
public synchronized int mergeImagesToPDF(Path sourceDir, Path targetFile, String globPattern) throws IOException {
logger.info("mergeImagesToPDF: source=" + sourceDir + " target=" + targetFile + " glob =" + globPattern);
public synchronized int mergeImagesToPDF(Path sourceDir,
Path targetFile,
String globPattern) throws IOException {
logger.log(Level.FINE, "mergeImagesToPDF: source=" + sourceDir + " target=" + targetFile + " glob =" + globPattern);
AtomicInteger pagecount = new AtomicInteger();
PathMatcher pathMatcher = sourceDir.getFileSystem().getPathMatcher("glob:" + globPattern);
List<PDDocument> coverPageDocs = new ArrayList<>();
@ -222,7 +228,7 @@ public class PDFRasterizer {
}
for (Path path : entries) {
if (path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".pdf")) {
logger.info("found pdf " + path);
logger.log(Level.FINE, "found pdf " + path);
try (InputStream inputStream = Files.newInputStream(path)) {
PDDocument doc = Loader.loadPDF(inputStream.readAllBytes());
for (int i = 0; i < doc.getNumberOfPages(); i++) {
@ -266,7 +272,7 @@ public class PDFRasterizer {
}
}
pdDocument.save(outputStream);
logger.info("mergeImagesToPDF: done, " + pagecount.get() + " pages");
logger.log(Level.FINE, "pagesToPDF: done, " + pagecount.get() + " pages");
} finally {
for (PDDocument pd : coverPageDocs) {
pd.close();
@ -277,7 +283,7 @@ public class PDFRasterizer {
public synchronized void scalePDF(Path sourceFile,
Path targetFile) throws IOException {
logger.info("scalePDF: source = " + sourceFile + " target = " + targetFile + " starting");
logger.log(Level.FINE, "scalePDF: source = " + sourceFile + " target = " + targetFile + " starting");
List<String> gsArgs = new LinkedList<>();
gsArgs.add("-dNOPAUSE");
gsArgs.add("-dBATCH");
@ -292,10 +298,27 @@ public class PDFRasterizer {
gsArgs.add("-sOutputFile=" + targetFile.toString());
gsArgs.add(sourceFile.toString());
try (Ghostscript gs = new Ghostscript()) {
gs.setStdIn(null);
logger.info(gsArgs.toString());
gs.run(gsArgs.toArray(new String[0]));
logger.info("scalePDF: source = " + sourceFile + " target = " + targetFile + " done");
gs.run(gsArgs);
logger.log(Level.FINE, "scalePDF: source = " + sourceFile + " target = " + targetFile + " done");
}
}
public synchronized void downgradePDF(Path sourceFile,
Path targetFile) throws IOException {
logger.log(Level.FINE, "downgradePDF: source = " + sourceFile + " target = " + targetFile + " starting");
List<String> gsArgs = new LinkedList<>();
gsArgs.add("-dNOPAUSE");
gsArgs.add("-dBATCH");
gsArgs.add("-dQUIET");
gsArgs.add("-dPDFSETTINGS=/printer");
gsArgs.add("-dFIXEDMEDIA");
gsArgs.add("-dCompatibilityLevel=1.4");
gsArgs.add("-sDEVICE=pdfwrite");
gsArgs.add("-sOutputFile=" + targetFile.toString());
gsArgs.add(sourceFile.toString());
try (Ghostscript gs = new Ghostscript()) {
gs.run(gsArgs);
logger.log(Level.FINE, "downgradePDF: source = " + sourceFile + " target = " + targetFile + " done");
}
}
@ -333,14 +356,16 @@ public class PDFRasterizer {
gsArgs.add("-sOutputFile=" + target.toString());
gsArgs.add(pdfapsPath.toAbsolutePath().toString());
gsArgs.add(source.toString());
gs.setStdIn(null);
gs.run(gsArgs.toArray(new String[0]));
gs.run(gsArgs);
} finally {
deleteTmpPath(tmpPath);
}
}
private static void copyAndReplace(Path source, Path target, String from, String to) throws IOException {
private static void copyAndReplace(Path source,
Path target,
String from,
String to) throws IOException {
try (BufferedReader br = Files.newBufferedReader(source); BufferedWriter bw = Files.newBufferedWriter(target)) {
String line;
while ((line = br.readLine()) != null) {

View file

@ -1,57 +0,0 @@
package org.xbib.graphics.ghostscript;
public class PageRaster {
private int width;
private int height;
private int raster;
private int format;
private byte[] data;
public PageRaster() {
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getRaster() {
return raster;
}
public void setRaster(int raster) {
this.raster = raster;
}
public int getFormat() {
return format;
}
public void setFormat(int format) {
this.format = format;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
}

View file

@ -21,23 +21,20 @@ public class GhostscriptLibraryLoader {
};
private static final String[] MAC_LIBNAMES = {
"libgs.9.54.dylib",
"libgs.9.54",
"gs.9.54",
"libgs.9.25.dylib",
"libgs.9.25",
"gs.9.25",
"libgs.dylib",
"gs.9.26",
"gs",
};
private GhostscriptLibrary ghostscriptLibrary;
@SuppressWarnings("deprecation")
public GhostscriptLibraryLoader() {
Map<String, Object> options = Map.of(Library.OPTION_CALLING_CONVENTION, Function.C_CONVENTION,
Library.OPTION_INVOCATION_MAPPER, (InvocationMapper) (lib, m) -> {
if (m.getName().equals("dispose")) {
return (proxy, method, args) -> {
// we use the deprecated dispose() call here by intention
lib.dispose();
return null;
};
@ -51,7 +48,7 @@ public class GhostscriptLibraryLoader {
for (String libname : libnames) {
try {
this.ghostscriptLibrary = Native.load(libname, GhostscriptLibrary.class, options);
} catch (Error e) {
} catch (Exception | Error e) {
logger.log(Level.WARNING, "library " + libname + " not found", e);
}
}

View file

@ -1,26 +0,0 @@
package org.xbib.graphics.ghostscript.internal;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingOutputStream extends ByteArrayOutputStream {
private final Logger logger;
public LoggingOutputStream(Logger logger) {
super();
this.logger = logger;
}
@Override
public void flush() throws IOException {
super.flush();
String s = new String(buf, 0, count);
if (s.length() > 0) {
logger.log(Level.FINE, s);
}
reset();
}
}

View file

@ -1,17 +0,0 @@
package org.xbib.graphics.ghostscript.internal;
import java.io.OutputStream;
/**
*
*/
public class NullOutputStream extends OutputStream {
@Override
public void write(int b) {
}
@Override
public void write(byte[] b, int off, int len) {
}
}

View file

@ -1,23 +1,24 @@
package org.xbib.graphics.ghostscript.test;
import java.io.IOException;
import org.junit.jupiter.api.Disabled;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.ghostscript.Ghostscript;
import org.xbib.graphics.ghostscript.GhostscriptRevision;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
public class GhostscriptTest {
public class BasicGhostscriptTest {
private static final Logger logger = Logger.getLogger(GhostscriptTest.class.getName());
private static final Logger logger = Logger.getLogger(BasicGhostscriptTest.class.getName());
private static final String dir = "src/test/resources/org/xbib/graphics/ghostscript/test/";
@ -39,7 +40,7 @@ public class GhostscriptTest {
@Test
public void testExit() {
try (Ghostscript gs = new Ghostscript()) {
String[] args = {"-dNODISPLAY", "-dQUIET"};
List<String> args = List.of("-dNODISPLAY", "-dQUIET");
gs.run(args);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
@ -50,7 +51,7 @@ public class GhostscriptTest {
@Test
public void testRunString() {
try (Ghostscript gs = new Ghostscript()) {
String[] args = {"-dNODISPLAY", "-dQUIET"};
List<String> args = List.of("-dNODISPLAY", "-dQUIET");
gs.run(args, "devicenames ==", null);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
@ -63,7 +64,7 @@ public class GhostscriptTest {
@Test
public void testRunFile() {
try (Ghostscript gs = new Ghostscript()) {
String[] args = {"-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER"};
List<String> args = List.of("-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER");
gs.run(args, null, dir + "input.ps");
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
@ -73,45 +74,38 @@ public class GhostscriptTest {
}
}
// This test throws core dump.
// [libgs.so.9.25+0x32dc11] clump_splay_walk_fwd+0x31
// [libgs.so.9.56+0x32bcf4] clump_splay_walk_fwd+0x34
@Disabled("core dump")
@Test
public void testStdIn() {
try (Ghostscript gs = new Ghostscript();
InputStream is = new FileInputStream(dir + "input.ps")) {
gs.setStdIn(is);
String[] args = {"-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-sOutputFile=%stdout", "-f", "-"};
gs.run(args);
public void testStdOut() throws Exception {
Path path = Paths.get("build/devicenames");
try (OutputStream outputStream = Files.newOutputStream(path)) {
outputStream.write("devicenames ==\n".getBytes());
}
try (Ghostscript gs = new Ghostscript()) {
List<String> args = List.of("-dNODISPLAY", "-sOutputFile=%stdout", "-f", path.toString());
int ret = gs.run(args);
} catch (Exception e) {
fail(e.getMessage());
} finally {
Files.delete(path);
}
}
@Test
public void testStdOut() {
try (Ghostscript gs = new Ghostscript();
InputStream is = new ByteArrayInputStream("devicenames ==\n".getBytes())) {
gs.setStdIn(is);
String[] args = { "-dNODISPLAY", "-sOutputFile=%stdout", "-f", "-" };
gs.run(args);
public void testStdErr() throws Exception {
Path path = Paths.get("build/broken");
try (OutputStream outputStream = Files.newOutputStream(path)) {
outputStream.write("foobar\n".getBytes());
}
try (Ghostscript gs = new Ghostscript()) {
List<String> args = List.of("-dNODISPLAY", "-sOutputFile=%stdout", "-f", path.toString());
int ret = gs.run(args);
} catch (Exception e) {
// expect error
if (!e.getMessage().contains("error code = -100")) {
fail(e.getMessage());
}
}
@Test
public void testStdErr() {
try (Ghostscript gs = new Ghostscript();
InputStream is = new ByteArrayInputStream("stupid\n".getBytes())) {
gs.setStdIn(is);
String[] args = { "-dNODISPLAY", "-sOutputFile=%stdout", "-f", "-" };
gs.run(args);
} catch (Exception e) {
if (!e.getMessage().contains("error code -100")) {
fail(e.getMessage());
}
} finally {
Files.delete(path);
}
}
}

View file

@ -1,11 +1,15 @@
package org.xbib.graphics.ghostscript.test;
import com.sun.jna.ptr.IntByReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.graphics.ghostscript.internal.GhostscriptLibrary;
import org.xbib.graphics.ghostscript.internal.GhostscriptLibraryLoader;
public class GhostScriptLibraryTester {
private static final Logger logger = Logger.getLogger(GhostScriptLibraryTester.class.getName());
private final GhostscriptLibrary ghostscriptLibrary;
public GhostScriptLibraryTester() {
@ -33,7 +37,7 @@ public class GhostScriptLibraryTester {
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
String[] args = {
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
};
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
IntByReference exitCode = new IntByReference();
@ -47,10 +51,8 @@ public class GhostScriptLibraryTester {
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
String[] args = {
"ps2pdf",
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-sDEVICE=pdfwrite",
"-sOutputFile=" + output,
"-c", ".setpdfwrite", "-f", input
"ps2pdf", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-sDEVICE=pdfwrite",
"-sOutputFile=" + output, "-f", input
};
int result = ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
ghostscriptLibrary.gsapi_exit(instanceByRef.getValue());
@ -62,7 +64,7 @@ public class GhostScriptLibraryTester {
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
String[] args = {
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
};
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
IntByReference exitCode = new IntByReference();
@ -77,7 +79,7 @@ public class GhostScriptLibraryTester {
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
String[] args = {
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
};
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
IntByReference exitCode = new IntByReference();
@ -94,7 +96,7 @@ public class GhostScriptLibraryTester {
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
String[] args = {
"-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
"gs", "-dNODISPLAY", "-dNOPAUSE", "-dBATCH", "-dSAFER"
};
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);
IntByReference exitCode = new IntByReference();
@ -108,9 +110,7 @@ public class GhostScriptLibraryTester {
GhostscriptLibrary.gs_main_instance.ByReference instanceByRef = new GhostscriptLibrary.gs_main_instance.ByReference();
ghostscriptLibrary.gsapi_new_instance(instanceByRef.getPointer(), null);
final StringBuilder stdOutBuffer = new StringBuilder();
final StringBuilder stdInBuffer = new StringBuilder();
GhostscriptLibrary.stdin_fn stdinCallback = (caller_handle, buf, len) -> {
stdInBuffer.append("OK");
return 0;
};
GhostscriptLibrary.stdout_fn stdoutCallback = (caller_handle, str, len) -> {
@ -120,8 +120,7 @@ public class GhostScriptLibraryTester {
GhostscriptLibrary.stderr_fn stderrCallback = (caller_handle, str, len) -> len;
ghostscriptLibrary.gsapi_set_stdio(instanceByRef.getValue(), stdinCallback, stdoutCallback, stderrCallback);
String[] args = {
"-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH",
"-sOutputFile=%stdout", "-f", "-"
"gs", "-dNODISPLAY", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-sOutputFile=%stdout", "-f", "-"
};
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(),
args.length, args);
@ -130,6 +129,8 @@ public class GhostScriptLibraryTester {
ghostscriptLibrary.gsapi_run_string_with_length(instanceByRef.getValue(), command, command.length(), 0, exitCode);
ghostscriptLibrary.gsapi_exit(instanceByRef.getValue());
ghostscriptLibrary.gsapi_delete_instance(instanceByRef.getValue());
logger.log(Level.FINE, stdOutBuffer.toString());
}
public String setDisplayCallback() {
@ -176,7 +177,7 @@ public class GhostScriptLibraryTester {
displayCallback.size = displayCallback.size();
ghostscriptLibrary.gsapi_set_display_callback(instanceByRef.getValue(), displayCallback);
String[] args = new String[]{
"-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER",
"gs", "-dQUIET", "-dNOPAUSE", "-dBATCH", "-dSAFER",
"-sDEVICE=display", "-sDisplayHandle=0", "-dDisplayFormat=16#a0800"
};
ghostscriptLibrary.gsapi_init_with_args(instanceByRef.getValue(), args.length, args);

View file

@ -1,10 +1,10 @@
package org.xbib.graphics.ghostscript.test;
import java.util.Set;
import java.util.logging.Level;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -19,9 +19,10 @@ public class GhostscriptLibraryTest {
private static GhostScriptLibraryTester gslib;
@BeforeAll
public static void setUp() throws Exception {
public static void setUp() {
logger.log(Level.INFO, "setUp: loading ghostscript library");
gslib = new GhostScriptLibraryTester();
logger.info("setUp: ghostscript library loaded");
logger.log(Level.INFO, "setUp: ghostscript library loaded");
}
@Test
@ -35,13 +36,10 @@ public class GhostscriptLibraryTest {
}
@Test
@Disabled("GPL Ghostscript 9.54.0: Unrecoverable error, exit code 1")
public void gsapiInitWithArgs() {
String input = dir + "input.ps";
String output = "build/output.pdf";
gslib.withInput(input, output);
File outputFile = new File(output);
assertTrue(outputFile.exists());
}
@Test
@ -70,9 +68,10 @@ public class GhostscriptLibraryTest {
}
@Test
@Disabled
public void gsapiSetDisplayCallback() {
String display = gslib.setDisplayCallback();
assertEquals("OPEN-PRESIZE-UPDATE-SIZE-PAGE-UPDATE-SYNC-PRECLOSE-CLOSE", display);
assertTrue(Set.of("OPEN-PRESIZE-UPDATE-SIZE-PRECLOSE-CLOSEOPEN-PRESIZE-UPDATE-SIZE-UPDATE-PAGE-PRECLOSE-CLOSE", // gs 9.54
"OPEN-PRESIZE-UPDATE-SIZE-UPDATE-PRECLOSE-CLOSEOPEN-PRESIZE-UPDATE-SIZE-PAGE-UPDATE-PRECLOSE-CLOSE") // gs 10
.contains(display));
}
}

View file

@ -1,23 +1,32 @@
package org.xbib.graphics.ghostscript.test;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.ghostscript.PDFConverter;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class PDFConverterTest {
private static final Logger logger = Logger.getLogger(PDFConverterTest.class.getName());
@Test
public void testConvertWithPS() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PDFConverter converter = new PDFConverter();
converter.convert(getClass().getClassLoader().getResourceAsStream("input.ps"), baos);
assertTrue(baos.size() > 0);
baos.close();
assertTrue(baos.toString(StandardCharsets.UTF_8).startsWith("%PDF-1.4"));
PDFConverter converter = new PDFConverter(List.of("ps2pdf"));
try (InputStream inputStream = getClass().getResourceAsStream("input.ps");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
converter.convert(inputStream, outputStream);
assertTrue(outputStream.size() > 0);
assertTrue(outputStream.toString(StandardCharsets.UTF_8).startsWith("%PDF-1.4"));
}
}
@Test
@ -25,13 +34,13 @@ public class PDFConverterTest {
final ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
final ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
final ByteArrayOutputStream baos3 = new ByteArrayOutputStream();
final PDFConverter converter = new PDFConverter();
final PDFConverter converter = new PDFConverter(List.of("ps2pdf"));
Thread thread1 = new Thread() {
public void run() {
try {
converter.convert(this.getClass().getClassLoader().getResourceAsStream("input.ps"), baos1);
converter.convert(this.getClass().getResourceAsStream("input.ps"), baos1);
} catch (Exception e) {
e.printStackTrace();
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
};
@ -39,9 +48,9 @@ public class PDFConverterTest {
Thread thread2 = new Thread() {
public void run() {
try {
converter.convert(this.getClass().getClassLoader().getResourceAsStream("input.ps"), baos2);
converter.convert(this.getClass().getResourceAsStream("input.ps"), baos2);
} catch (Exception e) {
e.printStackTrace();
logger.log(Level.SEVERE, e.getMessage(), e);
}
};
};
@ -49,9 +58,9 @@ public class PDFConverterTest {
Thread thread3 = new Thread() {
public void run() {
try {
converter.convert(this.getClass().getClassLoader().getResourceAsStream("input.ps"), baos3);
converter.convert(this.getClass().getResourceAsStream("input.ps"), baos3);
} catch (Exception e) {
e.printStackTrace();
logger.log(Level.SEVERE, e.getMessage(), e);
}
};
};
@ -69,9 +78,19 @@ public class PDFConverterTest {
@Test
public void testConvertWithUnsupportedDocument() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PDFConverter converter = new PDFConverter();
converter.convert(this.getClass().getClassLoader().getResourceAsStream("input.pdf"), baos);
baos.close();
try (InputStream inputStream = getClass().getResourceAsStream("input.pdf");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
converter.convert(inputStream, outputStream);
}
}
@Test
public void testConvertDistiller() throws Exception {
PDFConverter converter = new PDFConverter(List.of("-dFIXEDMEDIA", "-dPDFFitPage"));
try (InputStream inputStream = getClass().getResourceAsStream("3977940_retrieve_74_.pdf");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
converter.convert(inputStream, outputStream);
}
}
}

View file

@ -28,7 +28,7 @@ public class PDFRasterizerTest {
Path targetFile = Paths.get("build/color.pdf");
PDFRasterizer pdfRasterizer = new PDFRasterizer();
int pagecount = pdfRasterizer.mergeImagesToPDF(sourceDir, targetFile);
logger.info("pagecount = " + pagecount);
logger.log(Level.FINE, "pagecount = " + pagecount);
assertEquals(1, pagecount);
}
@ -38,7 +38,7 @@ public class PDFRasterizerTest {
Path targetFile = Paths.get("build/3656573.pdf");
PDFRasterizer pdfRasterizer = new PDFRasterizer();
int pagecount = pdfRasterizer.mergeImagesToPDF(sourceDir, targetFile);
logger.info("pagecount = " + pagecount);
logger.log(Level.FINE, "pagecount = " + pagecount);
assertEquals(9, pagecount);
}
@ -50,10 +50,10 @@ public class PDFRasterizerTest {
int pagecount = 0;
try {
PDFRasterizer pdfRasterizer = new PDFRasterizer();
pdfRasterizer.pdfToImage(source, path, null, null);
pdfRasterizer.pdfToImage(source, path, "test");
Path tmpTarget = path.resolve(target.getFileName());
pagecount = pdfRasterizer.mergeImagesToPDF(path, tmpTarget);
logger.info("pagecount = " + pagecount);
logger.log(Level.FINE, "pagecount = " + pagecount);
pdfRasterizer.scalePDF(tmpTarget, target);
} finally {
delete(path);
@ -61,6 +61,14 @@ public class PDFRasterizerTest {
assertEquals(28, pagecount);
}
@Test
public void testDowngrade() throws IOException {
Path source = Paths.get("src/test/resources/org/xbib/graphics/ghostscript/test/3977940_retrieve_74_.pdf");
Path target = Paths.get("build/3977940-new.pdf");
PDFRasterizer pdfRasterizer = new PDFRasterizer();
pdfRasterizer.downgradePDF(source, target);
}
@Test
public void testPDFRasterizerToImage() throws Exception {
Path path = Paths.get("build/resources/test");
@ -72,7 +80,7 @@ public class PDFRasterizerTest {
delete(target);
Files.createDirectories(target);
PDFRasterizer rasterizer = new PDFRasterizer();
rasterizer.pdfToImage(p, target, "pdf-", "1-");
rasterizer.pdfToImage(p, target, "pdf-");
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
fail(e);
@ -88,7 +96,7 @@ public class PDFRasterizerTest {
try (Stream<Path> stream = Files.list(path)) {
stream.forEach(p -> {
if (p.toString().endsWith(".pdf")) {
logger.info("found " + p.toString());
logger.log(Level.FINE, "found " + p);
Path target = Paths.get("build/" + p.getFileName());
try {
delete(target);

View file

@ -0,0 +1,3 @@
dependencies {
testImplementation testLibs.assertj
}

View file

@ -0,0 +1,11 @@
import org.xbib.graphics.jpeg2000.imageio.JPEG2000ReaderSpi;
import javax.imageio.spi.ImageReaderSpi;
module org.xbib.graphics.jpegtwothousand {
requires java.desktop;
exports org.xbib.graphics.jpeg2000;
exports org.xbib.graphics.jpeg2000.imageio;
uses ImageReaderSpi;
provides ImageReaderSpi with JPEG2000ReaderSpi;
}

View file

@ -0,0 +1,84 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is defined to represent a Bits Per Component Box of JPEG
* JP2 file format. A Bits Per Component box has a length, and a fixed
* type of "bpcc". Its content is a byte array containing the bit
* depths of the color components.
* <p>
* This box is necessary only when the bit depth are not identical for all
* the components.
*
* @author http://bfo.com
*/
public class BitsPerComponentBox extends Box {
private byte[] data;
public BitsPerComponentBox() {
super(fromString("bpcc"));
}
public BitsPerComponentBox(byte[] bitDepth) {
this();
this.data = bitDepth;
}
@Override
public int getLength() {
return data.length;
}
@Override
public void read(RandomAccessIO in) throws IOException {
data = new byte[in.length()];
in.readFully(data, 0, data.length);
}
@Override
public void write(DataOutputStream out) throws IOException {
out.write(data);
}
/**
* Returns the bit depths for all the image components.
*/
public byte[] getBitDepth() {
return data;
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
for (int i = 0; i < data.length; i++) {
out.writeStartElement("bpc");
out.writeCharacters(Integer.toString(data[i] & 0xFF));
out.writeEndElement();
}
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"bpc\":[");
for (int i = 0; i < data.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(data[i]);
}
sb.append("]}");
return sb.toString();
}
}

View file

@ -0,0 +1,161 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
/**
* This class is defined to create the box of JP2 file format. A box has
* a length, a type, an optional extra length and its content. The subclasses
* should explain the content information.
*
* @author http://bfo.com
*/
public class Box {
private static final Map<Integer, Class<? extends Box>> boxClasses = new HashMap<Integer, Class<? extends Box>>();
static {
//children for the root
boxClasses.put(Integer.valueOf(fromString("ftyp")), FileTypeBox.class);
boxClasses.put(Integer.valueOf(fromString("jp2h")), HeaderBox.class);
// boxClasses.put(Integer.valueOf(0x69686472), LabelBox.class); // L.9.13
boxClasses.put(Integer.valueOf(fromString("ihdr")), ImageHeaderBox.class);
boxClasses.put(Integer.valueOf(fromString("bpcc")), BitsPerComponentBox.class);
boxClasses.put(Integer.valueOf(fromString("colr")), ColorSpecificationBox.class);
boxClasses.put(Integer.valueOf(fromString("pclr")), PaletteBox.class);
boxClasses.put(Integer.valueOf(fromString("cmap")), ComponentMappingBox.class);
boxClasses.put(Integer.valueOf(fromString("cdef")), ChannelDefinitionBox.class);
boxClasses.put(Integer.valueOf(fromString("res ")), ResolutionSuperBox.class);
boxClasses.put(Integer.valueOf(fromString("resc")), RescBox.class);
boxClasses.put(Integer.valueOf(fromString("resd")), ResdBox.class);
boxClasses.put(Integer.valueOf(fromString("jp2c")), CodeStreamBox.class);
// boxClasses.put(Integer.valueOf(0x6A703269), IntellectualPropertyBox.class);
boxClasses.put(Integer.valueOf(fromString("xml ")), XMLBox.class); // L.9.18
boxClasses.put(Integer.valueOf(fromString("uuid")), UUIDBox.class);
boxClasses.put(Integer.valueOf(fromString("uinf")), UUIDInfoBox.class);
boxClasses.put(Integer.valueOf(fromString("ulst")), UUIDListBox.class);
boxClasses.put(Integer.valueOf(fromString("url ")), URLBox.class);
// Children of JPEG2000UUIDInfoBox
}
private final int type;
private byte[] raw;
public Box(int type) {
this.type = type;
}
public static int fromString(String s) {
if (s.length() != 4) {
throw new IllegalArgumentException();
}
return (s.charAt(0) << 24) | (s.charAt(1) << 16) | (s.charAt(2) << 8) | s.charAt(3);
}
public static String toString(byte[] v) {
String s = new BigInteger(1, v).toString(16);
if ((s.length() & 1) == 1) {
s = "0" + s;
}
return s;
}
public static String toString(int v) {
StringBuilder sb = new StringBuilder(4);
for (int i = 24; i >= 0; i -= 8) {
int c = (v >> i) & 0xFF;
if (c >= 0x20 && c <= 0x7f) {
sb.append((char) c);
} else {
String s = "0000000" + Integer.toHexString(v);
return "0x" + s.substring(s.length() - 8);
}
}
return sb.toString();
}
public static Box createBox(int type) {
Class<? extends Box> cl = boxClasses.get(type);
if (cl == null) {
return new Box(type);
} else {
try {
return cl.newInstance();
} catch (InstantiationException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
}
// Shouldn't happen
throw new Error("Failed during box creation", e);
} catch (Exception e) {
// Shouldn't happen
throw new Error("Failed during box creation", e);
}
}
}
/**
* Read the content from the specified IO, which should be truncated
* to the correct length. The current position of the specified "in"
* is at the start of the "box contents" (DBox field)
*/
public void read(RandomAccessIO in) throws IOException {
raw = new byte[in.length()];
in.readFully(raw, 0, raw.length);
}
/**
* Write the content of this box to the OutputStream. The content
* itself is written, not the length
*/
public void write(DataOutputStream out) throws IOException {
out.write(raw);
}
public int getType() {
return type;
}
public int getLength() {
return raw == null ? 0 : raw.length;
}
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
if (raw != null) {
out.writeCharacters(toString(raw));
}
out.writeEndElement();
}
public String toString() {
String sb = "{\"type\":\"" +
toString(type) +
"\",\"length\":" +
getLength() +
"}";
return sb;
}
public static class RescBox extends ResolutionBox {
RescBox() {
super(fromString("resc"));
}
}
public static class ResdBox extends ResolutionBox {
ResdBox() {
super(fromString("resd"));
}
}
}

View file

@ -0,0 +1,172 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class is designed to represent a Channel Definition Box of
* JPEG JP2 file format. A Channel Definition Box has a length, and
* a fixed type of "cdef". Its content defines the type of the image
* channels: color channel, alpha channel or premultiplied alpha channel.
* <p>
* CORRECTION - it is actually "ComponentDefinitionBox" in the spec.
*
* @author http://bfo.com
*/
public class ChannelDefinitionBox extends Box {
/**
* The cached data elements.
*/
private short num;
private short[] channels;
private short[] types;
private short[] associations;
public ChannelDefinitionBox() {
super(fromString("cdef"));
}
/** Constructs a <code>ChannelDefinitionBox</code> based on the provided <code>ColorModel</code>.
*
* This was from JAI but is untested by BFO
*
public ChannelDefinitionBox(ColorModel colorModel) {
this();
// creates the buffers for the channel definitions.
short length = (short)(colorModel.getComponentSize().length - 1);
num = (short)(length * (colorModel.isAlphaPremultiplied() ? 3 : 2));
channels = new short[num];
types = new short[num];
associations = new short[num];
// fills the arrays.
fillBasedOnBands(length, colorModel.isAlphaPremultiplied(), channels, types, associations);
}
private static void fillBasedOnBands(int numComps, boolean isPremultiplied, short[] c, short[] t, short[] a) {
int num = numComps * (isPremultiplied ? 3 : 2);
if (isPremultiplied) {
for (int i = numComps * 2; i < num; i++) {
c[i] = (short)(i - numComps * 2);
t[i] = 2; // 2 -- premultiplied
a[i] = (short)(i + 1 - numComps * 2);
}
}
for (int i = 0; i < numComps; i++) {
int j = i + numComps;
c[i] = (short)i;
t[i] = 0; // The original channel
a[j] = a[i] = (short)(i + 1);
c[j] = (short)numComps;
t[j] = 1; // 1 -- transparency
}
}
*/
/**
* Constructs a <code>ChannelDefinitionBox</code> based on the provided
* channel definitions.
*/
public ChannelDefinitionBox(short[] channel, short[] types, short[] associations) {
this();
this.num = (short) channel.length;
this.channels = channel;
this.types = types;
this.associations = associations;
}
@Override
public void read(RandomAccessIO in) throws IOException {
// Note all of these are actually unsigned shorts, so any images with > 16383 channels will fail...
num = in.readShort();
channels = new short[num];
types = new short[num];
associations = new short[num];
for (int i = 0; i < num; i++) {
channels[i] = in.readShort();
types[i] = in.readShort();
associations[i] = in.readShort();
}
}
@Override
public int getLength() {
return 2 + num * 6;
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeShort(num);
for (int i = 0; i < num; i++) {
out.writeShort(channels[i]);
out.writeShort(types[i]);
out.writeShort(associations[i]);
}
}
/**
* Returns the defined channels.
*/
public short[] getChannel() {
return channels;
}
/**
* Returns the channel types. Values are 0 (color), 1 (opacity), 2 (premultiplied opacity) or -1 (unspecified)
*/
public short[] getTypes() {
return types;
}
/**
* Returns the association which associates a color channel to a color
* component in the color space of the image.
*/
public short[] getAssociation() {
return associations;
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
for (int i = 0; i < channels.length; i++) {
out.writeEmptyElement("cmap");
out.writeAttribute("channel", Integer.toString(channels[i]));
out.writeAttribute("type", Integer.toString(types[i]));
out.writeAttribute("assoc", Integer.toString(associations[i]));
}
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"channels\":[");
for (int i = 0; i < channels.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append("{\"channel\":");
sb.append(channels[i]);
sb.append(",\"type\":");
sb.append(types[i]);
sb.append(",\"association\":");
sb.append(associations[i]);
sb.append("}");
}
sb.append("]}");
return sb.toString();
}
}

View file

@ -0,0 +1,101 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.codestream.HeaderInfo;
import org.xbib.graphics.jpeg2000.j2k.codestream.reader.HeaderDecoder;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This represents the "jp2c" box, which contains the CodeStream
*
* @author http://bfo.com
*/
public class CodeStreamBox extends Box {
private byte[] data;
private int length;
private RandomAccessIO io;
public CodeStreamBox() {
super(fromString("jp2c"));
}
public CodeStreamBox(byte[] data) {
this();
this.data = data;
this.length = data.length;
}
@Override
public int getLength() {
return length;
}
@Override
public void read(RandomAccessIO io) throws IOException {
this.io = io;
this.length = io.length();
}
@Override
public void write(DataOutputStream out) throws IOException {
if (data != null) {
out.write(data);
} else if (io != null) {
byte[] b = new byte[8192];
io.seek(0);
int remaining = io.length();
while (remaining > 0) {
int c = Math.min(b.length, remaining);
io.readFully(b, 0, c);
out.write(b, 0, c);
remaining -= c;
}
}
}
/**
* Return a RandomAccessIO which contains the codestream to pass to the BitstreamReader
*/
public RandomAccessIO getRandomAccessIO() throws IOException {
if (io != null) {
io.seek(0);
return io;
}
throw new IllegalStateException("Not created from a RandomAccessIO");
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
J2KReadParam param = new SimpleJ2KReadParam();
HeaderInfo hi = new HeaderInfo();
try {
HeaderDecoder hd = new HeaderDecoder(getRandomAccessIO(), param, hi);
out.writeStartElement("SIZ");
out.writeAttribute("iw", Integer.toString(hi.siz.xsiz - hi.siz.x0siz));
out.writeAttribute("ih", Integer.toString(hi.siz.ysiz - hi.siz.y0siz));
out.writeAttribute("tw", Integer.toString(hi.siz.xtsiz));
out.writeAttribute("th", Integer.toString(hi.siz.ytsiz));
out.writeAttribute("numc", Integer.toString(hi.siz.csiz));
for (int i = 0; i < hi.siz.csiz; i++) {
out.writeStartElement("component");
out.writeAttribute("depth", Integer.toString(hi.siz.getOrigBitDepth(i)));
out.writeAttribute("signed", Boolean.toString(hi.siz.isOrigSigned(i)));
out.writeAttribute("subx", Integer.toString(hi.siz.xrsiz[i]));
out.writeAttribute("suby", Integer.toString(hi.siz.yrsiz[i]));
out.writeEndElement();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
out.writeEndElement();
}
}

View file

@ -0,0 +1,202 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This represents the "colr" box.
* Its content contains the method to define the color space,
* the precedence and approximation accuracy (0 for JP2 files), the
* enumerated color space, and the ICC color profile if any.
*
* @author http://bfo.com
*/
public class ColorSpecificationBox extends Box {
/**
* The enumerated color space defined in JP2 file format.
*/
public static final int ECS_sRGB = 16;
public static final int ECS_GRAY = 17;
public static final int ECS_YCC = 18;
private byte method;
private byte precedence;
private byte approximation;
private int ecs;
private volatile byte[] profileData;
private volatile ICC_Profile profile;
public ColorSpecificationBox() {
super(fromString("colr"));
method = 1;
approximation = 1;
}
public ColorSpecificationBox(ColorSpace cs) {
this();
if (cs.isCS_sRGB()) {
ecs = 16;
} else if (cs.getNumComponents() == 1) {
ecs = 17;
} else if (cs.getNumComponents() == 4) {
ecs = 12;
} else if (cs instanceof ICC_ColorSpace) {
method = 2;
profile = ((ICC_ColorSpace) cs).getProfile();
}
}
public ColorSpecificationBox(int ecs) {
this(1, 0, 1, ecs, null);
}
public ColorSpecificationBox(ICC_Profile profile) {
this(2, 0, 1, 0, profile);
}
/**
* Creates a <code>ColorSpecificationBox</code> from the provided data
* elements.
*/
public ColorSpecificationBox(int m, int p, int a, int ecs, ICC_Profile profile) {
this();
this.method = (byte) m;
this.precedence = (byte) p;
this.approximation = (byte) a;
this.ecs = ecs;
this.profile = profile;
}
/**
* Returns the method to define the color space.
*/
public byte getMethod() {
return method;
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"method\":");
sb.append(method);
sb.append(",\"precedence\":");
sb.append(precedence);
sb.append(",\"approximation\":");
sb.append(approximation);
sb.append(",\"ecs\":");
sb.append(ecs);
sb.append("}");
return sb.toString();
}
/**
* Returns <code>Precedence</code>.
*/
public byte getPrecedence() {
return precedence;
}
/**
* Returns <code>ApproximationAccuracy</code>.
*/
public byte getApproximationAccuracy() {
return approximation;
}
/**
* Returns the enumerated color space.
*/
public int getEnumeratedColorSpace() {
return ecs;
}
/**
* Returns the ICC color profile in this color specification box,
* or null none exists.
*/
public ICC_Profile getICCProfile() {
if (profile == null && profileData != null) {
synchronized (this) {
if (profile == null && profileData != null) {
profile = ICC_Profile.getInstance(profileData);
}
}
}
return profile;
}
/**
* Returns the raw bytes of the ICC color profile in this color
* specification box, or null if none exists..
*/
public byte[] getICCProfileData() {
// Reason for this method is to allows access to the ICC profile
// data without instantiation of the ICC_Profile object.
if (profileData == null && profile != null) {
synchronized (this) {
if (profileData == null && profile != null) {
profileData = profile.getData();
}
}
}
return profileData;
}
@Override
public int getLength() {
return 3 + (method == 1 ? 4 : getICCProfileData().length);
}
@Override
public void read(RandomAccessIO in) throws IOException {
method = in.readByte();
precedence = in.readByte();
approximation = in.readByte();
if (method == 2 || method == 3) {
profileData = new byte[in.length() - in.getPos()];
in.readFully(profileData, 0, profileData.length);
} else {
ecs = in.readInt();
}
}
@Override
public void write(DataOutputStream out) throws IOException {
out.write(method);
out.write(precedence);
out.write(approximation);
if (method == 1) {
out.writeInt(ecs);
} else if (getICCProfileData() != null) {
out.write(getICCProfileData());
}
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
out.writeAttribute("method", Integer.toString(getMethod()));
out.writeAttribute("precedence", Integer.toString(getPrecedence()));
out.writeAttribute("approximation", Integer.toString(getApproximationAccuracy()));
if (getMethod() == 1) {
out.writeStartElement("enumcs");
out.writeCharacters(Integer.toString(getEnumeratedColorSpace()));
out.writeEndElement();
} else if (getICCProfile() != null) {
out.writeStartElement("profile");
out.writeCharacters(toString(getICCProfileData()));
out.writeEndElement();
}
out.writeEndElement();
}
}

View file

@ -0,0 +1,111 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This represents the "cmap" box.
* This box exists if and only is a PaletteBox exists. Its
* content defines the type LUT output components and their mapping to the
* color component.
*
* @author http://bfo.com
*/
public class ComponentMappingBox extends Box {
private short[] components;
private byte[] type;
private byte[] map;
public ComponentMappingBox() {
super(fromString("cmap"));
}
/**
* Constructs a <code>ComponentMappingBox</code> from the provided
* component mapping.
*/
public ComponentMappingBox(short[] comp, byte[] t, byte[] m) {
this();
this.components = comp;
this.type = t;
this.map = m;
}
@Override
public int getLength() {
return components.length * 4;
}
@Override
public void read(RandomAccessIO in) throws IOException {
int len = in.length() / 4;
components = new short[len];
type = new byte[len];
map = new byte[len];
for (int i = 0; i < len; i++) {
components[i] = in.readShort();
type[i] = in.readByte();
map[i] = in.readByte();
}
}
@Override
public void write(DataOutputStream out) throws IOException {
for (int i = 0; i < type.length; i++) {
out.writeShort(components[i]);
out.write(type[i]);
out.write(map[i]);
}
}
public short[] getComponent() {
return components;
}
public byte[] getComponentType() {
return type;
}
public byte[] getComponentAssociation() {
return map;
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
for (int i = 0; i < components.length; i++) {
out.writeEmptyElement("cmap");
out.writeAttribute("component", Integer.toString(components[i]));
out.writeAttribute("type", Integer.toString(type[i]));
out.writeAttribute("assoc", Integer.toString(map[i]));
}
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"components\":[");
for (int i = 0; i < components.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append("{\"component\":");
sb.append(components[i]);
sb.append(",\"type\":");
sb.append(type[i]);
sb.append(",\"association\":");
sb.append(map[i]);
sb.append("}");
}
sb.append("]}");
return sb.toString();
}
}

View file

@ -0,0 +1,124 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import org.xbib.graphics.jpeg2000.j2k.io.SubRandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This represents any Box that contains other Boxes.
*
* @author http://bfo.com
*/
public class ContainerBox extends Box {
private final List<Box> boxes;
public ContainerBox(int type) {
super(type);
boxes = new ArrayList<Box>();
}
public static Box readBox(RandomAccessIO in) throws IOException {
int start = in.getPos();
int len = in.readInt();
int type = in.readInt();
Box box = Box.createBox(type);
RandomAccessIO sub;
if (len == 0) {
sub = new SubRandomAccessIO(in, in.length() - in.getPos());
} else if (len == 1) {
throw new IOException("Long boxes not supported");
} else if (len < 8) {
throw new IOException("Invalid box length " + len);
} else {
sub = new SubRandomAccessIO(in, len - 8);
}
// System.out.println("Reading box at "+start+" "+toString(type)+" len="+len+" stream="+sub.getPos()+"/"+sub.length());
box.read(sub);
// System.out.println("Skip to "+start +"+"+ len+" = "+(start+len)+" from "+in.getPos()+"/"+in.length());
if (len != 0 || start + len < in.length()) {
in.seek(len == 0 ? in.length() : start + len);
}
return box;
}
public static void writeBox(Box box, DataOutputStream out) throws IOException {
int len = box.getLength();
out.writeInt(len == 0 ? 0 : len + 8);
out.writeInt(box.getType());
box.write(out);
}
@Override
public int getLength() {
int len = 0;
for (int i = 0; i < boxes.size(); i++) {
int sublen = boxes.get(i).getLength();
if (sublen == 0) {
return 0;
}
len += 8 + sublen;
}
return len;
}
/**
* Read the content from the specified IO, which should be truncated
* to the correct length. The current position of the specified "in"
* is at the start of the "box contents" (DBox field)
*/
@Override
public void read(RandomAccessIO in) throws IOException {
while (in.length() - in.getPos() > 0) {
add(readBox(in));
}
}
@Override
public void write(DataOutputStream out) throws IOException {
for (int i = 0; i < boxes.size(); i++) {
writeBox(boxes.get(i), out);
}
}
public ContainerBox add(Box box) {
boxes.add(box);
return this;
}
public List<Box> getBoxes() {
return Collections.unmodifiableList(boxes);
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
for (Box box : boxes) {
box.write(out);
}
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"boxes\":[");
for (int i = 0; i < boxes.size(); i++) {
if (i > 0) {
sb.append(",");
}
sb.append(boxes.get(i).toString());
}
sb.append("]}");
return sb.toString();
}
}

View file

@ -0,0 +1,126 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This represents the "ftyp: box.
* <p>
* The content of a file type box contains the brand ("jp2 " for JP2 file",
* the minor version (0 for JP2 file format), and a compatibility list (one of
* which should be "jp2 " if brand is not "jp2 ".)
*
* @author http://bfo.com
*/
public class FileTypeBox extends Box {
private int brand;
private int minorVersion;
private int[] compat;
public FileTypeBox() {
super(fromString("ftyp"));
brand = 0x6a703220;
minorVersion = 0;
compat = new int[]{fromString("jp2 ")};
}
/**
* Constructs a <code>FileTypeBox</code> from the provided brand, minor
* version and compatibility list.
*/
public FileTypeBox(int br, int minorVersion, int[] compat) {
this();
this.brand = br;
this.minorVersion = minorVersion;
this.compat = compat;
}
@Override
public int getLength() {
return 8 + compat.length * 4;
}
@Override
public void read(RandomAccessIO in) throws IOException {
brand = in.readInt();
minorVersion = in.readInt();
int len = (in.length() - in.getPos()) / 4;
if (len > 0) {
compat = new int[len];
for (int i = 0; i < len; i++) {
compat[i] = in.readInt();
}
}
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(brand);
out.writeInt(minorVersion);
for (int i = 0; i < compat.length; i++) {
out.writeInt(compat[i]);
}
}
/**
* Returns the brand of this file type box.
*/
public int getBrand() {
return brand;
}
/**
* Returns the minor version of this file type box.
*/
public int getMinorVersion() {
return minorVersion;
}
/**
* Returns the compatibility list of this file type box.
*/
public int[] getCompatibilityList() {
return compat;
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
out.writeAttribute("brand", toString(brand));
out.writeAttribute("minorVersion", "0x" + Integer.toHexString(minorVersion));
for (int i = 0; i < compat.length; i++) {
out.writeStartElement("compat");
out.writeCharacters(toString(compat[i]));
out.writeEndElement();
}
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"brand\":\"");
sb.append(toString(brand));
sb.append("\",\"minorVersion\":");
sb.append(minorVersion);
sb.append("\",\"compat\":[");
for (int i = 0; i < compat.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append("\"");
sb.append(toString(compat[i]));
sb.append("\"");
}
sb.append("]}");
return sb.toString();
}
}

View file

@ -0,0 +1,101 @@
package org.xbib.graphics.jpeg2000;
/**
* This represents the "jp2h" box
*
* @author http://bfo.com
*/
public class HeaderBox extends ContainerBox {
private ImageHeaderBox ihdr;
private BitsPerComponentBox bpcc;
private PaletteBox pclr;
private ComponentMappingBox cmap;
private ChannelDefinitionBox cdef;
private ResolutionSuperBox res;
private ColorSpecificationBox colr; // the first one seen
public HeaderBox() {
super(fromString("jp2h"));
}
public ImageHeaderBox getImageHeaderBox() {
return ihdr;
}
public BitsPerComponentBox getBitsPerComponentBox() {
return bpcc;
}
public PaletteBox getPaletteBox() {
return pclr;
}
public ComponentMappingBox getComponentMappingBox() {
return cmap;
}
public ChannelDefinitionBox getChannelDefinitionBox() {
return cdef;
}
public ResolutionSuperBox getResolutionSuperBox() {
return res;
}
public ColorSpecificationBox getColorSpecificationBoxes() {
return colr;
}
@Override
public ContainerBox add(Box box) {
if (box instanceof ImageHeaderBox) {
if (ihdr == null) {
ihdr = (ImageHeaderBox) box;
} else {
throw new IllegalStateException("More than one ihdr box");
}
} else {
if (ihdr == null) {
throw new IllegalStateException("ihdr box must come first");
}
if (box instanceof BitsPerComponentBox) {
if (bpcc == null) {
bpcc = (BitsPerComponentBox) box;
} else {
throw new IllegalStateException("More than one bpcc box");
}
} else if (box instanceof ColorSpecificationBox) {
if (colr == null) {
colr = (ColorSpecificationBox) box;
}
// More than one is allowed
} else if (box instanceof PaletteBox) {
if (pclr == null) {
pclr = (PaletteBox) box;
} else {
throw new IllegalStateException("More than one pclr box");
}
} else if (box instanceof ComponentMappingBox) {
if (cmap == null) {
cmap = (ComponentMappingBox) box;
} else {
throw new IllegalStateException("More than one cmap box");
}
} else if (box instanceof ChannelDefinitionBox) {
if (cdef == null) {
cdef = (ChannelDefinitionBox) box;
} else {
throw new IllegalStateException("More than one cdef box");
}
} else if (box instanceof ResolutionSuperBox) {
if (res == null) {
res = (ResolutionSuperBox) box;
} else {
throw new IllegalStateException("More than one res box");
}
}
}
return super.add(box);
}
}

View file

@ -0,0 +1,166 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This represents the "ihdr" Box
* <p>
* The content of an image header box contains the width/height, number of
* image components, the bit depth (coded with sign/unsign information),
* the compression type (7 for JP2 file), the flag to indicate the color
* space is known or not, and a flag to indicate whether the intellectual
* property information included in this file.
*
* @author http://bfo.com
*/
public class ImageHeaderBox extends Box {
private int width;
private int height;
private short numComp;
private byte bitDepth;
private byte compressionType;
private byte unknownColor;
private byte intelProp;
public ImageHeaderBox() {
super(fromString("ihdr"));
}
/**
* Create an Image Header Box from the element values.
*/
public ImageHeaderBox(int width, int height, int numComp, int bitDepth, boolean unknownColor, boolean intelProp) {
this();
this.height = height;
this.width = width;
this.numComp = (short) numComp;
this.bitDepth = (byte) bitDepth;
this.compressionType = (byte) 7;
this.unknownColor = (byte) (unknownColor ? 1 : 0);
this.intelProp = (byte) (intelProp ? 1 : 0);
}
@Override
public int getLength() {
return 14;
}
@Override
public void read(RandomAccessIO in) throws IOException {
height = in.readInt();
width = in.readInt();
numComp = in.readShort();
bitDepth = in.readByte();
compressionType = in.readByte();
unknownColor = in.readByte();
intelProp = in.readByte();
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(height);
out.writeInt(width);
out.writeShort(numComp);
out.write(bitDepth);
out.write(compressionType);
out.write(unknownColor);
out.write(intelProp);
}
/**
* Returns the height of the image.
*/
public int getHeight() {
return height;
}
/**
* Returns the width of the image.
*/
public int getWidth() {
return width;
}
/**
* Returns the number of image components.
*/
public short getNumComponents() {
return numComp;
}
/**
* Returns the compression type.
*/
public byte getCompressionType() {
return compressionType;
}
/**
* Returns the bit depth for all the image components.
*/
public byte getBitDepth() {
return bitDepth;
}
/**
* Returns the <code>UnknowColorspace</code> flag.
*/
public byte getUnknownColorspace() {
return unknownColor;
}
/**
* Returns the <code>IntellectualProperty</code> flag.
*/
public byte getIntellectualProperty() {
return intelProp;
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeEmptyElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
out.writeAttribute("width", Integer.toString(getWidth()));
out.writeAttribute("height", Integer.toString(getHeight()));
out.writeAttribute("numc", Integer.toString(getNumComponents()));
if (getBitDepth() != 255) {
out.writeAttribute("depth", Integer.toString(getBitDepth()));
}
if (getCompressionType() != 7) {
out.writeAttribute("compression", Integer.toString(getCompressionType()));
}
out.writeAttribute("unknownColorSpace", Integer.toString(getUnknownColorspace()));
out.writeAttribute("ip", Integer.toString(getIntellectualProperty()));
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"width\":\"");
sb.append(getWidth());
sb.append(",\"height\":\"");
sb.append(getHeight());
sb.append(",\"numc\":\"");
sb.append(getNumComponents());
if (getBitDepth() != 255) {
sb.append(",\"depth\":\"");
sb.append(getBitDepth());
}
if (getCompressionType() != 7) {
sb.append(",\"compression\":\"");
sb.append(getCompressionType());
}
sb.append("\",\"unknownColorSpace\":");
sb.append(getUnknownColorspace());
sb.append("\",\"ip\":");
sb.append(getIntellectualProperty());
sb.append("}");
return sb.toString();
}
}

View file

@ -0,0 +1,131 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The J2KFile is the top level representation of a JP2 or JPX encoded
* file. It contains boxes and may be read or written in the same was
* as any {@link ContainerBox}
*
* @author http://bfo.com
*/
public class J2KFile {
private static final long SIGMARKER = 0x6a5020200d0a870aL;
private HeaderBox jp2h;
private FileTypeBox ftyp;
private CodeStreamBox jp2c;
private final List<Box> boxes;
public J2KFile() {
boxes = new ArrayList<Box>();
}
/**
* Read a J2KFile from the specified input
*/
public J2KFile read(RandomAccessIO in) throws IOException {
if (in.readInt() != 12 || in.readInt() != SIGMARKER >> 32 || in.readInt() != (int) SIGMARKER) {
throw new IOException("No JP2 Signature Box");
}
while (in.length() - in.getPos() >= 8) { // 8 is minimum length for box
add(ContainerBox.readBox(in));
}
return this;
}
/**
* Add a Box to this JP2File
*
* @param box the box
* @return this
*/
public J2KFile add(Box box) throws IOException {
if (box instanceof FileTypeBox) {
if (ftyp == null) {
ftyp = (FileTypeBox) box;
} else {
throw new IOException("More than one ftyp box");
}
} else if (ftyp == null) {
throw new IOException("No ftyp Box");
} else if (box instanceof HeaderBox) {
if (jp2h == null) {
jp2h = (HeaderBox) box;
} else {
throw new IOException("More than one jp2h box");
}
} else if (box instanceof CodeStreamBox) {
if (jp2h == null) {
throw new IOException("jp2c must follow jp2h");
} else if (jp2c == null) {
jp2c = (CodeStreamBox) box;
} else {
// Others are allowed.
}
}
boxes.add(box);
return this;
}
/**
* Return the {@link HeaderBox} from the file
*/
public HeaderBox getHeaderBox() {
return jp2h;
}
/**
* Return the {@link CodeStreamBox} from the file
*/
public CodeStreamBox getCodeStreamBox() {
return jp2c;
}
/**
* Return the full list of boxes from the file.
* The returned list is read-only
*/
public List<Box> getBoxes() {
return Collections.unmodifiableList(boxes);
}
/**
* Write the J2KFile to the specified OutputStream
*/
public OutputStream write(OutputStream out) throws IOException {
if (jp2h == null || jp2c == null) {
throw new IOException("Missing jp2h or jp2c");
}
DataOutputStream o = new DataOutputStream(out);
o.writeInt(12);
o.writeLong(SIGMARKER);
for (int i = 0; i < boxes.size(); i++) {
ContainerBox.writeBox(boxes.get(i), o);
}
return out;
}
/**
* Serialize the J2KFile as XML to the specified XMLStreamWriter
*/
public XMLStreamWriter write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement("j2k");
for (Box box : boxes) {
box.write(out);
}
out.writeEndElement();
return out;
}
}

View file

@ -0,0 +1,41 @@
package org.xbib.graphics.jpeg2000;
/**
* Interface which defines the parameters required to write a JP2 image.
* Abstracted away from J2KImageReadParamJava
*
* @author http://bfo.com
*/
public interface J2KReadParam {
/**
* If true, no ROI de-scaling is performed. Decompression is done
* like there is no ROI in the image.
* A suitable default is "true"
*/
boolean getNoROIDescaling();
/**
* Specifies the decoding rate in bits per pixel (bpp) where the
* number of pixels is related to the image's original size (Note:
* this parameter is not affected by <code>resolution</code>).
* A value of <code>Double.MAX_VALUE</code> means decode
* with the encoding rate - this is a suitable default.
*/
double getDecodingRate();
/**
* Specifies the resolution level wanted for the decoded image
* (0 means the lowest available resolution, the resolution
* level gives an image with the original dimension). If the given index
* is greater than the number of available resolution levels of the
* compressed image, the decoded image has the lowest available
* resolution (among all tile-components). This parameter affects only
* the inverse wavelet transform and not the number of bytes read by the
* codestream parser, which depends only on <code>decodingRate</code>.
* A value of -1 means to use the resolution level at encoding.
* This is a suitable default.
*/
int getResolution();
}

View file

@ -0,0 +1,603 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.codestream.HeaderInfo;
import org.xbib.graphics.jpeg2000.j2k.codestream.reader.BitstreamReaderAgent;
import org.xbib.graphics.jpeg2000.j2k.codestream.reader.HeaderDecoder;
import org.xbib.graphics.jpeg2000.j2k.decoder.DecoderSpecs;
import org.xbib.graphics.jpeg2000.j2k.entropy.decoder.EntropyDecoder;
import org.xbib.graphics.jpeg2000.j2k.image.BlkImgDataSrc;
import org.xbib.graphics.jpeg2000.j2k.image.DataBlkInt;
import org.xbib.graphics.jpeg2000.j2k.image.ImgDataConverter;
import org.xbib.graphics.jpeg2000.j2k.image.invcomptransf.InvCompTransf;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import org.xbib.graphics.jpeg2000.j2k.quantization.dequantizer.Dequantizer;
import org.xbib.graphics.jpeg2000.j2k.roi.ROIDeScaler;
import org.xbib.graphics.jpeg2000.j2k.util.FacilityManager;
import org.xbib.graphics.jpeg2000.j2k.util.MsgLogger;
import org.xbib.graphics.jpeg2000.j2k.wavelet.synthesis.InverseWT;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
/**
* <p>
* An InputStream giving access to the decoded image data. Tiles are decoded on demand, meaning
* the entire image doesn't have to be decoded in memory. The image is converted to 8-bit, YCbCr
* images are converted to RGB and component subsampling is removed, but otherwise the image data
* is unchanged.
* </p>
*
* @author http://bfo.com
*/
public class J2KReader extends InputStream implements MsgLogger {
private RandomAccessIO in;
private Thread registerThread;
private BlkImgDataSrc src; // image data source
private DecoderSpecs decSpec;
private InverseWT invWT;
private BitstreamReaderAgent breader;
private int fulliw, fullih, numtx, numty, iw, ih, scanline, numc, fullscale, scale, ntw, nth;
private int[] depth;
private int[] channels;
// variable
private DataBlkInt db;
private int pos, ty, length;
private byte[] buf;
private final boolean baseline = true;
private boolean seenapprox;
private int[][] palette;
private ColorSpace cs;
private int cstype;
private ResolutionBox resc, resd;
/**
* Create a new J2KReader from a "jp2" file
*
* @param file the J2KFile to read from
*/
public J2KReader(J2KFile file) throws IOException {
for (Box box : file.getHeaderBox().getBoxes()) {
addBox(box);
}
init(file.getCodeStreamBox().getRandomAccessIO());
}
/**
* Create a new J2KReader from a raw codestream.
*
* @param box the CodeStream to read from
*/
public J2KReader(CodeStreamBox box) throws IOException {
init(box.getRandomAccessIO());
}
private void init(RandomAccessIO in) throws IOException {
this.in = in;
registerThread = Thread.currentThread();
FacilityManager.registerMsgLogger(registerThread, this);
HeaderInfo hi = new HeaderInfo();
J2KReadParam param = new SimpleJ2KReadParam();
HeaderDecoder hd = new HeaderDecoder(in, param, hi);
depth = new int[hd.getNumComps()];
for (int i = 0; i < depth.length; i++) {
depth[i] = hd.getOriginalBitDepth(i);
}
decSpec = hd.getDecoderSpecs();
breader = BitstreamReaderAgent.createInstance(in, hd, param, decSpec, false, hi);
if (isInterrupted()) {
throw new InterruptedIOException();
}
EntropyDecoder entdec = hd.createEntropyDecoder(breader, param);
if (isInterrupted()) {
throw new InterruptedIOException();
}
ROIDeScaler roids = hd.createROIDeScaler(entdec, param, decSpec);
if (isInterrupted()) {
throw new InterruptedIOException();
}
Dequantizer deq = hd.createDequantizer(roids, depth, decSpec);
if (isInterrupted()) {
throw new InterruptedIOException();
}
invWT = InverseWT.createInstance(deq, decSpec);
if (isInterrupted()) {
throw new InterruptedIOException();
}
fullscale = breader.getImgRes();
fulliw = breader.getImgWidth(fullscale);
fullih = breader.getImgHeight(fullscale);
setTargetSize(fulliw, fullih);
}
/**
* Set the target size for the output image. The default size is
* the full size of the image, but it's possible to access lower
* resolution versions of the image by calling this method with
* the desired size. While the file image size may not match
* exactly, it will be as close as usefully possible.
*
* @param targetwidth the desired target width of the image
* @param targetheight the desired target height of the image
*/
public void setTargetSize(int targetwidth, int targetheight) {
// Find the best scale so that final width/height are >= 1 and < 2
// times the desired width.
int newscale = fullscale;
for (int i = fullscale; i >= 1; i--) {
if (targetwidth > breader.getImgWidth(i) || targetheight > breader.getImgHeight(i)) {
break;
}
newscale = i;
}
if (newscale != scale) {
scale = newscale;
invWT.setImgResLevel(scale);
ImgDataConverter converter = new ImgDataConverter(invWT, 0);
src = new InvCompTransf(converter, decSpec, depth);
iw = src.getImgWidth();
ih = src.getImgHeight();
numtx = src.getNumTiles(null).x;
numty = src.getNumTiles(null).y;
numc = src.getNumComps();
scanline = iw * numc;
}
}
protected void addBox(Box box) {
if (box instanceof ImageHeaderBox b) {
channels = new int[b.getNumComponents()];
for (int i = 0; i < channels.length; i++) {
channels[i] = i;
}
} else if (box instanceof PaletteBox b) {
int indexsize = b.getNumEntries();
int numc = b.getNumComp();
palette = new int[indexsize][numc];
for (int i = 0; i < indexsize; i++) {
for (int c = 0; c < numc; c++) {
palette[i][c] = b.getComponentValue(i, c);
}
}
} else if (box instanceof ColorSpecificationBox b) {
int method = b.getMethod();
if (method == 1) {
cs = createColorSpace(b.getEnumeratedColorSpace(), null);
} else if (method == 2 || method == 3) {
cs = createColorSpace(0, b.getICCProfileData());
}
} else if (box instanceof ChannelDefinitionBox b) {
// ComponentDefinitionBox
short[] c = b.getChannel();
short[] a = b.getAssociation();
for (int i = 0; i < c.length; i++) {
channels[c[i]] = a[i] - 1;
}
} else if (box instanceof ResolutionBox) {
if (Box.toString(box.getType()).equals("resc")) {
resc = (ResolutionBox) box;
} else if (Box.toString(box.getType()).equals("resd")) {
resd = (ResolutionBox) box;
}
}
}
public void flush() {
}
public void printmsg(int sev, String msg) {
// System.out.println(msg);
}
public void println(String str, int flind, int ind) {
// System.out.println(str);
}
//--------------------------------------------------------------
// InputStream methods
private boolean nextRow(boolean skip) throws IOException {
try {
rowCallback();
// System.out.println("IN: ty="+ty+"/"+numty+" numtx="+numtx+" numc="+numc+" skip="+skip+" pos="+pos+" length="+length);
if (ty == numty) {
return false;
}
if (!skip) {
for (int tx = 0; tx < numtx; tx++) {
src.setTile(tx, ty);
final int tileix = src.getTileIdx();
// Determine tile width/height - this is not as simple as
// calling src.getTileWidth when using less than full res.
int tw = 0;
int th = 0;
for (int iz = 0; iz < numc; iz++) {
tw = Math.max(tw, src.getTileCompWidth(tileix, iz));
th = Math.max(th, src.getTileCompHeight(tileix, iz));
}
if (db == null) {
// First pass
buf = new byte[scanline * th];
db = new DataBlkInt(0, 0, tw, th);
ntw = tw;
nth = th;
}
db.w = tw;
db.h = th;
length = scanline * th;
final int itx = tx * ntw;
final int ity = 0;
for (int iz = 0; iz < numc; iz++) {
int riz = channels == null ? iz : channels[iz]; // output channel, could differ from input channel
if (riz < 0) {
// This is the OPACITY channel. Well technically
// it's something that applies to all channels, but
// given the limitations of what we can do with that
// we'll assume opacity, as that's the only example
// seen to date.
riz = numc - 1;
}
final int depth = src.getNomRangeBits(iz);
final int mid = 1 << (depth - 1);
final int csx = src.getCompSubsX(iz);
final int csy = src.getCompSubsY(iz);
final int fb = src.getFixedPoint(iz);
// System.out.println("iwh="+iw+"x"+ih+" txy="+tx+"x"+ty+" of "+numtx+","+numty+" itxy="+src.getTilePartULX()+"x"+src.getTilePartULY()+" tcwh="+tw+"x"+th+" twh="+src.getTileWidth()+"x"+src.getTileHeight()+" ntwh="+src.getNomTileWidth()+"x"+src.getNomTileHeight()+" iz="+iz+"="+riz+" ss="+csx+"x"+csy+" d="+depth+" mid="+mid+" fb="+fb+" sl="+scanline+" buf="+buf.length+" channels="+java.util.Arrays.toString(channels));
int[] shift = null;
if (depth < 8) {
shift = new int[1 << depth];
for (int i = 0; i < shift.length; i++) {
shift[i] = Math.round(i * 255f / ((1 << depth) - 1));
}
}
do {
db = (DataBlkInt) src.getInternCompData(db, iz);
} while (db.progressive);
// Main loop: retrieve value, scaled to 8 bits and adjust midpoint
for (int iy = 0; iy < th; iy++) {
if (isInterrupted()) {
throw new InterruptedIOException();
}
for (int ix = 0; ix < tw; ix++) {
int val = (db.data[db.offset + iy * tw + ix] >> fb) + mid;
if (depth == 8) {
val = Math.max(0, Math.min(255, val));
} else if (depth > 8) {
val = Math.max(0, Math.min(255, val >> (depth - 8)));
} else {
val = shift[val < 0 ? 0 : val >= shift.length ? shift.length - 1 : val];
}
buf[((ity + (iy * csy)) * scanline) + ((itx + (ix * csx)) * numc) + riz] = (byte) val;
}
}
if (csx != 1 || csy != 1) {
// Component is subsampled; use bilinear interpolation to fill the gaps. Quick and dirty,
// tested with limited test data
for (int iy = 0; iy < th; iy++) {
if (isInterrupted()) {
throw new InterruptedIOException();
}
for (int ix = 0; ix < tw; ix++) {
// Values on each of the four corners of our space
int v00 = buf[((ity + (iy * csy)) * scanline) + ((itx + (ix * csx)) * numc) + riz] & 0xFF;
int v01 = ix + 1 == tw ? v00 : buf[((ity + (iy * csy)) * scanline) + ((itx + ((ix + 1) * csx)) * numc) + riz] & 0xFF;
int v10 = iy + 1 == th ? v00 : buf[((ity + ((iy + 1) * csy)) * scanline) + ((itx + (ix * csx)) * numc) + riz] & 0xFF;
int v11 = iy + 1 == th ? (ix + 1 == tw ? v00 : v10) : (ix + 1 == tw ? v10 : buf[((ity + ((iy + 1) * csy)) * scanline) + ((itx + ((ix + 1) * csx)) * numc) + riz] & 0xFF);
for (int jy = 0; jy < csy; jy++) {
for (int jx = 0; jx < csx; jx++) {
if (jx + jy != 0 && ix + jx < tw && iy + jy < th) {
// q = interpolated(v00, v01, v10, v11)
int q0 = v00 + ((v10 - v00) * jx / (csx - 1));
int q1 = v01 + ((v11 - v01) * jx / (csx - 1));
int q = q0 + ((q1 - q0) * jy / (csy - 1));
buf[((ity + (iy * csy) + jy) * scanline) + ((itx + (ix * csx) + jx) * numc) + riz] = (byte) q;
}
}
}
}
}
}
}
}
}
ty++;
if (ty == numty) {
free();
}
pos = 0;
return true;
} catch (RuntimeException e) {
throw new IOException("Decode failed at " + pos, e);
}
}
public int read() throws IOException {
if (pos == length) {
if (!nextRow(false)) {
return -1;
}
}
return buf[pos++] & 0xFF;
}
public int read(byte[] out) throws IOException {
return read(out, 0, out.length);
}
public int read(byte[] out, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
int origlen = len;
while (len > 0) {
if (pos == length) {
if (!nextRow(false)) {
break;
}
}
int avail = Math.min(len, length - pos);
System.arraycopy(buf, pos, out, off, avail);
len -= avail;
off += avail;
pos += avail;
}
return len == origlen ? -1 : origlen - len;
}
public long skip(long len) throws IOException {
long origlen = len;
if (buf == null && len > 0) {
// Ensure read buffer is initialized before we try and skip
int v = read();
if (v < 0) {
return 0;
}
len--;
}
while (len > 0) {
int avail = Math.min((int) len, length - pos);
len -= avail;
pos += avail;
if (pos == length) {
if (!nextRow(len > buf.length)) {
break;
}
}
}
return origlen - len;
}
public int available() {
return buf.length - pos;
}
private void free() throws IOException {
if (in != null) {
FacilityManager.unregisterMsgLogger(registerThread);
registerThread = null;
in.close();
in = null;
src = null;
decSpec = null;
invWT = null;
breader = null;
db = null;
}
}
public void close() throws IOException {
free();
}
//--------------------------------------------------------------
// Image methods
/**
* Return the overwall width of the image, in pixels
*/
public int getWidth() {
return iw;
}
/**
* Return the overall height of the image, in pixels
*/
public int getHeight() {
return ih;
}
/**
* Return the number of components in the image data,
* which will be 1 for indexed images, otherwise the number
* of components in the image ColorSpace, plus one if the
* image has an alpha channel
*/
public int getNumComponents() {
return numc;
}
/**
* Return the number of bytes in each scanline of the image
*/
public int getRowSpan() {
return getNumComponents() * getWidth();
}
/**
* Return the number of bits for each component - currently this is always 8.
*/
public int getBitsPerComponent() {
return 8;
}
/**
* Return the original bit depth for the specified component from the source image.
*/
public int getOriginalBitsPerComponent(int comp) {
return depth[comp];
}
/**
* Return the ColorSpace for the image, which may be null if this
* implementation has no support for the encoded space (eg. Lab or CMYK)
*/
public ColorSpace getColorSpace() {
return cs;
}
/**
* Return true if the image is indexed with a palette
*/
public boolean isIndexed() {
return palette != null;
}
/**
* Return the number of entries in the index
*/
public int getIndexSize() {
return palette != null ? palette.length : -1;
}
/**
* Return the "capture" resolution specified in the file in dots-per-meter,
* with the horizontal and vertical resolution stored as the X and Y
* values of the returned point. If no resolution is specified,
* return null
*/
public Point2D getCaptureResolution() {
return resc == null ? null : new Point2D.Double(resc.getHorizontalResolution(), resc.getVerticalResolution());
}
/**
* Return the "display" resolution specified in the file in dots-per-meter,
* with the horizontal and vertical resolution stored as the X and Y
* values of the returned point. If no resolution is specified,
* return null
*/
public Point2D getDisplayResolution() {
return resd == null ? null : new Point2D.Double(resd.getHorizontalResolution(), resd.getVerticalResolution());
}
/**
* Return the specified component from the image palette
*
* @param color the color, from 0..getIndexSize()
* @param component the component, from 0..getColorSpace().getNumComponents();
*/
public int getIndexComponent(int color, int component) {
return palette[color][component];
}
public String toString() {
return "{JPX: w=" + getWidth() + " h=" + getHeight() + " numc=" + getNumComponents() + " bpc=" + getBitsPerComponent() + (isIndexed() ? " ix" + getIndexSize() : "") + " rs=" + getRowSpan() + " hash=0x" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
/**
* Convert the enumerated colorspace or ICC profile data to a {@link ColorSpace}.
* This method could be overriden by subclasses to increase the number
* of supported ColorSpaces.
*
* @param e the enumerated colorspace value, eg 16 for sRGB, or 0 if an ICC profile is specified.
* @param iccprofile the raw data of the ICC profile of specified, or null if an enumerated colorspace is used.
* @return the ColorSpace, or null if it is unsupported.
*/
protected ColorSpace createColorSpace(int e, byte[] iccprofile) {
if (iccprofile != null) {
return new ICC_ColorSpace(ICC_Profile.getInstance(iccprofile));
} else {
switch (e) {
case 16:
return ColorSpace.getInstance(ColorSpace.CS_sRGB);
case 17:
return ColorSpace.getInstance(ColorSpace.CS_GRAY);
default:
cstype = e;
}
}
return null;
}
/**
* Return true if the reading process should throw an InterruptedIOException.
* By default this just checks if Thread.isInterrupted, but different Thread
* interruption mechanisms can be used by overriding this method.
*/
protected boolean isInterrupted() {
return Thread.currentThread().isInterrupted();
}
/**
* Called when a row of tiles is loaded. For benchmarking, the default is a no-op
*/
protected void rowCallback() throws IOException {
}
/**
* Read the content of this J2KReader and return it as a BufferedImage object
* The ColorSpace must be supported by Java, which means - without extensions
* to this API - only RGB, GrayScale and indexed-RGB are supported.
* The InputStream this obejct represents will be read fully and closed.
*
* @throws IOException if an IOException is encountered during read
*/
public BufferedImage getBufferedImage() throws IOException {
int width = getWidth();
int height = getHeight();
ColorSpace colorSpace = getColorSpace();
if (colorSpace == null) {
throw new IllegalStateException("Can't create image: unsupported ColorSpace type " + cstype);
}
int bpc = getBitsPerComponent();
int numc = getNumComponents();
ColorModel colorModel;
if (isIndexed() && colorSpace.isCS_sRGB()) {
// IndexColorModel is always sRGB in Java.
int indexSize = getIndexSize();
byte[] palette = new byte[3 * indexSize];
for (int i = 0; i < indexSize; i++) {
palette[i * 3] = (byte) getIndexComponent(i, 0);
palette[i * 3 + 1] = (byte) getIndexComponent(i, 1);
palette[i * 3 + 2] = (byte) getIndexComponent(i, 2);
}
colorModel = new IndexColorModel(bpc, indexSize, palette, 0, false);
} else {
boolean opaque = getNumComponents() == colorSpace.getNumComponents();
if (opaque) {
colorModel = new ComponentColorModel(colorSpace, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
} else {
colorModel = new ComponentColorModel(colorSpace, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
}
}
SampleModel sampleModel = colorModel.createCompatibleSampleModel(width, height);
DataBufferByte buffer = (DataBufferByte) sampleModel.createDataBuffer();
byte[] buf = buffer.getData();
int off = 0, l;
while (off < buf.length && (l = read(buf, off, buf.length - off)) >= 0) {
off += l;
}
close();
WritableRaster raster = Raster.createWritableRaster(sampleModel, buffer, null);
return new BufferedImage(colorModel, raster, false, null);
}
}

View file

@ -0,0 +1,86 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.IntegerSpec;
import org.xbib.graphics.jpeg2000.j2k.StringSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.CBlkSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.PrecinctSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.ProgressionSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.encoder.LayersInfo;
import org.xbib.graphics.jpeg2000.j2k.image.forwcomptransf.ForwCompTransfSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.GuardBitsSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.QuantStepSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.QuantTypeSpec;
import org.xbib.graphics.jpeg2000.j2k.roi.MaxShiftSpec;
import org.xbib.graphics.jpeg2000.j2k.wavelet.analysis.AnWTFilterSpec;
/**
* Interface which defines the parameters required to write a JP2 image.
* Abstracted away from the horror that is J2KImageWriteParamJava
*
* @author http://bfo.com
*/
public interface J2KWriteParam {
/**
* Return the desired compression ratio; a value of
* 1 implies completely lossless; values of 4-6 are
* visually lossless, up towards 20 for lossy but
* acceptable
*/
float getCompressionRatio();
boolean getLossless();
int getNumComponents();
String getLayers();
IntegerSpec getDecompositionLevel();
PrecinctSizeSpec getPrecinctPartition();
ProgressionSpec getProgressionType();
void setProgressionType(LayersInfo lyrs, String values);
String getProgressionName();
StringSpec getSOP();
StringSpec getEPH();
ForwCompTransfSpec getComponentTransformation();
CBlkSizeSpec getCodeBlockSize();
StringSpec getBypass();
StringSpec getResetMQ();
StringSpec getTerminateOnByte();
StringSpec getCausalCXInfo();
StringSpec getMethodForMQLengthCalc();
StringSpec getMethodForMQTermination();
StringSpec getCodeSegSymbol();
AnWTFilterSpec getFilters();
QuantStepSizeSpec getQuantizationStep();
QuantTypeSpec getQuantizationType();
GuardBitsSpec getGuardBits();
MaxShiftSpec getROIs();
int getStartLevelROI();
boolean getAlignROI();
int getNumTiles();
}

View file

@ -0,0 +1,257 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.codestream.writer.FileCodestreamWriter;
import org.xbib.graphics.jpeg2000.j2k.codestream.writer.HeaderEncoder;
import org.xbib.graphics.jpeg2000.j2k.entropy.encoder.EntropyCoder;
import org.xbib.graphics.jpeg2000.j2k.entropy.encoder.PostCompRateAllocator;
import org.xbib.graphics.jpeg2000.j2k.image.BlkImgDataSrc;
import org.xbib.graphics.jpeg2000.j2k.image.ImgDataConverter;
import org.xbib.graphics.jpeg2000.j2k.image.forwcomptransf.ForwCompTransf;
import org.xbib.graphics.jpeg2000.j2k.io.AbstractDataSource;
import org.xbib.graphics.jpeg2000.j2k.quantization.quantizer.Quantizer;
import org.xbib.graphics.jpeg2000.j2k.roi.encoder.ROIScaler;
import org.xbib.graphics.jpeg2000.j2k.util.FacilityManager;
import org.xbib.graphics.jpeg2000.j2k.util.MsgLogger;
import org.xbib.graphics.jpeg2000.j2k.wavelet.analysis.ForwardWT;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* A class to create a J2KFile. J2K compressed data may be
* created from any source, although for convenience a
* method is supplied to create from a BufferedImage. Compression
* requires all the image data all the time, so compression from
* a stream is not possible.
*
* @author http://bfo.com
*/
public class J2KWriter implements MsgLogger {
private ColorSpecificationBox colr;
private PaletteBox pclr;
private ResolutionBox resc, resd;
private J2KWriteParam param;
private float ratio;
private boolean reversible;
private BlkImgDataSrc src;
/**
* Create a new J2KWriter
*/
public J2KWriter() {
}
@Override
public void flush() {
}
@Override
public void printmsg(int sev, String msg) {
}
@Override
public void println(String str, int flind, int ind) {
}
/**
* Set the write parameters. Usually this isn't required, but
* it may be called for fine control of the encoding
*/
public void setParams(J2KWriteParam param) {
this.param = param;
}
/**
* Set the compresison ratio.
*
*/
public void setCompressionRatio(float ratio, boolean reversible) {
this.ratio = ratio;
this.reversible = reversible;
}
/**
* Set the ColorSpace that is written out. This is required if
* a BufferedImage wasn't used as a source
*/
public void setColorSpace(ColorSpace space) {
colr = new ColorSpecificationBox(space);
pclr = null;
}
/**
* Set the ColorSpace that is written out, and use a palette
*
* @param space the underlying ColorSpace
* @param size the number of entries in the palette
* @param numc the number of components in the palette index
* @param palette the palette entries, which are accessed as <code>palette[entry * numc + component]</code>
*/
public void setColorSpace(ColorSpace space, int size, int numc, byte[] palette) {
colr = new ColorSpecificationBox(space);
pclr = new PaletteBox(size, numc, palette);
}
/**
* Set the "capture" resolution that is written out.
*
* @param horiz the horizontal resolution in dots-per-meter, or 0 for no box
* @param vertical the horizontal resolution in dots-per-meter, or 0 for no box
*/
public void setCaptureResolution(double horiz, double vertical) {
resc = horiz > 0 && vertical > 0 ? new ResolutionBox(Box.fromString("resc"), (float) horiz, (float) vertical) : null;
}
/**
* Set the "diisplay" resolution that is written out.
*
* @param horiz the horizontal resolution in dots-per-meter, or 0 for no box
* @param vertical the horizontal resolution in dots-per-meter, or 0 for no box
*/
public void setDisplayResolution(double horiz, double vertical) {
resd = horiz > 0 && vertical > 0 ? new ResolutionBox(Box.fromString("resd"), (float) horiz, (float) vertical) : null;
}
/**
* Set the Source for the image. Any BlkImgDataSrc may be used, but
* a {@link AbstractDataSource} would be the easiest
*/
public void setSource(BlkImgDataSrc src) {
this.src = src;
}
/**
* Set the Source for the image to the BufferedImage.
*
* @param img the image
* @param tilesize the tilesize, 256 is recommended
*/
public void setSource(BufferedImage img, int tilesize) {
setSource(AbstractDataSource.newInstance(img, tilesize));
ColorModel cm = img.getColorModel();
if (cm instanceof IndexColorModel) {
throw new UnsupportedOperationException("No indexed yet");
// TODO
} else {
setColorSpace(cm.getColorSpace());
}
}
private J2KFile doCreate(OutputStream out) throws IOException {
if (src == null) {
throw new IllegalStateException("No source");
}
if (param == null) {
param = new SimpleJ2KWriteParam(src.getNumComps(), src.getNumTiles());
((SimpleJ2KWriteParam) param).setProgressionName("res");
}
if (ratio != 0 && param instanceof SimpleJ2KWriteParam) {
((SimpleJ2KWriteParam) param).setCompression(ratio, reversible);
}
if (param.getNumComponents() != src.getNumComps() || param.getNumTiles() != src.getNumTiles()) {
throw new IllegalStateException("Param and source do not match");
}
Thread registerThread = Thread.currentThread();
J2KFile file = new J2KFile();
HeaderBox jp2h = new HeaderBox();
int bpc = src.getNomRangeBits(0);
int totbpc = bpc;
for (int i = 1; i < src.getNumComps(); i++) {
totbpc += src.getNomRangeBits(i);
if (src.getNomRangeBits(i) != bpc) {
bpc = 255;
}
}
jp2h.add(new ImageHeaderBox(src.getImgWidth(), src.getImgHeight(), src.getNumComps(), bpc, false, false));
if (colr != null) {
jp2h.add(colr);
}
if (pclr != null) {
jp2h.add(pclr);
}
if (resc != null || resd != null) {
ResolutionSuperBox res = new ResolutionSuperBox();
if (resc != null) {
res.add(resc);
}
if (resd != null) {
res.add(resd);
}
jp2h.add(res);
}
if (bpc == 255) {
byte[] b = new byte[src.getNumComps()];
for (int i = 0; i < b.length; i++) {
b[i] = (byte) src.getNomRangeBits(i);
}
jp2h.add(new BitsPerComponentBox(b));
}
file.add(new FileTypeBox());
file.add(jp2h);
OutputStream bout;
if (out == null) {
bout = new ByteArrayOutputStream();
} else {
file.add(new CodeStreamBox());
file.write(out);
bout = out;
}
try {
FacilityManager.registerMsgLogger(registerThread, this);
ForwCompTransf fctransf = new ForwCompTransf(src, param);
ImgDataConverter converter = new ImgDataConverter(fctransf);
ForwardWT dwt = ForwardWT.createInstance(converter, param);
Quantizer quant = Quantizer.createInstance(dwt, param);
ROIScaler rois = ROIScaler.createInstance(quant, param);
EntropyCoder ecoder = EntropyCoder.createInstance(rois, param, param.getCodeBlockSize(), param.getPrecinctPartition(), param.getBypass(), param.getResetMQ(), param.getTerminateOnByte(), param.getCausalCXInfo(), param.getCodeSegSymbol(), param.getMethodForMQLengthCalc(), param.getMethodForMQTermination());
FileCodestreamWriter bwriter = new FileCodestreamWriter(bout, Integer.MAX_VALUE);
ratio = param.getCompressionRatio();
float rate = ratio == 1 ? Float.POSITIVE_INFINITY : totbpc / ratio;
PostCompRateAllocator ralloc = PostCompRateAllocator.createInstance(ecoder, rate, bwriter, param);
HeaderEncoder headenc = new HeaderEncoder(src, new boolean[src.getNumComps()], dwt, src, param, rois, ralloc);
ralloc.setHeaderEncoder(headenc);
headenc.encodeMainHeader();
ralloc.initialize();
headenc.reset();
headenc.encodeMainHeader();
bwriter.commitBitstreamHeader(headenc);
ralloc.runAndWrite();
bwriter.close();
if (out == null) {
file.add(new CodeStreamBox(((ByteArrayOutputStream) bout).toByteArray()));
}
return file;
} finally {
FacilityManager.unregisterMsgLogger(registerThread);
}
}
/**
* Create and return a {@link J2KFile} which has the compressed image data
*/
public J2KFile create() throws IOException {
return doCreate(null);
}
/**
* Create and write a {@link J2KFile} to the specified OutputStream. This
* method does not need any intermediate buffers and can write the compressed
* image straight to the OutputStream, unlike {@link #create} which has to
* store the image in memory.
*/
public void write(OutputStream out) throws IOException {
doCreate(out);
}
}

View file

@ -0,0 +1,169 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import java.awt.image.IndexColorModel;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents the "pclr" box.
* <p>
* Its content contains the number of palette entry, the number of color
* components, the bit depths of the output components, the LUT.
*
* @author http://bfo.com
*/
public class PaletteBox extends Box {
private int numEntries;
private int numc;
private int[] bitDepth;
private int[] lut;
public PaletteBox() {
super(fromString("pclr"));
}
/**
* Constructs a <code>PlatteBox</code> from an <code>IndexColorModel</code>.
* Lifted from JAI, untested
*/
public PaletteBox(IndexColorModel icm) {
this(icm.hasAlpha() ? new int[]{8, 8, 8, 8} : new int[]{8, 8, 8}, getLUT(icm));
}
/**
* Constructs a <code>PlatteBox</code> from the provided length, bit
* depths of the color components and the LUT.
*/
public PaletteBox(int[] depths, int[] lut) {
this();
this.bitDepth = depths;
this.lut = lut;
this.numc = depths.length;
this.numEntries = lut.length / numc;
}
public PaletteBox(int len, int numc, byte[] lut) {
this();
this.numEntries = len;
this.numc = numc;
this.bitDepth = new int[numc];
for (int i = 0; i < numc; i++) {
bitDepth[i] = 8;
}
this.lut = new int[len * numc];
for (int i = 0; i < lut.length; i++) {
this.lut[i] = lut[i] & 0xFF;
}
}
/**
* Gets the LUT from the <code>IndexColorModel</code> as an two-dimensional
* byte array.
*/
private static int[] getLUT(IndexColorModel icm) {
int size = icm.getMapSize();
int numc = icm.hasAlpha() ? 3 : 4; // IndexColorModel always RGB or RGBA
int[] lut = new int[size * numc];
int k = 0;
for (int i = 0; i < size; i++) {
lut[k++] = icm.getRed(i);
lut[k++] = icm.getGreen(i);
lut[k++] = icm.getBlue(i);
if (numc == 4) {
lut[k++] = icm.getAlpha(i);
}
}
return lut;
}
/**
* Return the number of palette entries.
*/
public int getNumEntries() {
return numEntries;
}
/**
* Return the number of color components.
*/
public int getNumComp() {
return numc;
}
public int getComponentDepth(int component) {
return (bitDepth[component] & 0x7F) + 1;
}
public boolean isComponentSigned(int component) {
return (bitDepth[component] & 0x80) != 0;
}
public int getComponentValue(int entry, int component) {
return lut[entry * numc + component];
}
@Override
public void read(RandomAccessIO in) throws IOException {
numEntries = in.readShort();
numc = in.readByte() & 0xFF;
bitDepth = new int[numc];
for (int i = 0; i < numc; i++) {
bitDepth[i] = in.readByte() & 0xFF;
}
lut = new int[in.length() - in.getPos()];
for (int i = 0; i < lut.length; i++) {
int c = i % numc;
int d = getComponentDepth(c);
boolean signed = isComponentSigned(c);
if (d <= 8) {
lut[i] = signed ? (int) in.readByte() : in.readByte() & 0xFF;
} else {
lut[i] = signed ? (int) in.readShort() : in.readShort() & 0xFFFF;
}
}
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeShort(numEntries);
out.write(numc);
for (int i = 0; i < numc; i++) {
out.write(bitDepth[i]);
}
for (int i = 0; i < lut.length; i++) {
int c = i % numc;
int v = i / numc;
int d = getComponentDepth(c);
if (d <= 8) {
out.write(getComponentValue(v, c));
} else {
out.writeShort(getComponentValue(v, c));
}
}
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"depth\":[");
for (int i = 0; i < numc; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(bitDepth[i]);
}
sb.append("],\"lut\":[");
for (int i = 0; i < lut.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(lut[i]);
}
sb.append("]}");
return sb.toString();
}
}

View file

@ -0,0 +1,144 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents the "resc" and "resd" boxes.
* <p>
* Its contens includes the resolution numerators, denominator, and the
* exponents for both horizontal and vertical directions.
*
* @author http://bfo.com, with large parts copied from JAI source
*/
public class ResolutionBox extends Box {
private short numV;
private short numH;
private short denomV;
private short denomH;
private byte expV;
private byte expH;
/**
* The cached horizontal/vertical resolutions.
*/
private float hRes;
private float vRes;
public ResolutionBox(int type) {
super(type);
}
/**
* Constructs a <code>ResolutionBox</code> from the provided type and
* horizontal/vertical resolutions.
*/
public ResolutionBox(int type, float hRes, float vRes) {
this(type);
this.hRes = hRes;
this.vRes = vRes;
denomH = denomV = 1;
expV = 0;
if (vRes >= 32768) {
int temp = (int) vRes;
while (temp >= 32768) {
expV++;
temp /= 10;
}
numV = (short) (temp & 0xFFFF);
} else {
numV = (short) vRes;
}
expH = 0;
if (hRes >= 32768) {
int temp = (int) hRes;
while (temp >= 32768) {
expH++;
temp /= 10;
}
numH = (short) (temp & 0xFFFF);
} else {
numH = (short) hRes;
}
}
/**
* Return the horizontal resolution.
*/
public float getHorizontalResolution() {
return hRes;
}
/**
* Return the vertical resolution.
*/
public float getVerticalResolution() {
return vRes;
}
@Override
public int getLength() {
return 10;
}
@Override
public void read(RandomAccessIO in) throws IOException {
numV = in.readShort();
denomV = in.readShort();
numH = in.readShort();
denomH = in.readShort();
expV = in.readByte();
expH = in.readByte();
vRes = (float) ((numV & 0xFFFF) * Math.pow(10, expV) / (denomV & 0xFFFF));
hRes = (float) ((numH & 0xFFFF) * Math.pow(10, expH) / (denomH & 0xFFFF));
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeShort(numV);
out.writeShort(denomV);
out.writeShort(numH);
out.writeShort(denomH);
out.write(expV);
out.write(expH);
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeEmptyElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
out.writeAttribute("hres", Float.toString(hRes));
out.writeAttribute("vres", Float.toString(vRes));
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"nv\":");
sb.append(numV);
sb.append(",\"dv\":");
sb.append(denomV);
sb.append(",\"ev\":");
sb.append(expV);
sb.append(",\"nh\":");
sb.append(numH);
sb.append(",\"dh\":");
sb.append(denomH);
sb.append(",\"eh\":");
sb.append(expH);
sb.append(",\"hres\":");
sb.append(hRes);
sb.append(",\"vres\":");
sb.append(vRes);
sb.append("}");
return sb.toString();
}
}

View file

@ -0,0 +1,17 @@
package org.xbib.graphics.jpeg2000;
/**
* Represents the "res" resolution superbox
*/
public class ResolutionSuperBox extends ContainerBox {
public ResolutionSuperBox() {
super(fromString("res "));
}
public ResolutionSuperBox(ResolutionBox box) {
this();
add(box);
}
}

View file

@ -0,0 +1,23 @@
package org.xbib.graphics.jpeg2000;
/**
* Interface which defines the parameters required to read a JP2 image.
* The default values for each property are used.
*
* @author http://bfo.com
*/
public class SimpleJ2KReadParam implements J2KReadParam {
public boolean getNoROIDescaling() {
return true;
}
public double getDecodingRate() {
return Double.MAX_VALUE;
}
public int getResolution() {
return -1;
}
}

View file

@ -0,0 +1,299 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.IntegerSpec;
import org.xbib.graphics.jpeg2000.j2k.ModuleSpec;
import org.xbib.graphics.jpeg2000.j2k.StringSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.CBlkSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.PrecinctSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.ProgressionSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.encoder.LayersInfo;
import org.xbib.graphics.jpeg2000.j2k.image.forwcomptransf.ForwCompTransfSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.GuardBitsSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.QuantStepSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.QuantTypeSpec;
import org.xbib.graphics.jpeg2000.j2k.roi.MaxShiftSpec;
import org.xbib.graphics.jpeg2000.j2k.wavelet.analysis.AnWTFilterSpec;
/**
* A minimal instance of the J2KWriteParam interface.
*
* @author http://bfo.com
*/
public class SimpleJ2KWriteParam implements J2KWriteParam {
private final int numc, numtiles;
private final StringSpec stringtrue, stringfalse;
private IntegerSpec decompositionLevel;
private PrecinctSizeSpec precinctPartition;
private ProgressionSpec progressionType;
private GuardBitsSpec guardBits;
private AnWTFilterSpec filters;
private ForwCompTransfSpec componentTransformation;
private QuantStepSizeSpec quantizationStep;
private QuantTypeSpec quantizationType;
private CBlkSizeSpec codeBlockSize;
private StringSpec methodForMQTermination;
private StringSpec methodForMQLengthCalc;
private int startLevelROI;
private float ratio;
private String layers;
private String progressionName;
private boolean alignROI;
private boolean sop;
private boolean eph;
private boolean bypass;
private boolean resetMQ;
private boolean terminateOnByte;
private boolean causalCXInfo;
private boolean codeSegSymbol;
private boolean lossless;
private MaxShiftSpec rois;
/**
* Create a new SimpleJ2KWriteParam
*
* @param numc the number of components
* @param numtiles the number of tiles
*/
public SimpleJ2KWriteParam(int numc, int numtiles) {
this.numc = numc;
this.numtiles = numtiles;
stringtrue = new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, "true");
stringfalse = new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, null);
setLayers("0.015 +20 2.0 +10");
setProgressionName("layer");
setDecompositionLevel(5);
setGuardBits(2);
setQuantizationStep(Double.NaN);
setCodeBlockSize(64, 64);
setFilters(true, true);
setROIs(-1, false, null);
setMQ(null, null);
setCompression(1, true);
}
/**
* Set the desired Compression Ratio. A value of
* 1 implies lossless, higher ratios involve loss
*
* @param ratio the compression ratio
* @param reversible if true, the reversible filter is used. The irreversible one tends to give slightly better results but may use more memory
*/
public void setCompression(float ratio, boolean reversible) {
this.ratio = Math.max(1, ratio);
lossless = ratio == 1;
setFilters(lossless || reversible, true);
}
public float getCompressionRatio() {
return ratio;
}
public int getNumComponents() {
return numc;
}
public int getNumTiles() {
return numtiles;
}
public boolean getLossless() {
return lossless;
}
public String getLayers() {
return layers;
}
private void setLayers(String layers) {
this.layers = layers;
}
public IntegerSpec getDecompositionLevel() {
return decompositionLevel;
}
public void setDecompositionLevel(int level) {
decompositionLevel = new IntegerSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, this, Integer.toString(level), "5");
precinctPartition = new PrecinctSizeSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, null, getDecompositionLevel(), this, null);
}
public PrecinctSizeSpec getPrecinctPartition() {
return precinctPartition;
}
public void setProgressionType(LayersInfo lyrs, String values) {
progressionType = new ProgressionSpec(getNumTiles(), getNumComponents(), lyrs.getTotNumLayers(), getDecompositionLevel(), ModuleSpec.SPEC_TYPE_TILE_COMP, this, values);
}
public ProgressionSpec getProgressionType() {
return progressionType;
}
public String getProgressionName() {
return progressionName;
}
public void setProgressionName(String name) {
if ("res".equals(name) || "layer".equals(name) || "res-pos".equals(name) || "pos-comp".equals(name) || "comp-pos".equals(name)) {
progressionName = name;
} else {
throw new IllegalArgumentException(name);
}
}
public void setFilters(boolean filter53, boolean transform) {
if (filter53) {
quantizationType = new QuantTypeSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, this, "reversible");
filters = new AnWTFilterSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, getQuantizationType(), this, "w5x3");
} else {
quantizationType = new QuantTypeSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, this, "expounded");
filters = new AnWTFilterSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, getQuantizationType(), this, "w9x7");
}
componentTransformation = new ForwCompTransfSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE, getFilters(), this, Boolean.toString(transform));
}
public AnWTFilterSpec getFilters() {
return filters;
}
public QuantTypeSpec getQuantizationType() {
return quantizationType;
}
public ForwCompTransfSpec getComponentTransformation() {
return componentTransformation;
}
public void setCodeBlockSize(int w, int h) {
codeBlockSize = new CBlkSizeSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, this, w + " " + h);
}
public CBlkSizeSpec getCodeBlockSize() {
return codeBlockSize;
}
public StringSpec getSOP() {
// return sop ? stringtrue : stringfalse;
return new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, "false");
}
public StringSpec getEPH() {
// return eph ? stringtrue : stringfalse;
return new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, "false");
}
public StringSpec getBypass() {
// return bypass ? stringtrue : stringfalse;
return new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, "false");
}
public StringSpec getResetMQ() {
// return resetMQ ? stringtrue : stringfalse;
return new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, null);
}
public StringSpec getTerminateOnByte() {
// return terminateOnByte ? stringtrue : stringfalse;
return new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, null);
}
public StringSpec getCausalCXInfo() {
// return causalCXInfo ? stringtrue : stringfalse;
return new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, null);
}
public StringSpec getCodeSegSymbol() {
// return codeSegSymbol ? stringtrue : stringfalse;
return new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "false", new String[]{"true", "false"}, null, null);
}
public void setMQ(String lengthcalc, String termination) {
if (lengthcalc == null) {
lengthcalc = "near_opt";
}
if (termination == null) {
termination = "near_opt";
}
methodForMQLengthCalc = new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "near_opt", new String[]{"near_opt", "lazy_good", "lazy"}, this, lengthcalc);
methodForMQTermination = new StringSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, "near_opt", new String[]{"near_opt", "easy", "predict", "full"}, this, termination);
}
public StringSpec getMethodForMQLengthCalc() {
return methodForMQLengthCalc;
}
public StringSpec getMethodForMQTermination() {
return methodForMQTermination;
}
public QuantStepSizeSpec getQuantizationStep() {
return quantizationStep;
}
public void setQuantizationStep(double val) {
quantizationStep = new QuantStepSizeSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, this, val == val ? Double.toString(val) : null);
}
public GuardBitsSpec getGuardBits() {
return guardBits;
}
public void setGuardBits(int bits) {
if (bits <= 0) {
throw new IllegalArgumentException();
}
guardBits = new GuardBitsSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, this, Integer.toString(bits));
}
public void setROIs(int start, boolean align, String rois) {
this.startLevelROI = start;
this.alignROI = align;
this.rois = new MaxShiftSpec(getNumTiles(), getNumComponents(), ModuleSpec.SPEC_TYPE_TILE_COMP, rois);
}
public MaxShiftSpec getROIs() {
return rois;
}
public int getStartLevelROI() {
return startLevelROI;
}
public boolean getAlignROI() {
return alignROI;
}
public String toString() {
String sb = "{ lossless:" + getLossless() +
", numc:" + getNumComponents() +
", numt:" + getNumTiles() +
", layers:'" + getLayers() + "'" +
", decomp:" + getDecompositionLevel() +
", precinct:" + getPrecinctPartition() +
", prog:" + getProgressionType() +
", progn:'" + getProgressionName() + "'" +
", sop:" + getSOP() +
", eph:" + getEPH() +
", tran:" + getComponentTransformation() +
", cbsize:" + getCodeBlockSize() +
", bypass:" + getBypass() +
", resetmq:" + getResetMQ() +
", termonbyte:" + getTerminateOnByte() +
", causalcxinfo:" + getCausalCXInfo() +
", mqlengthcalc:" + getMethodForMQLengthCalc() +
", mqterm:" + getMethodForMQTermination() +
", codeseg:" + getCodeSegSymbol() +
", filters:" + getFilters() +
", quantstep:" + getQuantizationStep() +
", quanttype:" + getQuantizationType() +
", guardbits:" + getGuardBits() +
", rois:" + getROIs() +
", startlevelroi:" + getStartLevelROI() +
", alignroi:" + getAlignROI() +
"}";
return sb;
}
}

View file

@ -0,0 +1,109 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* This class represents the "url " box.
* Its content are a one-byte version, a three-byte flags and
* a URL pertains to the UUID List box within its UUID Info superbox.
*
* @author http://bfo.com
*/
public class URLBox extends Box {
/**
* The element values.
*/
private int version;
private int flags;
private String url;
public URLBox() {
super(fromString("url "));
}
/**
* Constructs a <code>DataEntryURLBox</code> from its data elements.
*/
public URLBox(int version, int flags, String url) {
this();
this.version = version;
this.flags = flags;
this.url = url;
}
@Override
public int getLength() {
return 4 + url.length();
}
@Override
public void read(RandomAccessIO in) throws IOException {
version = in.read();
flags = (in.read() << 16) | (in.read() << 8) | in.read();
byte[] b = new byte[in.length() - in.getPos()];
in.readFully(b, 0, b.length);
url = new String(b, StandardCharsets.ISO_8859_1);
}
@Override
public void write(DataOutputStream out) throws IOException {
out.write(version);
out.write(flags >> 16);
out.write(flags >> 8);
out.write(flags);
out.write(url.getBytes(StandardCharsets.ISO_8859_1));
}
/**
* Returns the <code>Version</code> data element.
*/
public int getVersion() {
return version;
}
/**
* Returns the <code>Flags</code> data element.
*/
public int getFlags() {
return flags;
}
/**
* Returns the <code>URL</code> data element.
*/
public String getURL() {
return url;
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
out.writeAttribute("version", Integer.toString(getVersion()));
out.writeAttribute("flags", "0x" + Integer.toHexString(getFlags()));
out.writeCharacters(getURL());
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"version\":");
sb.append(getVersion());
sb.append(",\"flags\":");
sb.append(getFlags());
sb.append(",\"url\":\"");
sb.append(getURL().replaceAll("\"", "\\\""));
sb.append("\"}");
return sb.toString();
}
}

View file

@ -0,0 +1,166 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
/**
* This class represents the "uuid" box.
* Its content is a 16-byte UUID followed with a various-length data.
*
* @author http://bfo.com
*/
public class UUIDBox extends Box {
public static final String UUID_XMP = "be7acfcb97a942e89c71999491e3afac";
private byte[] uuid;
private byte[] data;
/**
* Constructs a <code>UUIDBox</code> from its content data array.
*/
public UUIDBox() {
super(fromString("uuid"));
}
/**
* Create a new UUID box
*
* @param key the key, which must be a 16 byte long array encoded as a 32-character long hex string
* @parma data the data
*/
public UUIDBox(String key, byte[] data) {
this();
if (key.length() != 32) {
throw new IllegalArgumentException();
}
uuid = new byte[16];
for (int i = 0; i < 16; i++) {
uuid[i] = (byte) ((Character.digit(key.charAt(i * 2), 16) << 4) + Character.digit(key.charAt(i * 2 + 1), 16));
}
this.data = data;
}
@Override
public int getLength() {
return uuid.length + data.length;
}
@Override
public void read(RandomAccessIO in) throws IOException {
uuid = new byte[16];
in.readFully(uuid, 0, 16);
data = new byte[in.length() - in.getPos()];
in.readFully(data, 0, data.length);
}
@Override
public void write(DataOutputStream out) throws IOException {
out.write(uuid, 0, uuid.length);
out.write(data, 0, data.length);
}
/**
* Returns the UUID of this box.
*/
public String getUUID() {
return toString(uuid);
}
/**
* Returns the UUID data of this box.
*/
public byte[] getData() {
return data;
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
out.writeAttribute("uuid", getUUID());
boolean raw = true;
if (UUID_XMP.equals(getUUID())) {
try {
String s = new String(getData(), StandardCharsets.UTF_8);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.transform(new StreamSource(new StringReader(s)), new StreamResult(new StringWriter()));
// Syntax is valid, redo output to actual outputstream, removing startDoc/endDoc
final XMLEventWriter w = XMLOutputFactory.newInstance().createXMLEventWriter(new StAXResult(out));
t.transform(new StreamSource(new StringReader(s)), new StAXResult(new XMLEventWriter() {
public void add(XMLEvent event) throws XMLStreamException {
if (!event.isStartDocument() && !event.isEndDocument()) {
w.add(event);
}
}
public void add(XMLEventReader reader) throws XMLStreamException {
w.add(reader);
}
public void close() throws XMLStreamException {
}
public void flush() throws XMLStreamException {
w.flush();
}
public NamespaceContext getNamespaceContext() {
return w.getNamespaceContext();
}
public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
w.setNamespaceContext(context);
}
public String getPrefix(String uri) throws XMLStreamException {
return w.getPrefix(uri);
}
public void setDefaultNamespace(String uri) throws XMLStreamException {
w.setDefaultNamespace(uri);
}
public void setPrefix(String prefix, String uri) throws XMLStreamException {
w.setPrefix(prefix, uri);
}
}));
raw = false;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (raw) {
out.writeCharacters(toString(data));
}
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append("\"uuid\":\"");
sb.append(getUUID());
sb.append("\",\"data\":");
sb.append(toString(getData()));
sb.append("\"}");
return sb.toString();
}
}

View file

@ -0,0 +1,50 @@
package org.xbib.graphics.jpeg2000;
/**
* Represents the "uinf" box
*
* @author http://bfo.com
*/
public class UUIDInfoBox extends ContainerBox {
private UUIDListBox ulst;
private URLBox url;
public UUIDInfoBox() {
super(fromString("uinf"));
}
public UUIDInfoBox(UUIDListBox ulst, URLBox url) {
this();
add(ulst);
add(url);
}
public UUIDListBox getUUIDListBox() {
return ulst;
}
public URLBox getURLBox() {
return url;
}
@Override
public ContainerBox add(Box box) {
if (box instanceof UUIDListBox) {
if (ulst == null) {
ulst = (UUIDListBox) box;
} else {
throw new IllegalStateException("More than one ulst box");
}
} else if (box instanceof URLBox) {
if (ulst == null) {
throw new IllegalStateException("ulst box must come before url box");
} else if (url == null) {
url = (URLBox) box;
} else {
throw new IllegalStateException("More than one url box");
}
}
return super.add(box);
}
}

View file

@ -0,0 +1,101 @@
package org.xbib.graphics.jpeg2000;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* This class represents the "ulst" box.
* Its contents include the number of UUID entry and a list of 16-byte UUIDs.
*
* @author http://bfo.com
*/
public class UUIDListBox extends Box {
private byte[][] uuids;
public UUIDListBox() {
super(fromString("ulst"));
}
/**
* Constructs a <code>UUIDListBox</code> from the provided list of UUIDs
*
* @param uuids a list of uuids, each represented as 32-character long strings representing a hex-encoded 16-byte array
*/
public UUIDListBox(String[] uuids) {
this();
this.uuids = new byte[uuids.length][16];
for (int j = 0; j < uuids.length; j++) {
String key = uuids[j];
if (key.length() != 32) {
throw new IllegalArgumentException();
}
for (int i = 0; i < 16; i++) {
this.uuids[j][i] = (byte) ((Character.digit(key.charAt(i * 2), 16) << 4) + Character.digit(key.charAt(i * 2 + 1), 16));
}
}
}
public int getSize() {
return uuids.length;
}
public String getUUID(int i) {
return toString(uuids[i]);
}
@Override
public int getLength() {
return 2 + uuids.length * 16;
}
@Override
public void read(RandomAccessIO in) throws IOException {
int num = in.readShort();
uuids = new byte[num][16];
for (int i = 0; i < num; i++) {
in.readFully(uuids[i], 0, 16);
}
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeShort(uuids.length);
for (int i = 0; i < uuids.length; i++) {
out.write(uuids[i]);
}
}
@Override
public void write(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(toString(getType()).trim());
out.writeAttribute("length", Integer.toString(getLength()));
for (int i = 0; i < uuids.length; i++) {
out.writeStartElement("uuid");
out.writeCharacters(toString(uuids[i]));
out.writeEndElement();
}
out.writeEndElement();
}
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
sb.deleteCharAt(sb.length() - 1);
sb.append(",\"uuids\":[");
for (int i = 0; i < uuids.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append("\"");
sb.append(toString(uuids[i]));
sb.append("\"");
}
sb.append("]}");
return sb.toString();
}
}

View file

@ -0,0 +1,15 @@
package org.xbib.graphics.jpeg2000;
/**
* This class represnts the "xml " box.
*
* @author http://bfo.com
*/
public class XMLBox extends Box {
public XMLBox() {
super(fromString("xml "));
}
// TODO
}

View file

@ -0,0 +1,265 @@
package org.xbib.graphics.jpeg2000.imageio;
import org.xbib.graphics.jpeg2000.J2KFile;
import org.xbib.graphics.jpeg2000.J2KReader;
import org.xbib.graphics.jpeg2000.j2k.io.AbstractRandomAccessIO;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
/**
* ImageIO compatible reader for JPEG2000 images.
*/
public class JPEG2000Reader extends ImageReader {
private BufferedImage image;
public JPEG2000Reader(ImageReaderSpi imageReaderImpl) {
super(imageReaderImpl);
}
@Override
public boolean canReadRaster() {
return true;
}
@Override
public int getNumImages(boolean allowSearch) {
return 1;
}
@Override
public int getWidth(int imageIndex) throws IOException {
return getImage().getWidth();
}
@Override
public int getHeight(int imageIndex) throws IOException {
return getImage().getHeight();
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
BufferedImage image = getImage();
return Collections.singleton(new ImageTypeSpecifier(image.getColorModel(), image.getSampleModel())).iterator();
}
@Override
public IIOMetadata getStreamMetadata() {
throw new UnsupportedOperationException("Metadata not supported");
}
@Override
public IIOMetadata getImageMetadata(int imageIndex) {
throw new UnsupportedOperationException("Metadata not supported");
}
@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
return getImage();
}
@Override
public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) {
super.setInput(input, seekForwardOnly, ignoreMetadata);
image = null;
}
private BufferedImage getImage() throws IOException {
if (image == null) {
ImageInputStream in = (ImageInputStream) getInput();
J2KFile jfile = new J2KFile();
RandomAccessIO io;
if (in.length() > 0) {
io = new FixedJ2KRandomAccessIO(in);
} else {
io = new VariableJ2KRandomAccessIO(in);
}
jfile.read(io);
J2KReader reader = new J2KReader(jfile);
image = reader.getBufferedImage();
reader.close();
}
return image;
}
/**
* AbstractRandomAccessIO that proxies to an unknown-length ImageInputStream,
* which reads the source stream into memory
*/
private static class VariableJ2KRandomAccessIO extends AbstractRandomAccessIO {
private byte[] buffer;
private int pos;
VariableJ2KRandomAccessIO(ImageInputStream in) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream(32768);
byte[] buffer = new byte[32768];
int len = 0;
while (len >= 0) {
len = in.read(buffer);
if (len > 0) {
bout.write(buffer, 0, len);
}
}
this.buffer = bout.toByteArray();
}
@Override
public void close() throws IOException {
buffer = null;
}
@Override
public int getPos() throws IOException {
if (buffer == null) {
throw new IOException("Closed");
}
return pos;
}
@Override
public int length() throws IOException {
if (buffer == null) {
throw new IOException("Closed");
}
return buffer.length;
}
@Override
public void seek(int off) throws IOException {
// RandomAccessIO requires EOF if we seek beyond stream bounds; ImageInputStream does not
if (buffer == null) {
throw new IOException("Closed");
}
if (pos > buffer.length || pos < 0) {
throw new EOFException();
}
pos = off;
}
@Override
public int read() throws IOException {
// RandomAccessIO requires an EOFException rather than a -1 return value
if (buffer == null) {
throw new IOException("Closed");
}
if (pos >= buffer.length || pos < 0) {
throw new EOFException();
}
return buffer[pos++] & 0xFF;
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
if (buffer == null) {
throw new IOException("Closed");
} else if (len > buffer.length - pos) {
throw new EOFException("Requested " + len + " but " + (buffer.length - pos) + " remaining");
}
System.arraycopy(buffer, pos, b, off, len);
pos += len;
}
@Override
public void write(int b) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void flush() throws IOException {
}
}
/**
* AbstractRandomAccessIO that proxies to a fixed-length ImageInputStream,
* so doesn't have to read anything into memory
*/
private static class FixedJ2KRandomAccessIO extends AbstractRandomAccessIO {
private final ImageInputStream in;
private final long start;
private boolean closed;
FixedJ2KRandomAccessIO(ImageInputStream in) throws IOException {
this.in = in;
this.start = in.getStreamPosition();
if (in.length() - this.start > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Can't support stream of " + in.length() + " bytes");
}
}
@Override
public void close() throws IOException {
closed = true;
// in.close(); No, don't proxy this.
}
@Override
public int getPos() throws IOException {
if (closed) {
throw new IOException("Closed");
}
return (int) (in.getStreamPosition() - start);
}
@Override
public int length() throws IOException {
if (closed) {
throw new IOException("Closed");
}
return (int) (in.length() - start);
}
@Override
public void seek(int off) throws IOException {
// RandomAccessIO requires EOF if we seek beyond stream bounds; ImageInputStream does not
if (closed) {
throw new IOException("Closed");
}
if (off < 0 || off > length()) {
throw new EOFException("off=" + off + " len=" + length());
}
in.seek(start + off);
}
@Override
public int read() throws IOException {
if (closed) {
throw new IOException("Closed");
}
int v = in.read();
if (v < 0) {
throw new EOFException();
}
return v;
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
if (closed) {
throw new IOException("Closed");
}
in.readFully(b, off, len);
}
@Override
public void write(int b) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void flush() throws IOException {
}
}
}

View file

@ -0,0 +1,56 @@
package org.xbib.graphics.jpeg2000.imageio;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Locale;
/**
* Checks for JPEG2000 and creates the nessesary Reader.
*/
public class JPEG2000ReaderSpi extends ImageReaderSpi {
private static final byte[] magicNumber = new byte[]
{
0, 0, 0, 12, 106, 80
};
@Override
public boolean canDecodeInput(Object source) throws IOException {
if (source instanceof ImageInputStream in) {
in.mark();
byte[] magicNumber = new byte[6];
in.readFully(magicNumber);
in.reset();
return Arrays.equals(JPEG2000ReaderSpi.magicNumber, magicNumber);
}
return false;
}
@Override
public Class[] getInputTypes() {
return new Class[]{ImageInputStream.class};
}
@Override
public ImageReader createReaderInstance(Object extension) throws IOException {
return new JPEG2000Reader(this);
}
@Override
public String[] getFormatNames() {
return new String[]{"jpeg2000", "JPEG2000"};
}
@Override
public String[] getFileSuffixes() {
return new String[]{"jp2", "j2k"};
}
@Override
public String getDescription(Locale locale) {
return "JPEG2000 ImageIO Support";
}
}

View file

@ -0,0 +1,346 @@
/**
* $RCSfile: IntegerSpec.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:58 $
* $State: Exp $
* <p>
* Class: IntegerSpec
* <p>
* Description: Holds specs corresponding to an Integer
* <p>
* <p>
* <p>
* COPYRIGHT:
* <p>
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
* <p>
* Copyright (c) 1999/2000 JJ2000 Partners.
*/
package org.xbib.graphics.jpeg2000.j2k;
import org.xbib.graphics.jpeg2000.J2KWriteParam;
import java.util.StringTokenizer;
/**
* This class extends ModuleSpec and is responsible of Integer
* specifications for each tile-component.
*
* @see ModuleSpec
* */
public class IntegerSpec extends ModuleSpec {
/** The largest value of type int */
protected static int MAX_INT = Integer.MAX_VALUE;
/**
* Constructs a new 'IntegerSpec' for the specified number of
* tiles and components and with allowed type of
* specifications. This constructor is normally called at decoder
* side.
*
* @param nt The number of tiles
*
* @param nc The number of components
*
* @param type The type of allowed specifications
*
**/
public IntegerSpec(int nt, int nc, byte type) {
super(nt, nc, type);
}
/**
* Constructs a new 'IntegerSpec' for the specified number of
* tiles and components, the allowed specifications type
* instance. This constructor is normally called at
* encoder side and parse arguments of specified option.
*
* @param nt The number of tiles
*
* @param nc The number of components
*
* @param type The allowed specifications type
**/
public IntegerSpec(int nt, int nc, byte type, J2KWriteParam wp, String values,
String defaultValue) {
super(nt, nc, type);
if (values == null) { // No parameter specified
try {
setDefault(Integer.valueOf(defaultValue));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Non recognized value" +
" for option -" +
": " + defaultValue);
}
return;
}
Integer value;
// Parse argument
StringTokenizer stk = new StringTokenizer(values);
String word; // current word
byte curSpecType = SPEC_DEF; // Specification type of the
// current parameter
boolean[] tileSpec = null; // Tiles concerned by the specification
boolean[] compSpec = null; // Components concerned by the specification
while (stk.hasMoreTokens()) {
word = stk.nextToken();
switch (word.charAt(0)) {
case 't': // Tiles specification
tileSpec = parseIdx(word, nTiles);
if (curSpecType == SPEC_COMP_DEF)
curSpecType = SPEC_TILE_COMP;
else
curSpecType = SPEC_TILE_DEF;
break;
case 'c': // Components specification
compSpec = parseIdx(word, nComp);
if (curSpecType == SPEC_TILE_DEF)
curSpecType = SPEC_TILE_COMP;
else
curSpecType = SPEC_COMP_DEF;
break;
default:
try {
value = Integer.valueOf(word);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Non recognized value" +
" for option -: " + word);
}
if (curSpecType == SPEC_DEF) {
setDefault(value);
} else if (curSpecType == SPEC_TILE_DEF) {
for (int i = tileSpec.length - 1; i >= 0; i--)
if (tileSpec[i]) {
setTileDef(i, value);
}
} else if (curSpecType == SPEC_COMP_DEF) {
for (int i = compSpec.length - 1; i >= 0; i--)
if (compSpec[i]) {
setCompDef(i, value);
}
} else {
for (int i = tileSpec.length - 1; i >= 0; i--) {
for (int j = compSpec.length - 1; j >= 0; j--) {
if (tileSpec[i] && compSpec[j]) {
setTileCompVal(i, j, value);
}
}
}
}
// Re-initialize
curSpecType = SPEC_DEF;
tileSpec = null;
compSpec = null;
break;
}
}
// Check that default value has been specified
if (getDefault() == null) {
int ndefspec = 0;
for (int t = nt - 1; t >= 0; t--) {
for (int c = nc - 1; c >= 0; c--) {
if (specValType[t][c] == SPEC_DEF) {
ndefspec++;
}
}
}
// If some tile-component have received no specification, it takes
// the default value
if (ndefspec != 0) {
try {
setDefault(Integer.valueOf(defaultValue));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Non recognized value" +
" for option - : " + defaultValue);
}
} else {
// All tile-component have been specified, takes the first
// tile-component value as default.
setDefault(getTileCompVal(0, 0));
switch (specValType[0][0]) {
case SPEC_TILE_DEF:
for (int c = nc - 1; c >= 0; c--) {
if (specValType[0][c] == SPEC_TILE_DEF)
specValType[0][c] = SPEC_DEF;
}
tileDef[0] = null;
break;
case SPEC_COMP_DEF:
for (int t = nt - 1; t >= 0; t--) {
if (specValType[t][0] == SPEC_COMP_DEF)
specValType[t][0] = SPEC_DEF;
}
compDef[0] = null;
break;
case SPEC_TILE_COMP:
specValType[0][0] = SPEC_DEF;
tileCompVal.put("t0c0", null);
break;
}
}
}
}
/**
* Get the maximum value of each tile-component
*
* @return The maximum value
*
*/
public int getMax() {
int max = ((Integer) def).intValue();
int tmp;
for (int t = 0; t < nTiles; t++) {
for (int c = 0; c < nComp; c++) {
tmp = ((Integer) getSpec(t, c)).intValue();
if (max < tmp)
max = tmp;
}
}
return max;
}
/**
* Get the minimum value of each tile-component
*
* @return The minimum value
*
*/
public int getMin() {
int min = ((Integer) def).intValue();
int tmp;
for (int t = 0; t < nTiles; t++) {
for (int c = 0; c < nComp; c++) {
tmp = ((Integer) getSpec(t, c)).intValue();
if (min > tmp)
min = tmp;
}
}
return min;
}
/**
* Get the maximum value of each tile for specified component
*
* @param c The component index
*
* @return The maximum value
*
*/
public int getMaxInComp(int c) {
int max = 0;
int tmp;
for (int t = 0; t < nTiles; t++) {
tmp = ((Integer) getSpec(t, c)).intValue();
if (max < tmp)
max = tmp;
}
return max;
}
/**
* Get the minimum value of each tile for specified component
*
* @param c The component index
*
* @return The minimum value
*
*/
public int getMinInComp(int c) {
int min = MAX_INT; // Big value
int tmp;
for (int t = 0; t < nTiles; t++) {
tmp = ((Integer) getSpec(t, c)).intValue();
if (min > tmp)
min = tmp;
}
return min;
}
/**
* Get the maximum value of each component in specified tile
*
* @param t The tile index
*
* @return The maximum value
*
*/
public int getMaxInTile(int t) {
int max = 0;
int tmp;
for (int c = 0; c < nComp; c++) {
tmp = ((Integer) getSpec(t, c)).intValue();
if (max < tmp)
max = tmp;
}
return max;
}
/**
* Get the minimum value of each component in specified tile
*
* @param t The tile index
*
* @return The minimum value
*
*/
public int getMinInTile(int t) {
int min = MAX_INT; // Big value
int tmp;
for (int c = 0; c < nComp; c++) {
tmp = ((Integer) getSpec(t, c)).intValue();
if (min > tmp)
min = tmp;
}
return min;
}
}

View file

@ -0,0 +1,89 @@
/*
* $RCSfile: JJ2KExceptionHandler.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:58 $
* $State: Exp $
*
* Class: JJ2KExceptionHandler
*
* Description: A class to handle exceptions
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k;
/**
* This class handles exceptions. It should be used in places where it
* is not known how to handle the exception, and the exception can not
* be thrown higher in the stack.
*
* <P>Different options can be registered for each Thread and
* ThreadGroup. <i>This feature is not implemented yet</i>
*/
public class JJ2KExceptionHandler {
/**
* Handles the exception. If no special action is registered for
* the current thread, then the Exception's stack trace and a
* descriptive message are printed to standard error and the
* current thread is stopped.
*
* <P><i>Registration of special actions is not implemented yet.</i>
*
* @param e The exception to handle
*/
public static void handleException(Throwable e) {
// Test if there is an special action (not implemented yet)
// If no special action
// Print the Exception message and stack to standard error
// including this method in the stack.
e.fillInStackTrace();
e.printStackTrace();
// Print an explicative message
System.err.println("The Thread is being terminated bacause an " +
"Exception (shown above)\n" +
"has been thrown and no special action was " +
"defined for this Thread.");
// Stop the thread (do not use stop, since it's deprecated in
// Java 1.2)
throw new ThreadDeath();
}
}

View file

@ -0,0 +1,98 @@
/*
* $RCSfile: JJ2KInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:58 $
* $State: Exp $
*
* Class: JJ2KInfo
*
* Description: Holds general JJ2000 informartion (version,
* copyright, etc.)
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k;
/**
* This class holds general JJ2000 information, such as the version number,
* copyright, contact address, etc.
*/
public class JJ2KInfo {
/**
* The version number (such as 2.0, 2.1.1, etc.)
*/
public static final String version = "4.1";
/**
* The copyright message string. Double newlines separate paragraphs.
* Newlines should be respected when displaying the message.
*/
public static final String copyright =
"This software module was originally developed by Raphaël Grosbois " +
"and Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); " +
"Joel Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, " +
"David Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon" +
" Research Centre " +
"France S.A) in the course of development of the JPEG 2000 standard " +
"as " +
"specified by ISO/IEC 15444 (JPEG 2000 Standard). This software " +
"module is an implementation of a part of the JPEG 2000 Standard. " +
"Swiss Federal Institute of Technology-EPFL, Ericsson Radio Systems " +
"AB and Canon Research Centre France S.A (collectively JJ2000 " +
"Partners) agree not to assert against ISO/IEC and users of the JPEG " +
"2000 Standard (Users) any of their rights under the copyright, not " +
"including other intellectual property rights, for this software " +
"module with respect to the usage by ISO/IEC and Users of this " +
"software module or modifications thereof for use in hardware or " +
"software products claiming conformance to the JPEG 2000 Standard. " +
"Those intending to use this software module in hardware or software " +
"products are advised that their use may infringe existing patents. " +
"The original developers of this software module, JJ2000 Partners " +
"and " +
"ISO/IEC assume no liability for use of this software module or " +
"modifications thereof. No license or right to this software module " +
"is granted for non JPEG 2000 Standard conforming products. JJ2000 " +
"Partners have full right to use this software module for his/her " +
"own purpose, assign or donate this software module to any third " +
"party and to inhibit third parties from using this software module " +
"for non JPEG 2000 Standard conforming products. This copyright " +
"notice must be included in all copies or derivative works of this " +
"software module.\n\nCopyright (c) 1999/2000 JJ2000 Partners.";
/**
* The bug reporting e-mail address
*/
public final static String bugaddr = "jj2000-bugs@ltssg3.epfl.ch";
}

View file

@ -0,0 +1,637 @@
/*
* $RCSfile: ModuleSpec.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:58 $
* $State: Exp $
*
* Class: ModuleSpec
*
* Description: Generic class for storing module specs
*
* from WTFilterSpec (Diego Santa Cruz)
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k;
import java.awt.*;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* This generic class is used to handle values to be used by a module for each
* tile and component. It uses attribute to determine which value to use. It
* should be extended by each module needing this feature.
* <p>
* This class might be used for values that are only tile specific or
* component specific but not both.
*
* <P>The attributes to use are defined by a hierarchy. The hierarchy is:
*
* <ul>
* <li> Tile and component specific attribute</li>
* <li> Tile specific default attribute</li>
* <li> Component main default attribute</li>
* <li> Main default attribute</li>
* </ul>
*/
public class ModuleSpec implements Cloneable {
/**
* The identifier for a specification module that applies only to
* components
*/
public final static byte SPEC_TYPE_COMP = 0;
/**
* The identifier for a specification module that applies only to
* tiles
*/
public final static byte SPEC_TYPE_TILE = 1;
/**
* The identifier for a specification module that applies both to
* tiles and components
*/
public final static byte SPEC_TYPE_TILE_COMP = 2;
/**
* The identifier for default specification
*/
public final static byte SPEC_DEF = 0;
/**
* The identifier for "component default" specification
*/
public final static byte SPEC_COMP_DEF = 1;
/**
* The identifier for "tile default" specification
*/
public final static byte SPEC_TILE_DEF = 2;
/**
* The identifier for a "tile-component" specification
*/
public final static byte SPEC_TILE_COMP = 3;
/**
* The type of the specification module
*/
protected int specType;
/**
* The number of tiles
*/
protected int nTiles = 0;
/**
* The number of components
*/
protected int nComp = 0;
/**
* The spec type for each tile-component. The first index is
* the tile index, the second is the component index.
*/
protected byte[][] specValType;
/**
* Default value for each tile-component
*/
protected Object def = null;
/**
* The default value for each component. Null if no component
* specific value is defined
*/
protected Object[] compDef = null;
/**
* The default value for each tile. Null if no tile specific
* value is defined
*/
protected Object[] tileDef = null;
/**
* The specific value for each tile-component. Value of tile 16 component
* 3 is accessible through the hash value "t16c3". Null if no
* tile-component specific value is defined
*/
protected Hashtable tileCompVal;
/**
* The specified value in string format
*/
protected String specified;
/**
* Constructs a 'ModuleSpec' object, initializing all the components and
* tiles to the 'SPEC_DEF' spec val type, for the specified number of
* components and tiles.
*
* @param nt The number of tiles
* @param nc The number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both.
*/
public ModuleSpec(int nt, int nc, byte type) {
nTiles = nt;
nComp = nc;
specValType = new byte[nt][nc];
switch (type) {
case SPEC_TYPE_TILE:
specType = SPEC_TYPE_TILE;
break;
case SPEC_TYPE_COMP:
specType = SPEC_TYPE_COMP;
break;
case SPEC_TYPE_TILE_COMP:
specType = SPEC_TYPE_TILE_COMP;
break;
}
}
/**
* This method is responsible of parsing tile indexes set and
* component indexes set for an option. Such an argument must
* follow the following policy:<br>
*
* <tt>t&lt;indexes set&gt;</tt> or <tt>c&lt;indexes set&gt;</tt> where
* tile or component indexes are separated by commas or a
* dashes.
*
* <p><u>Example:</u><br>
* <li> <tt>t0,3,4</tt> means tiles with indexes 0, 3 and 4.<br>
* <li> <tt>t2-4</tt> means tiles with indexes 2,3 and 4.<br>
* <p>
* It returns a boolean array skteching which tile or component are
* concerned by the next parameters.
*
* @param word The word to parse.
* @param maxIdx Maximum authorized index
* @return Indexes concerned by this parameter.
*/
public static final boolean[] parseIdx(String word, int maxIdx) {
int nChar = word.length(); // Number of characters
char c = word.charAt(0); // current character
int idx = -1; // Current (tile or component) index
int lastIdx = -1; // Last (tile or component) index
boolean isDash = false; // Whether or not last separator was a dash
boolean[] idxSet = new boolean[maxIdx];
int i = 1; // index of the current character
while (i < nChar) {
c = word.charAt(i);
if (Character.isDigit(c)) {
if (idx == -1)
idx = 0;
idx = idx * 10 + (c - '0');
} else {
if (idx == -1 || (c != ',' && c != '-')) {
throw new IllegalArgumentException("Bad construction for " +
"parameter: " + word);
}
if (idx < 0 || idx >= maxIdx) {
throw new IllegalArgumentException("Out of range index in " +
"parameter `" + word + "' : " +
+idx);
}
// Found a comma
if (c == ',') {
if (isDash) { // Previously found a dash, fill idxSet
for (int j = lastIdx + 1; j < idx; j++) {
idxSet[j] = true;
}
}
isDash = false;
} else // Found a dash
isDash = true;
// Udate idxSet
idxSet[idx] = true;
lastIdx = idx;
idx = -1;
}
i++;
}
// Process last found index
if (idx < 0 || idx >= maxIdx) {
throw new IllegalArgumentException("Out of range index in " +
"parameter `" + word + "' : " + idx);
}
if (isDash)
for (int j = lastIdx + 1; j < idx; j++) {
idxSet[j] = true;
}
idxSet[idx] = true;
return idxSet;
}
public ModuleSpec getCopy() {
return (ModuleSpec) this.clone();
}
protected Object clone() {
ModuleSpec ms;
try {
ms = (ModuleSpec) super.clone();
} catch (CloneNotSupportedException e) {
throw new Error("Error when cloning ModuleSpec instance");
}
// Create a copy of the specValType array
ms.specValType = new byte[nTiles][nComp];
for (int t = 0; t < nTiles; t++) {
System.arraycopy(specValType[t], 0, ms.specValType[t], 0, nComp);
}
// Create a copy of tileDef
if (tileDef != null) {
ms.tileDef = new Object[nTiles];
System.arraycopy(tileDef, 0, ms.tileDef, 0, nTiles);
}
// Create a copy of tileCompVal
if (tileCompVal != null) {
ms.tileCompVal = new Hashtable();
String tmpKey;
Object tmpVal;
for (Enumeration e = tileCompVal.keys(); e.hasMoreElements(); ) {
tmpKey = (String) e.nextElement();
tmpVal = tileCompVal.get(tmpKey);
ms.tileCompVal.put(tmpKey, tmpVal);
}
}
return ms;
}
/**
* Rotate the ModuleSpec instance by 90 degrees (this modifies only tile
* and tile-component specifications).
*
* @param anT Number of tiles along horizontal and vertical axis after
* rotation.
*/
public void rotate90(Point anT) {
// Rotate specValType
byte[][] tmpsvt = new byte[nTiles][];
int ax, ay;
Point bnT = new Point(anT.y, anT.x);
for (int by = 0; by < bnT.y; by++) {
for (int bx = 0; bx < bnT.x; bx++) {
ay = bx;
ax = bnT.y - by - 1;
tmpsvt[ay * anT.x + ax] = specValType[by * bnT.x + bx];
}
}
specValType = tmpsvt;
// Rotate tileDef
if (tileDef != null) {
Object[] tmptd = new Object[nTiles];
for (int by = 0; by < bnT.y; by++) {
for (int bx = 0; bx < bnT.x; bx++) {
ay = bx;
ax = bnT.y - by - 1;
tmptd[ay * anT.x + ax] = tileDef[by * bnT.x + bx];
}
}
tileDef = tmptd;
}
// Rotate tileCompVal
if (tileCompVal != null && tileCompVal.size() > 0) {
Hashtable tmptcv = new Hashtable();
String tmpKey;
Object tmpVal;
int btIdx, atIdx;
int i1, i2;
int bx, by;
for (Enumeration e = tileCompVal.keys(); e.hasMoreElements(); ) {
tmpKey = (String) e.nextElement();
tmpVal = tileCompVal.get(tmpKey);
i1 = tmpKey.indexOf('t');
i2 = tmpKey.indexOf('c');
btIdx = (Integer.valueOf(tmpKey.substring(i1 + 1, i2))).intValue();
bx = btIdx % bnT.x;
by = btIdx / bnT.x;
ay = bx;
ax = bnT.y - by - 1;
atIdx = ax + ay * anT.x;
tmptcv.put("t" + atIdx + tmpKey.substring(i2), tmpVal);
}
tileCompVal = tmptcv;
}
}
/**
* Gets default value for this module.
*
* @return The default value (Must be casted before use)
*/
public Object getDefault() {
return def;
}
/**
* Sets default value for this module
*/
public void setDefault(Object value) {
def = value;
}
/**
* Sets default value for specified component and specValType tag if
* allowed by its priority.
*
* @param c Component index
*/
public void setCompDef(int c, Object value) {
if (specType == SPEC_TYPE_TILE) {
String errMsg = "Option whose value is '" + value + "' cannot be "
+ "specified for components as it is a 'tile only' specific "
+ "option";
throw new IllegalArgumentException(errMsg);
}
if (compDef == null)
compDef = new Object[nComp];
for (int i = 0; i < nTiles; i++) {
if (specValType[i][c] < SPEC_COMP_DEF) {
specValType[i][c] = SPEC_COMP_DEF;
}
}
compDef[c] = value;
}
/**
* Gets default value of the specified component. If no specification have
* been entered for this component, returns default value.
*
* @param c Component index
* @return The default value for this component (Must be casted before
* use)
* @see #setCompDef
*/
public Object getCompDef(int c) {
if (specType == SPEC_TYPE_TILE) {
throw new IllegalArgumentException("Illegal use of ModuleSpec class");
}
if (compDef == null || compDef[c] == null) {
return getDefault();
} else
return compDef[c];
}
/**
* Sets default value for specified tile and specValType tag if
* allowed by its priority.
*
* @param t Tile index.
*/
public void setTileDef(int t, Object value) {
if (specType == SPEC_TYPE_COMP) {
String errMsg = "Option whose value is '" + value + "' cannot be "
+ "specified for tiles as it is a 'component only' specific "
+ "option";
throw new IllegalStateException(errMsg);
}
if (tileDef == null)
tileDef = new Object[nTiles];
for (int i = 0; i < nComp; i++) {
if (specValType[t][i] < SPEC_TILE_DEF) {
specValType[t][i] = SPEC_TILE_DEF;
}
}
tileDef[t] = value;
}
/**
* Gets default value of the specified tile. If no specification
* has been entered, it returns the default value.
*
* @param t Tile index
* @return The default value for this tile (Must be casted before use)
* @see #setTileDef
*/
public Object getTileDef(int t) {
if (specType == SPEC_TYPE_COMP) {
throw new IllegalStateException("Illegal use of ModuleSpec class");
}
if (tileDef == null || tileDef[t] == null) {
return getDefault();
} else
return tileDef[t];
}
/**
* Sets value for specified tile-component.
*
* @param t Tie index
* @param c Component index
*/
public void setTileCompVal(int t, int c, Object value) {
if (specType != SPEC_TYPE_TILE_COMP) {
String errMsg = "Option whose value is '" + value + "' cannot be "
+ "specified for ";
switch (specType) {
case SPEC_TYPE_TILE:
errMsg += "components as it is a 'tile only' specific option";
break;
case SPEC_TYPE_COMP:
errMsg += "tiles as it is a 'component only' specific option";
break;
}
throw new IllegalStateException(errMsg);
}
if (tileCompVal == null)
tileCompVal = new Hashtable();
specValType[t][c] = SPEC_TILE_COMP;
tileCompVal.put("t" + t + "c" + c, value);
}
/**
* Gets value of specified tile-component. This method calls getSpec but
* has a public access.
*
* @param t Tile index
* @param c Component index
* @return The value of this tile-component (Must be casted before use)
* @see #setTileCompVal
* @see #getSpec
*/
public Object getTileCompVal(int t, int c) {
if (specType != SPEC_TYPE_TILE_COMP) {
throw new IllegalStateException("Illegal use of ModuleSpec class");
}
return getSpec(t, c);
}
/**
* Gets value of specified tile-component without knowing if a
* specific tile-component value has been previously entered. It
* first check if a tile-component specific value has been
* entered, then if a tile specific value exist, then if a
* component specific value exist. If not the default value is
* returned.
*
* @param t Tile index
* @param c Component index
* @return Value for this tile component.
*/
protected Object getSpec(int t, int c) {
switch (specValType[t][c]) {
case SPEC_DEF:
return getDefault();
case SPEC_COMP_DEF:
return getCompDef(c);
case SPEC_TILE_DEF:
return getTileDef(t);
case SPEC_TILE_COMP:
return tileCompVal.get("t" + t + "c" + c);
default:
throw new IllegalArgumentException("Not recognized spec type");
}
}
/**
* Return the spec type of the given tile-component.
*
* @param t Tile index
* @param c Component index
*/
public byte getSpecValType(int t, int c) {
return specValType[t][c];
}
/**
* Whether or not specifications have been entered for the given
* component.
*
* @param c Index of the component
* @return True if component specification has been defined
*/
public boolean isCompSpecified(int c) {
return compDef != null && compDef[c] != null;
}
/**
* Whether or not specifications have been entered for the given
* tile.
*
* @param t Index of the tile
* @return True if tile specification has been entered
*/
public boolean isTileSpecified(int t) {
return tileDef != null && tileDef[t] != null;
}
/**
* Whether or not a tile-component specification has been defined
*
* @param t Tile index
* @param c Component index
* @return True if a tile-component specification has been defined.
*/
public boolean isTileCompSpecified(int t, int c) {
return tileCompVal != null && tileCompVal.get("t" + t + "c" + c) != null;
}
/**
* Returns a tile-component representative using default value.
*
* @return Tile component index in an array (first element: tile
* index, second element: component index).
* */
/*
public int[] getDefRep(){
int[] tcidx = new int[2];
for(int t=nTiles-1; t>=0; t--){
for(int c=nComp-1; c>=0; c--){
if(specValType[t][c]==SPEC_DEF){
tcidx[0] = t;
tcidx[1] = c;
return tcidx;
}
}
}
throw new IllegalArgumentException("No representative for "+
"default value");
}
*/
/**
* Returns a component representative using tile default value.
*
* @param t Tile index
*
* @return component index of the representant
* */
/*
public int getTileDefRep(int t){
for(int c=nComp-1; c>=0; c--)
if(specValType[t][c]==SPEC_TILE_DEF){
return c;
}
throw new IllegalArgumentException("No representative for tile "+
"default value");
}
*/
/**
* Returns a tile representative using component default value.
*
* @param c Component index
*
* @return tile index of the representant
* */
/*
public int getCompDefRep(int c){
for(int t=nTiles-1; t>=0; t--) {
if(specValType[t][c]==SPEC_COMP_DEF){
return t;
}
}
throw new IllegalArgumentException("No representative for component "+
"default value, c="+c);
}
*/
/*
public String getSpecified() {
return specified;
}
*/
}

View file

@ -0,0 +1,78 @@
/*
* $RCSfile: NoNextElementException.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:58 $
* $State: Exp $
*
* Class: NoNextElementException
*
* Description: Exception to indicate that there is no next
* element.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k;
/**
* This exception is thrown whenever a next???? method is called and
* there is no next element to return.
*/
public class NoNextElementException extends RuntimeException {
/**
* Constructs a new <tt>NoNextElementException</tt> exception with no
* detail message.
*/
public NoNextElementException() {
super();
}
/**
* Constructs a new <tt>NoNextElementException</tt> exception with
* the specified detail message.
*
* @param s The detail message.
*/
public NoNextElementException(String s) {
super(s);
}
}

View file

@ -0,0 +1,87 @@
/*
* $RCSfile: NotImplementedError.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:59 $
* $State: Exp $
*
* Class: NotImplementedError
*
* Description: Exception that is thrown whenever a non-implemented
* method is called.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k;
/**
* This exception is thrown whenever a feature or functionality that
* has not been implemented is calle.
*
* <P>Its purpose it is to ease the development and testing process. A
* class that partially implements its functionality should throw a
* <tt>NotImplementedError</tt> when a method that has not yet
* been implemented is called.
*
* <P>This class is made a subclass of <tt>Error</tt> since it should
* never be caught by an application. There is no need to declare this
* exception in the <tt>throws</tt> clause of a method.
*
* @see Error
*/
public class NotImplementedError extends Error {
/**
* Constructs a new <tt>NotImplementedError</tt> exception with
* the default detail message. The message is:
*
* <P><I>The called method has not been implemented yet. Sorry!</I>
*/
public NotImplementedError() {
super("The called method has not been implemented yet. Sorry!");
}
/**
* Constructs a new <tt>NotImplementedError</tt> exception with
* the specified detail message <tt>m</tt>.
*
* @param m The detail message to use
*/
public NotImplementedError(String m) {
super(m);
}
}

View file

@ -0,0 +1,237 @@
/*
* $RCSfile: StringSpec.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:59 $
* $State: Exp $
*
* Class: StringSpec
*
* Description: String specification for an option
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k;
import org.xbib.graphics.jpeg2000.J2KWriteParam;
import java.util.StringTokenizer;
/**
* This class extends ModuleSpec class in order to hold tile-component
* specifications using Strings.
*
* @see ModuleSpec
*/
public class StringSpec extends ModuleSpec {
private String specified;
/**
* Constructs an empty 'StringSpec' with specified number of
* tile and components. This constructor is called by the decoder.
*
* @param nt Number of tiles
* @param nc Number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both.
*/
public StringSpec(int nt, int nc, byte type) {
super(nt, nc, type);
}
/**
* Constructs a new 'StringSpec' for the specified number of
* components:tiles and the arguments of <tt>optName</tt>
* option. This constructor is called by the encoder. It also
* checks that the arguments belongs to the recognized arguments
* list.
*
* <P><u>Note:</u> The arguments must not start with 't' or 'c'
* since it is reserved for respectively tile and components
* indexes specification.
*
* @param nt The number of tiles
* @param nc The number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both.
* @param list The list of all recognized argument in a String array
*/
public StringSpec(int nt, int nc, byte type, String defaultValue,
String[] list, J2KWriteParam wp, String values) {
super(nt, nc, type);
specified = values;
boolean recognized = false;
String param = values;
if (values == null) {
for (int i = list.length - 1; i >= 0; i--)
if (defaultValue.equalsIgnoreCase(list[i])) {
recognized = true;
break;
}
if (!recognized)
throw new IllegalArgumentException("Default parameter of " +
"option - not" +
" recognized: " + defaultValue);
setDefault(defaultValue);
return;
}
// Parse argument
StringTokenizer stk = new StringTokenizer(specified);
String word; // current word
byte curSpecType = SPEC_DEF; // Specification type of the
// current parameter
boolean[] tileSpec = null; // Tiles concerned by the
// specification
boolean[] compSpec = null; // Components concerned by the specification
Boolean value;
while (stk.hasMoreTokens()) {
word = stk.nextToken();
if (word.matches("t[0-9]*")) {
tileSpec = parseIdx(word, nTiles);
if (curSpecType == SPEC_COMP_DEF) {
curSpecType = SPEC_TILE_COMP;
} else {
curSpecType = SPEC_TILE_DEF;
}
} else if (word.matches("c[0-9]*")) {
compSpec = parseIdx(word, nComp);
if (curSpecType == SPEC_TILE_DEF) {
curSpecType = SPEC_TILE_COMP;
} else
curSpecType = SPEC_COMP_DEF;
} else {
recognized = false;
for (int i = list.length - 1; i >= 0; i--)
if (word.equalsIgnoreCase(list[i])) {
recognized = true;
break;
}
if (!recognized)
throw new IllegalArgumentException("Default parameter of " +
"option not" +
" recognized: " + word);
if (curSpecType == SPEC_DEF) {
setDefault(word);
} else if (curSpecType == SPEC_TILE_DEF) {
for (int i = tileSpec.length - 1; i >= 0; i--)
if (tileSpec[i]) {
setTileDef(i, word);
}
} else if (curSpecType == SPEC_COMP_DEF) {
for (int i = compSpec.length - 1; i >= 0; i--)
if (compSpec[i]) {
setCompDef(i, word);
}
} else {
for (int i = tileSpec.length - 1; i >= 0; i--) {
for (int j = compSpec.length - 1; j >= 0; j--) {
if (tileSpec[i] && compSpec[j]) {
setTileCompVal(i, j, word);
}
}
}
}
// Re-initialize
curSpecType = SPEC_DEF;
tileSpec = null;
compSpec = null;
}
}
// Check that default value has been specified
if (getDefault() == null) {
int ndefspec = 0;
for (int t = nt - 1; t >= 0; t--) {
for (int c = nc - 1; c >= 0; c--) {
if (specValType[t][c] == SPEC_DEF) {
ndefspec++;
}
}
}
// If some tile-component have received no specification, it takes
// the default value defined in ParameterList
if (ndefspec != 0) {
param = defaultValue;
for (int i = list.length - 1; i >= 0; i--)
if (param.equalsIgnoreCase(list[i])) {
recognized = true;
break;
}
if (!recognized)
throw new IllegalArgumentException("Default parameter of " +
"option not" +
" recognized: " + specified);
setDefault(param);
} else {
// All tile-component have been specified, takes the first
// tile-component value as default.
setDefault(getSpec(0, 0));
switch (specValType[0][0]) {
case SPEC_TILE_DEF:
for (int c = nc - 1; c >= 0; c--) {
if (specValType[0][c] == SPEC_TILE_DEF)
specValType[0][c] = SPEC_DEF;
}
tileDef[0] = null;
break;
case SPEC_COMP_DEF:
for (int t = nt - 1; t >= 0; t--) {
if (specValType[t][0] == SPEC_COMP_DEF)
specValType[t][0] = SPEC_DEF;
}
compDef[0] = null;
break;
case SPEC_TILE_COMP:
specValType[0][0] = SPEC_DEF;
tileCompVal.put("t0c0", null);
break;
}
}
}
}
public String getSpecified() {
return specified;
}
}

View file

@ -0,0 +1,84 @@
/*
* $RCSfile: CBlkCoordInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:59 $
* $State: Exp $
*
* Class: CBlkCoordInfo
*
* Description: Used to store the code-blocks coordinates.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream;
import java.awt.*;
/**
* This class is used to store the coordinates of code-blocks.
*/
public class CBlkCoordInfo extends CoordInfo {
/**
* The code-block horizontal and vertical indexes
*/
public Point idx;
/**
* Constructor. Creates a CBlkCoordInfo object.
*/
public CBlkCoordInfo() {
this.idx = new Point();
}
/**
* Constructor. Creates a CBlkCoordInfo object width specified code-block
* vertical and horizontal indexes.
*
* @param m Code-block vertical index.
* @param n Code-block horizontal index.
*/
public CBlkCoordInfo(int m, int n) {
this.idx = new Point(n, m);
}
/**
* Returns code-block's information in a String
*
* @return String with code-block's information
*/
public String toString() {
return super.toString() + ", idx=" + idx;
}
}

View file

@ -0,0 +1,106 @@
/*
* $RCSfile: CoordInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:59 $
* $State: Exp $
*
* Class: CoordInfo
*
* Description: Used to store the coordinates of code-blocks/packets
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream;
/**
* This class is used to store the coordinates of objects such as code-blocks
* or precincts. As this is an abstract class, it cannot be used directly but
* derived classes have been created for code-blocks and packets
* (CBlkCoordInfo and PrecCoordInfo).
*
* @see PrecCoordInfo
* @see CBlkCoordInfo
*/
public abstract class CoordInfo {
/**
* Horizontal upper left coordinate in the subband
*/
public int ulx;
/**
* Vertical upper left coordinate in the subband
*/
public int uly;
/**
* Object's width
*/
public int w;
/**
* Object's height
*/
public int h;
/**
* Constructor. Creates a CoordInfo object.
*
* @param ulx The horizontal upper left coordinate in the subband
* @param uly The vertical upper left coordinate in the subband
* @param w The width
* @param h The height
*/
public CoordInfo(int ulx, int uly, int w, int h) {
this.ulx = ulx;
this.uly = uly;
this.w = w;
this.h = h;
}
/**
* Empty contructor
*/
public CoordInfo() {
}
/**
* Returns object's information in a String
*
* @return String with object's information
*/
public String toString() {
return "ulx=" + ulx + ", uly=" + uly + ", w=" + w + ", h=" + h;
}
}

View file

@ -0,0 +1,79 @@
/*
* $RCSfile: CorruptedCodestreamException.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:01:59 $
* $State: Exp $
*
* Class: CorruptedCodestreamException
*
* Description: Exception thrown when illegal bit stream
* values are decoded.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.codestream;
import java.io.IOException;
/**
* Thsi exception is thrown whenever an illegal value is read from a
* bit stream. The cause can be either a corrupted bit stream, or a a
* bit stream which is illegal.
*/
public class CorruptedCodestreamException extends IOException {
/**
* Constructs a new <tt>CorruptedCodestreamException</tt> exception
* with no detail message.
*/
public CorruptedCodestreamException() {
super();
}
/**
* Constructs a new <tt>CorruptedCodestreamException</tt> exception
* with the specified detail message.
*
* @param s The detail message.
*/
public CorruptedCodestreamException(String s) {
super(s);
}
}

View file

@ -0,0 +1,997 @@
/*
* $RCSfile: HeaderInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:00 $
* $State: Exp $
*
* Class: HeaderInfo
*
* Description: Holds information found in main and tile-part
* headers
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream;
import org.xbib.graphics.jpeg2000.j2k.wavelet.FilterTypes;
import java.util.Hashtable;
/**
* Classe that holds information found in the marker segments of the main and
* tile-part headers. There is one inner-class per marker segment type found
* in these headers.
*/
public class HeaderInfo implements Markers, ProgressionType, FilterTypes,
Cloneable {
/**
* Reference to the SIZ marker segment found in main header
*/
public SIZ siz;
/**
* Reference to the SOT marker segments found in tile-part headers. The
* kwy is given by "t"+tileIdx"_tp"+tilepartIndex.
*/
public Hashtable sot = new Hashtable();
/**
* Reference to the COD marker segments found in main and first tile-part
* header. The key is either "main" or "t"+tileIdx.
*/
public Hashtable cod = new Hashtable();
/**
* Reference to the COC marker segments found in main and first tile-part
* header. The key is either "main_c"+componentIndex or
* "t"+tileIdx+"_c"+component_index.
*/
public Hashtable coc = new Hashtable();
/**
* Reference to the RGN marker segments found in main and first tile-part
* header. The key is either "main_c"+componentIndex or
* "t"+tileIdx+"_c"+component_index.
*/
public Hashtable rgn = new Hashtable();
/**
* Reference to the QCD marker segments found in main and first tile-part
* header. The key is either "main" or "t"+tileIdx.
*/
public Hashtable qcd = new Hashtable();
/**
* Reference to the QCC marker segments found in main and first tile-part
* header. They key is either "main_c"+componentIndex or
* "t"+tileIdx+"_c"+component_index.
*/
public Hashtable qcc = new Hashtable();
/**
* Reference to the POC marker segments found in main and first tile-part
* header. They key is either "main" or "t"+tileIdx.
*/
public Hashtable poc = new Hashtable();
/**
* Reference to the CRG marker segment found in main header
*/
public CRG crg;
/**
* Reference to the COM marker segments found in main and tile-part
* headers. The key is either "main_"+comIdx or "t"+tileIdx+"_"+comIdx.
*/
public Hashtable com = new Hashtable();
/**
* Number of found COM marker segment
*/
private int ncom = 0;
/**
* Returns a new instance of SIZ
*/
public SIZ getNewSIZ() {
return new SIZ();
}
/**
* Returns a new instance of SOT
*/
public SOT getNewSOT() {
return new SOT();
}
/**
* Returns a new instance of COD
*/
public COD getNewCOD() {
return new COD();
}
/**
* Returns a new instance of COC
*/
public COC getNewCOC() {
return new COC();
}
/**
* Returns a new instance of RGN
*/
public RGN getNewRGN() {
return new RGN();
}
/**
* Returns a new instance of QCD
*/
public QCD getNewQCD() {
return new QCD();
}
/**
* Returns a new instance of QCC
*/
public QCC getNewQCC() {
return new QCC();
}
/**
* Returns a new instance of POC
*/
public POC getNewPOC() {
return new POC();
}
/**
* Returns a new instance of CRG
*/
public CRG getNewCRG() {
return new CRG();
}
/**
* Returns a new instance of COM
*/
public COM getNewCOM() {
ncom++;
return new COM();
}
/**
* Returns the number of found COM marker segments
*/
public int getNumCOM() {
return ncom;
}
/**
* Display information found in the different marker segments of the main
* header
*/
public String toStringMainHeader() {
int nc = siz.csiz;
// SIZ
String str = "" + siz;
// COD
if (cod.get("main") != null) {
str += "" + cod.get("main");
}
// COCs
for (int c = 0; c < nc; c++) {
if (coc.get("main_c" + c) != null) {
str += "" + coc.get("main_c" + c);
}
}
// QCD
if (qcd.get("main") != null) {
str += "" + qcd.get("main");
}
// QCCs
for (int c = 0; c < nc; c++) {
if (qcc.get("main_c" + c) != null) {
str += "" + qcc.get("main_c" + c);
}
}
// RGN
for (int c = 0; c < nc; c++) {
if (rgn.get("main_c" + c) != null) {
str += "" + rgn.get("main_c" + c);
}
}
// POC
if (poc.get("main") != null) {
str += "" + poc.get("main");
}
// CRG
if (crg != null) {
str += "" + crg;
}
// COM
for (int i = 0; i < ncom; i++) {
if (com.get("main_" + i) != null) {
str += "" + com.get("main_" + i);
}
}
return str;
}
/**
* Returns information found in the tile-part headers of a given tile.
*
* @param t index of the tile
* @param ntp Number of tile-parts
*/
public String toStringTileHeader(int t, int ntp) {
int nc = siz.csiz;
String str = "";
// SOT
for (int i = 0; i < ntp; i++) {
str += "Tile-part " + i + ", tile " + t + ":\n";
str += "" + sot.get("t" + t + "_tp" + i);
}
// COD
if (cod.get("t" + t) != null) {
str += "" + cod.get("t" + t);
}
// COCs
for (int c = 0; c < nc; c++) {
if (coc.get("t" + t + "_c" + c) != null) {
str += "" + coc.get("t" + t + "_c" + c);
}
}
// QCD
if (qcd.get("t" + t) != null) {
str += "" + qcd.get("t" + t);
}
// QCCs
for (int c = 0; c < nc; c++) {
if (qcc.get("t" + t + "_c" + c) != null) {
str += "" + qcc.get("t" + t + "_c" + c);
}
}
// RGN
for (int c = 0; c < nc; c++) {
if (rgn.get("t" + t + "_c" + c) != null) {
str += "" + rgn.get("t" + t + "_c" + c);
}
}
// POC
if (poc.get("t" + t) != null) {
str += "" + poc.get("t" + t);
}
return str;
}
/**
* Returns information found in the tile-part headers of a given tile
* exception the SOT marker segment.
*
* @param t index of the tile
* @param ntp Number of tile-parts
*/
public String toStringThNoSOT(int t, int ntp) {
int nc = siz.csiz;
String str = "";
// COD
if (cod.get("t" + t) != null) {
str += "" + cod.get("t" + t);
}
// COCs
for (int c = 0; c < nc; c++) {
if (coc.get("t" + t + "_c" + c) != null) {
str += "" + coc.get("t" + t + "_c" + c);
}
}
// QCD
if (qcd.get("t" + t) != null) {
str += "" + qcd.get("t" + t);
}
// QCCs
for (int c = 0; c < nc; c++) {
if (qcc.get("t" + t + "_c" + c) != null) {
str += "" + qcc.get("t" + t + "_c" + c);
}
}
// RGN
for (int c = 0; c < nc; c++) {
if (rgn.get("t" + t + "_c" + c) != null) {
str += "" + rgn.get("t" + t + "_c" + c);
}
}
// POC
if (poc.get("t" + t) != null) {
str += "" + poc.get("t" + t);
}
return str;
}
/**
* Returns a copy of this object
*/
public HeaderInfo getCopy(int nt) {
HeaderInfo nhi = null;
// SIZ
try {
nhi = (HeaderInfo) clone();
} catch (CloneNotSupportedException e) {
throw new Error("Cannot clone HeaderInfo instance");
}
nhi.siz = siz.getCopy();
// COD
if (cod.get("main") != null) {
COD ms = (COD) cod.get("main");
nhi.cod.put("main", ms.getCopy());
}
for (int t = 0; t < nt; t++) {
if (cod.get("t" + t) != null) {
COD ms = (COD) cod.get("t" + t);
nhi.cod.put("t" + t, ms.getCopy());
}
}
return nhi;
}
/**
* Internal class holding information found in the SIZ marker segment
*/
public class SIZ implements Cloneable {
public int lsiz;
public int rsiz;
public int xsiz;
public int ysiz;
public int x0siz;
public int y0siz;
public int xtsiz;
public int ytsiz;
public int xt0siz;
public int yt0siz;
public int csiz;
public int[] ssiz;
public int[] xrsiz;
public int[] yrsiz;
/**
* Component widths
*/
private int[] compWidth = null;
/**
* Maximum width among all components
*/
private int maxCompWidth = -1;
/**
* Component heights
*/
private int[] compHeight = null;
/**
* Maximum height among all components
*/
private int maxCompHeight = -1;
private int numTiles = -1;
private boolean[] origSigned = null;
private int[] origBitDepth = null;
/**
* Width of the specified tile-component
*
* @param c Component index
*/
public int getCompImgWidth(int c) {
if (compWidth == null) {
compWidth = new int[csiz];
for (int cc = 0; cc < csiz; cc++) {
compWidth[cc] =
(int) (Math.ceil((xsiz) / (double) xrsiz[cc])
- Math.ceil(x0siz / (double) xrsiz[cc]));
}
}
return compWidth[c];
}
public int getMaxCompWidth() {
if (compWidth == null) {
compWidth = new int[csiz];
for (int cc = 0; cc < csiz; cc++) {
compWidth[cc] =
(int) (Math.ceil((xsiz) / (double) xrsiz[cc])
- Math.ceil(x0siz / (double) xrsiz[cc]));
}
}
if (maxCompWidth == -1) {
for (int c = 0; c < csiz; c++) {
if (compWidth[c] > maxCompWidth) {
maxCompWidth = compWidth[c];
}
}
}
return maxCompWidth;
}
public int getCompImgHeight(int c) {
if (compHeight == null) {
compHeight = new int[csiz];
for (int cc = 0; cc < csiz; cc++) {
compHeight[cc] =
(int) (Math.ceil((ysiz) / (double) yrsiz[cc])
- Math.ceil(y0siz / (double) yrsiz[cc]));
}
}
return compHeight[c];
}
public int getMaxCompHeight() {
if (compHeight == null) {
compHeight = new int[csiz];
for (int cc = 0; cc < csiz; cc++) {
compHeight[cc] =
(int) (Math.ceil((ysiz) / (double) yrsiz[cc])
- Math.ceil(y0siz / (double) yrsiz[cc]));
}
}
if (maxCompHeight == -1) {
for (int c = 0; c < csiz; c++) {
if (compHeight[c] != maxCompHeight) {
maxCompHeight = compHeight[c];
}
}
}
return maxCompHeight;
}
public int getNumTiles() {
if (numTiles == -1) {
numTiles = ((xsiz - xt0siz + xtsiz - 1) / xtsiz) *
((ysiz - yt0siz + ytsiz - 1) / ytsiz);
}
return numTiles;
}
public boolean isOrigSigned(int c) {
if (origSigned == null) {
origSigned = new boolean[csiz];
for (int cc = 0; cc < csiz; cc++) {
origSigned[cc] = ((ssiz[cc] >>> SSIZ_DEPTH_BITS) == 1);
}
}
return origSigned[c];
}
public int getOrigBitDepth(int c) {
if (origBitDepth == null) {
origBitDepth = new int[csiz];
for (int cc = 0; cc < csiz; cc++) {
origBitDepth[cc] = (ssiz[cc] & ((1 << SSIZ_DEPTH_BITS) - 1)) + 1;
}
}
return origBitDepth[c];
}
public SIZ getCopy() {
SIZ ms = null;
try {
ms = (SIZ) this.clone();
} catch (CloneNotSupportedException e) {
throw new Error("Cannot clone SIZ marker segment");
}
return ms;
}
/**
* Display information found in SIZ marker segment
*/
public String toString() {
String str = "\n --- SIZ (" + lsiz + " bytes) ---\n";
str += " Capabilities : " + rsiz + "\n";
str += " Image dim. : " + (xsiz - x0siz) + "x" + (ysiz - y0siz) + ", (off=" +
x0siz + "," + y0siz + ")\n";
str += " Tile dim. : " + xtsiz + "x" + ytsiz + ", (off=" + xt0siz + "," +
yt0siz + ")\n";
str += " Component(s) : " + csiz + "\n";
str += " Orig. depth : ";
for (int i = 0; i < csiz; i++) {
str += getOrigBitDepth(i) + " ";
}
str += "\n";
str += " Orig. signed : ";
for (int i = 0; i < csiz; i++) {
str += isOrigSigned(i) + " ";
}
str += "\n";
str += " Subs. factor : ";
for (int i = 0; i < csiz; i++) {
str += xrsiz[i] + "," + yrsiz[i] + " ";
}
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the SOt marker segments
*/
public class SOT {
public int lsot;
public int isot;
public int psot;
public int tpsot;
public int tnsot;
/**
* Display information found in this SOT marker segment
*/
public String toString() {
String str = "\n --- SOT (" + lsot + " bytes) ---\n";
str += "Tile index : " + isot + "\n";
str += "Tile-part length : " + psot + " bytes\n";
str += "Tile-part index : " + tpsot + "\n";
str += "Num. of tile-parts : " + tnsot + "\n";
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the COD marker segments
*/
public class COD implements Cloneable {
public int lcod;
public int scod;
public int sgcod_po; // Progression order
public int sgcod_nl; // Number of layers
public int sgcod_mct; // Multiple component transformation
public int spcod_ndl; // Number of decomposition levels
public int spcod_cw; // Code-blocks width
public int spcod_ch; // Code-blocks height
public int spcod_cs; // Code-blocks style
public int[] spcod_t = new int[1]; // Transformation
public int[] spcod_ps; // Precinct size
public COD getCopy() {
COD ms = null;
try {
ms = (COD) this.clone();
} catch (CloneNotSupportedException e) {
throw new Error("Cannot clone SIZ marker segment");
}
return ms;
}
/**
* Display information found in this COD marker segment
*/
public String toString() {
String str = "\n --- COD (" + lcod + " bytes) ---\n";
str += " Coding style : ";
if (scod == 0) {
str += "Default";
} else {
if ((scod & SCOX_PRECINCT_PARTITION) != 0) str += "Precints ";
if ((scod & SCOX_USE_SOP) != 0) str += "SOP ";
if ((scod & SCOX_USE_EPH) != 0) str += "EPH ";
int cb0x = ((scod & SCOX_HOR_CB_PART) != 0) ? 1 : 0;
int cb0y = ((scod & SCOX_VER_CB_PART) != 0) ? 1 : 0;
if (cb0x != 0 || cb0y != 0) {
str += "Code-blocks offset";
str += "\n Cblk partition : " + cb0x + "," + cb0y;
}
}
str += "\n";
str += " Cblk style : ";
if (spcod_cs == 0) {
str += "Default";
} else {
if ((spcod_cs & 0x1) != 0) str += "Bypass ";
if ((spcod_cs & 0x2) != 0) str += "Reset ";
if ((spcod_cs & 0x4) != 0) str += "Terminate ";
if ((spcod_cs & 0x8) != 0) str += "Vert_causal ";
if ((spcod_cs & 0x10) != 0) str += "Predict ";
if ((spcod_cs & 0x20) != 0) str += "Seg_symb ";
}
str += "\n";
str += " Num. of levels : " + spcod_ndl + "\n";
switch (sgcod_po) {
case LY_RES_COMP_POS_PROG:
str += " Progress. type : LY_RES_COMP_POS_PROG\n";
break;
case RES_LY_COMP_POS_PROG:
str += " Progress. type : RES_LY_COMP_POS_PROG\n";
break;
case RES_POS_COMP_LY_PROG:
str += " Progress. type : RES_POS_COMP_LY_PROG\n";
break;
case POS_COMP_RES_LY_PROG:
str += " Progress. type : POS_COMP_RES_LY_PROG\n";
break;
case COMP_POS_RES_LY_PROG:
str += " Progress. type : COMP_POS_RES_LY_PROG\n";
break;
}
str += " Num. of layers : " + sgcod_nl + "\n";
str += " Cblk dimension : " + (1 << (spcod_cw + 2)) + "x" +
(1 << (spcod_ch + 2)) + "\n";
switch (spcod_t[0]) {
case W9X7:
str += " Filter : 9-7 irreversible\n";
break;
case W5X3:
str += " Filter : 5-3 reversible\n";
break;
}
str += " Multi comp tr. : " + (sgcod_mct == 1) + "\n";
if (spcod_ps != null) {
str += " Precincts : ";
for (int i = 0; i < spcod_ps.length; i++) {
str += (1 << (spcod_ps[i] & 0x000F)) + "x" +
(1 << (((spcod_ps[i] & 0x00F0) >> 4))) + " ";
}
}
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the COC marker segments
*/
public class COC {
public int lcoc;
public int ccoc;
public int scoc;
public int spcoc_ndl; // Number of decomposition levels
public int spcoc_cw;
public int spcoc_ch;
public int spcoc_cs;
public int[] spcoc_t = new int[1];
public int[] spcoc_ps;
/**
* Display information found in this COC marker segment
*/
public String toString() {
String str = "\n --- COC (" + lcoc + " bytes) ---\n";
str += " Component : " + ccoc + "\n";
str += " Coding style : ";
if (scoc == 0) {
str += "Default";
} else {
if ((scoc & 0x1) != 0) str += "Precints ";
if ((scoc & 0x2) != 0) str += "SOP ";
if ((scoc & 0x4) != 0) str += "EPH ";
}
str += "\n";
str += " Cblk style : ";
if (spcoc_cs == 0) {
str += "Default";
} else {
if ((spcoc_cs & 0x1) != 0) str += "Bypass ";
if ((spcoc_cs & 0x2) != 0) str += "Reset ";
if ((spcoc_cs & 0x4) != 0) str += "Terminate ";
if ((spcoc_cs & 0x8) != 0) str += "Vert_causal ";
if ((spcoc_cs & 0x10) != 0) str += "Predict ";
if ((spcoc_cs & 0x20) != 0) str += "Seg_symb ";
}
str += "\n";
str += " Num. of levels : " + spcoc_ndl + "\n";
str += " Cblk dimension : " + (1 << (spcoc_cw + 2)) + "x" +
(1 << (spcoc_ch + 2)) + "\n";
switch (spcoc_t[0]) {
case W9X7:
str += " Filter : 9-7 irreversible\n";
break;
case W5X3:
str += " Filter : 5-3 reversible\n";
break;
}
if (spcoc_ps != null) {
str += " Precincts : ";
for (int i = 0; i < spcoc_ps.length; i++) {
str += (1 << (spcoc_ps[i] & 0x000F)) + "x" +
(1 << (((spcoc_ps[i] & 0x00F0) >> 4))) + " ";
}
}
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the RGN marker segments
*/
public class RGN {
public int lrgn;
public int crgn;
public int srgn;
public int sprgn;
/**
* Display information found in this RGN marker segment
*/
public String toString() {
String str = "\n --- RGN (" + lrgn + " bytes) ---\n";
str += " Component : " + crgn + "\n";
if (srgn == 0) {
str += " ROI style : Implicit\n";
} else {
str += " ROI style : Unsupported\n";
}
str += " ROI shift : " + sprgn + "\n";
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the QCD marker segments
*/
public class QCD {
public int lqcd;
public int sqcd;
public int[][] spqcd;
private int qType = -1;
private int gb = -1;
public int getQuantType() {
if (qType == -1) {
qType = sqcd & ~(SQCX_GB_MSK << SQCX_GB_SHIFT);
}
return qType;
}
public int getNumGuardBits() {
if (gb == -1) {
gb = (sqcd >> SQCX_GB_SHIFT) & SQCX_GB_MSK;
}
return gb;
}
/**
* Display information found in this QCD marker segment
*/
public String toString() {
String str = "\n --- QCD (" + lqcd + " bytes) ---\n";
str += " Quant. type : ";
int qt = getQuantType();
if (qt == SQCX_NO_QUANTIZATION) str += "No quantization \n";
else if (qt == SQCX_SCALAR_DERIVED) str += "Scalar derived\n";
else if (qt == SQCX_SCALAR_EXPOUNDED) str += "Scalar expounded\n";
str += " Guard bits : " + getNumGuardBits() + "\n";
if (qt == SQCX_NO_QUANTIZATION) {
str += " Exponents :\n";
int exp;
for (int i = 0; i < spqcd.length; i++) {
for (int j = 0; j < spqcd[i].length; j++) {
if (i == 0 && j == 0) {
exp = (spqcd[0][0] >> SQCX_EXP_SHIFT) & SQCX_EXP_MASK;
str += "\tr=0 : " + exp + "\n";
} else if (i != 0 && j > 0) {
exp = (spqcd[i][j] >> SQCX_EXP_SHIFT) & SQCX_EXP_MASK;
str += "\tr=" + i + ",s=" + j + " : " + exp + "\n";
}
}
}
} else {
str += " Exp / Mantissa : \n";
int exp;
double mantissa;
for (int i = 0; i < spqcd.length; i++) {
for (int j = 0; j < spqcd[i].length; j++) {
if (i == 0 && j == 0) {
exp = (spqcd[0][0] >> 11) & 0x1f;
mantissa = (-1f - ((float) (spqcd[0][0] & 0x07ff)) /
(1 << 11)) / (-1 << exp);
str += "\tr=0 : " + exp + " / " + mantissa + "\n";
} else if (i != 0 && j > 0) {
exp = (spqcd[i][j] >> 11) & 0x1f;
mantissa = (-1f - ((float) (spqcd[i][j] & 0x07ff)) /
(1 << 11)) / (-1 << exp);
str += "\tr=" + i + ",s=" + j + " : " + exp + " / " +
mantissa + "\n";
}
}
}
}
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the QCC marker segments
*/
public class QCC {
public int lqcc;
public int cqcc;
public int sqcc;
public int[][] spqcc;
private int qType = -1;
private int gb = -1;
public int getQuantType() {
if (qType == -1) {
qType = sqcc & ~(SQCX_GB_MSK << SQCX_GB_SHIFT);
}
return qType;
}
public int getNumGuardBits() {
if (gb == -1) {
gb = (sqcc >> SQCX_GB_SHIFT) & SQCX_GB_MSK;
}
return gb;
}
/**
* Display information found in this QCC marker segment
*/
public String toString() {
String str = "\n --- QCC (" + lqcc + " bytes) ---\n";
str += " Component : " + cqcc + "\n";
str += " Quant. type : ";
int qt = getQuantType();
if (qt == SQCX_NO_QUANTIZATION) str += "No quantization \n";
else if (qt == SQCX_SCALAR_DERIVED) str += "Scalar derived\n";
else if (qt == SQCX_SCALAR_EXPOUNDED) str += "Scalar expounded\n";
str += " Guard bits : " + getNumGuardBits() + "\n";
if (qt == SQCX_NO_QUANTIZATION) {
str += " Exponents :\n";
int exp;
for (int i = 0; i < spqcc.length; i++) {
for (int j = 0; j < spqcc[i].length; j++) {
if (i == 0 && j == 0) {
exp = (spqcc[0][0] >> SQCX_EXP_SHIFT) & SQCX_EXP_MASK;
str += "\tr=0 : " + exp + "\n";
} else if (i != 0 && j > 0) {
exp = (spqcc[i][j] >> SQCX_EXP_SHIFT) & SQCX_EXP_MASK;
str += "\tr=" + i + ",s=" + j + " : " + exp + "\n";
}
}
}
} else {
str += " Exp / Mantissa : \n";
int exp;
double mantissa;
for (int i = 0; i < spqcc.length; i++) {
for (int j = 0; j < spqcc[i].length; j++) {
if (i == 0 && j == 0) {
exp = (spqcc[0][0] >> 11) & 0x1f;
mantissa = (-1f - ((float) (spqcc[0][0] & 0x07ff)) /
(1 << 11)) / (-1 << exp);
str += "\tr=0 : " + exp + " / " + mantissa + "\n";
} else if (i != 0 && j > 0) {
exp = (spqcc[i][j] >> 11) & 0x1f;
mantissa = (-1f - ((float) (spqcc[i][j] & 0x07ff)) /
(1 << 11)) / (-1 << exp);
str += "\tr=" + i + ",s=" + j + " : " + exp + " / " +
mantissa + "\n";
}
}
}
}
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the POC marker segments
*/
public class POC {
public int lpoc;
public int[] rspoc;
public int[] cspoc;
public int[] lyepoc;
public int[] repoc;
public int[] cepoc;
public int[] ppoc;
/**
* Display information found in this POC marker segment
*/
public String toString() {
String str = "\n --- POC (" + lpoc + " bytes) ---\n";
str += " Chg_idx RSpoc CSpoc LYEpoc REpoc CEpoc Ppoc\n";
for (int chg = 0; chg < rspoc.length; chg++) {
str += " " + chg
+ " " + rspoc[chg]
+ " " + cspoc[chg]
+ " " + lyepoc[chg]
+ " " + repoc[chg]
+ " " + cepoc[chg];
switch (ppoc[chg]) {
case ProgressionType.LY_RES_COMP_POS_PROG:
str += " LY_RES_COMP_POS_PROG\n";
break;
case ProgressionType.RES_LY_COMP_POS_PROG:
str += " RES_LY_COMP_POS_PROG\n";
break;
case ProgressionType.RES_POS_COMP_LY_PROG:
str += " RES_POS_COMP_LY_PROG\n";
break;
case ProgressionType.POS_COMP_RES_LY_PROG:
str += " POS_COMP_RES_LY_PROG\n";
break;
case ProgressionType.COMP_POS_RES_LY_PROG:
str += " COMP_POS_RES_LY_PROG\n";
break;
}
}
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the CRG marker segment
*/
public class CRG {
public int lcrg;
public int[] xcrg;
public int[] ycrg;
/**
* Display information found in the CRG marker segment
*/
public String toString() {
String str = "\n --- CRG (" + lcrg + " bytes) ---\n";
for (int c = 0; c < xcrg.length; c++) {
str += " Component " + c + " offset : " + xcrg[c] + "," + ycrg[c] + "\n";
}
str += "\n";
return str;
}
}
/**
* Internal class holding information found in the COM marker segments
*/
public class COM {
public int lcom;
public int rcom;
public byte[] ccom;
/**
* Display information found in the COM marker segment
*/
public String toString() {
String str = "\n --- COM (" + lcom + " bytes) ---\n";
if (rcom == 0) {
str += " Registration : General use (binary values)\n";
} else if (rcom == 1) {
str += " Registration : General use (IS 8859-15:1999 " +
"(Latin) values)\n";
str += " Text : " + (new String(ccom)) + "\n";
} else {
str += " Registration : Unknown\n";
}
str += "\n";
return str;
}
}
}

View file

@ -0,0 +1,288 @@
/*
* $RCSfile: Markers.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:00 $
* $State: Exp $
*
* Class: Markers
*
* Description: Defines the values of the markers in JPEG 2000 codestream
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream;
/**
* This interface defines the values of the different markers in the JPEG 2000
* codestream. They are 16 bit values, always appearing in big-endian (most
* significant byte first) and byte-aligned in the codestream. This interface
* also defines some other constants such as bit-masks and bit-shifts.
*/
public interface Markers {
// ----> Delimiting markers and marker segments <----
/**
* Start of codestream (SOC): 0xFF4F
*/
short SOC = (short) 0xff4f;
/**
* Start of tile-part (SOT): 0xFF90
*/
short SOT = (short) 0xff90;
/**
* Start of data (SOD): 0xFF93
*/
short SOD = (short) 0xff93;
/**
* End of codestream (EOC): 0xFFD9
*/
short EOC = (short) 0xffd9;
// ----> Fixed information marker segments <----
// ** SIZ marker **
/**
* SIZ marker (Image and tile size): 0xFF51
*/
short SIZ = (short) 0xff51;
/**
* No special capabilities (baseline) in codestream, in Rsiz field of SIZ
* marker: 0x00. All flag bits are turned off
*/
int RSIZ_BASELINE = 0x00;
/**
* Error resilience marker flag bit in Rsiz field in SIZ marker: 0x01
*/
int RSIZ_ER_FLAG = 0x01;
/**
* ROI present marker flag bit in Rsiz field in SIZ marker: 0x02
*/
int RSIZ_ROI = 0x02;
/**
* Component bitdepth bits in Ssiz field in SIZ marker: 7
*/
int SSIZ_DEPTH_BITS = 7;
/**
* The maximum number of component bitdepth
*/
int MAX_COMP_BITDEPTH = 38;
// ----> Functional marker segments <----
// ** COD/COC marker **
/**
* Coding style default (COD): 0xFF52
*/
short COD = (short) 0xff52;
/**
* Coding style component (COC): 0xFF53
*/
short COC = (short) 0xff53;
/**
* Precinct used flag
*/
int SCOX_PRECINCT_PARTITION = 1;
/**
* Use start of packet marker
*/
int SCOX_USE_SOP = 2;
/**
* Use end of packet header marker
*/
int SCOX_USE_EPH = 4;
/**
* Horizontal code-block partition origin is at x=1
*/
int SCOX_HOR_CB_PART = 8;
/**
* Vertical code-block partition origin is at y=1
*/
int SCOX_VER_CB_PART = 16;
/**
* The default size exponent of the precincts
*/
int PRECINCT_PARTITION_DEF_SIZE = 0xffff;
// ** RGN marker segment **
/**
* Region-of-interest (RGN): 0xFF5E
*/
short RGN = (short) 0xff5e;
/**
* Implicit (i.e. max-shift) ROI flag for Srgn field in RGN marker
* segment: 0x00
*/
int SRGN_IMPLICIT = 0x00;
// ** QCD/QCC markers **
/**
* Quantization default (QCD): 0xFF5C
*/
short QCD = (short) 0xff5c;
/**
* Quantization component (QCC): 0xFF5D
*/
short QCC = (short) 0xff5d;
/**
* Guard bits shift in SQCX field: 5
*/
int SQCX_GB_SHIFT = 5;
/**
* Guard bits mask in SQCX field: 7
*/
int SQCX_GB_MSK = 7;
/**
* No quantization (i.e. embedded reversible) flag for Sqcd or Sqcc
* (Sqcx) fields: 0x00.
*/
int SQCX_NO_QUANTIZATION = 0x00;
/**
* Scalar derived (i.e. LL values only) quantization flag for Sqcd or
* Sqcc (Sqcx) fields: 0x01.
*/
int SQCX_SCALAR_DERIVED = 0x01;
/**
* Scalar expounded (i.e. all values) quantization flag for Sqcd or Sqcc
* (Sqcx) fields: 0x02.
*/
int SQCX_SCALAR_EXPOUNDED = 0x02;
/**
* Exponent shift in SPQCX when no quantization: 3
*/
int SQCX_EXP_SHIFT = 3;
/**
* Exponent bitmask in SPQCX when no quantization: 3
*/
int SQCX_EXP_MASK = (1 << 5) - 1;
/**
* The "SOP marker segments used" flag within Sers: 1
*/
int ERS_SOP = 1;
/**
* The "segmentation symbols used" flag within Sers: 2
*/
int ERS_SEG_SYMBOLS = 2;
// ** Progression order change **
short POC = (short) 0xff5f;
// ----> Pointer marker segments <----
/**
* Tile-part lengths (TLM): 0xFF55
*/
short TLM = (short) 0xff55;
/**
* Packet length, main header (PLM): 0xFF57
*/
short PLM = (short) 0xff57;
/**
* Packet length, tile-part header (PLT): 0xFF58
*/
short PLT = (short) 0xff58;
/**
* Packed packet headers, main header (PPM): 0xFF60
*/
short PPM = (short) 0xff60;
/**
* Packed packet headers, tile-part header (PPT): 0xFF61
*/
short PPT = (short) 0xff61;
/**
* Maximum length of PPT marker segment
*/
int MAX_LPPT = 65535;
/**
* Maximum length of PPM marker segment
*/
int MAX_LPPM = 65535;
// ----> In bit stream markers and marker segments <----
/**
* Start pf packet (SOP): 0xFF91
*/
short SOP = (short) 0xff91;
/**
* Length of SOP marker (in bytes)
*/
short SOP_LENGTH = 6;
/**
* End of packet header (EPH): 0xFF92
*/
short EPH = (short) 0xff92;
/**
* Length of EPH marker (in bytes)
*/
short EPH_LENGTH = 2;
// ----> Informational marker segments <----
/**
* Component registration (CRG): 0xFF63
*/
short CRG = (short) 0xff63;
/**
* Comment (COM): 0xFF64
*/
short COM = (short) 0xff64;
/**
* General use registration value (COM): 0x0001
*/
short RCOM_GEN_USE = (short) 0x0001;
}

View file

@ -0,0 +1,93 @@
/*
* $RCSfile: PrecCoordInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:00 $
* $State: Exp $
*
* Class: PrecCoordInfo
*
* Description: Used to store the coordinates precincts.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream;
/**
* This class is used to store the coordinates of precincts.
*/
public class PrecCoordInfo extends CoordInfo {
/**
* Horizontal upper left coordinate in the reference grid
*/
public int xref;
/**
* Vertical upper left coordinate on the reference grid
*/
public int yref;
/**
* Constructor. Creates a PrecCoordInfo object.
*
* @param ulx Horizontal upper left coordinate in the subband
* @param uly Vertical upper left coordinate in the subband
* @param w Precint's width
* @param h Precinct's height
* @param xref The horizontal coordinate on the reference grid
* @param yref The vertical coordinate on the reference grid
*/
public PrecCoordInfo(int ulx, int uly, int w, int h,
int xref, int yref) {
super(ulx, uly, w, h);
this.xref = xref;
this.yref = yref;
}
/**
* Empty Constructor. Creates an empty PrecCoordInfo object.
*/
public PrecCoordInfo() {
super();
}
/**
* Returns precinct's information in a String
*
* @return String with precinct's information
*/
public String toString() {
return super.toString() + ", xref=" + xref + ", yref=" + yref;
}
}

View file

@ -0,0 +1,151 @@
/*
* $RCSfile: PrecInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:00 $
* $State: Exp $
*
* Class: PrecInfo
*
* Description: Keeps information about a precinct
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream;
/**
* Class that holds precinct coordinates and references to contained
* code-blocks in each subband.
*/
public class PrecInfo {
/**
* Precinct horizontal upper-left coordinate in the reference grid
*/
public int rgulx;
/**
* Precinct vertical upper-left coordinate in the reference grid
*/
public int rguly;
/**
* Precinct width reported in the reference grid
*/
public int rgw;
/**
* Precinct height reported in the reference grid
*/
public int rgh;
/**
* Precinct horizontal upper-left coordinate in the corresponding
* resolution level
*/
public int ulx;
/**
* Precinct vertical upper-left coordinate in the corresponding
* resolution level
*/
public int uly;
/**
* Precinct width in the corresponding resolution level
*/
public int w;
/**
* Precinct height in the corresponding resolution level
*/
public int h;
/**
* Resolution level index
*/
public int r;
/**
* Code-blocks belonging to this precinct in each subbands of the
* resolution level
*/
public CBlkCoordInfo[][][] cblk;
/**
* Number of code-blocks in each subband belonging to this precinct
*/
public int[] nblk;
/**
* Class constructor.
*
* @param r Resolution level index.
* @param ulx Precinct horizontal offset.
* @param uly Precinct vertical offset.
* @param w Precinct width.
* @param h Precinct height.
* @param rgulx Precinct horizontal offset in the image reference grid.
* @param rguly Precinct horizontal offset in the image reference grid.
* @param rgw Precinct width in the reference grid.
* @param rgh Precinct height in the reference grid.
*/
public PrecInfo(int r, int ulx, int uly, int w, int h, int rgulx, int rguly,
int rgw, int rgh) {
this.r = r;
this.ulx = ulx;
this.uly = uly;
this.w = w;
this.h = h;
this.rgulx = rgulx;
this.rguly = rguly;
this.rgw = rgw;
this.rgh = rgh;
if (r == 0) {
cblk = new CBlkCoordInfo[1][][];
nblk = new int[1];
} else {
cblk = new CBlkCoordInfo[4][][];
nblk = new int[4];
}
}
/**
* Returns PrecInfo object information in a String
*
* @return PrecInfo information
*/
public String toString() {
return "ulx=" + ulx + ",uly=" + uly + ",w=" + w + ",h=" + h + ",rgulx=" + rgulx +
",rguly=" + rguly + ",rgw=" + rgw + ",rgh=" + rgh;
}
}

View file

@ -0,0 +1,92 @@
/*
* $RCSfile: ProgressionType.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:00 $
* $State: Exp $
*
* Class: ProgressionType
*
* Description: The definition of the different bit stream
* profiles.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream;
/**
* This interface defines the identifiers for the different bit stream
* profiles and progression types.
*
* <P>Each progressive type has a different number: 'PT_SNR_PROG',
* 'PT_RES_PROG', or 'PT_ARB_PROG'. These are the same identifiers are used in
* the codestream syntax.
*
* <P>Each profile identifier is a flag bit. Therefore, several
* profiles can appear at the same time.
*
* <p>This interface defines the constants only. In order to use the constants
* in any other class you can either use the fully qualified name (e.g.,
* <tt>ProgressionType.LY_RES_COMP_POS_PROG</tt>) or declare this interface in
* the implements clause of the class and then access the identifier
* directly.</p>
*/
public interface ProgressionType {
/**
* The bit stream is Layer/Resolution/Component/Position progressive : 0
*/
int LY_RES_COMP_POS_PROG = 0;
/**
* The bit stream is Resolution/Layer/Component/Position progressive : 1
*/
int RES_LY_COMP_POS_PROG = 1;
/**
* The bit stream is Resolution/Position/Component/Layer progressive : 2
*/
int RES_POS_COMP_LY_PROG = 2;
/**
* The bit stream is Position/Component/Resolution/Layer progressive : 3
*/
int POS_COMP_RES_LY_PROG = 3;
/**
* The bit stream is Component/Position/Resolution/Layer progressive : 4
*/
int COMP_POS_RES_LY_PROG = 4;
}

View file

@ -0,0 +1,179 @@
/*
* $RCSfile: CBlkInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:01 $
* $State: Exp $
*
* Class: CBlkInfo
*
* Description: Object containing code-block informations.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.codestream.reader;
/**
* This class contains location of code-blocks' piece of codewords
* (there is one piece per layer) and some other information.
*/
public class CBlkInfo {
/**
* Upper-left x-coordinate of the code-block (relative to the
* tile)
*/
public int ulx;
/**
* Upper-left y-coordinate of the code-block (relative to the
* tile)
*/
public int uly;
/**
* Width of the code-block
*/
public int w;
/**
* Height of the code-block
*/
public int h;
/**
* The number of most significant bits which are skipped for this
* code-block (= Mb-1-bitDepth). See VM text
*/
public int msbSkipped;
/**
* Length of each piece of code-block's codewords
*/
public int[] len;
/**
* Offset of each piece of code-block's codewords in the file
*/
public int[] off;
/**
* The number of truncation point for each layer
*/
public int[] ntp;
/**
* The cumulative number of truncation points
*/
public int ctp;
/**
* The length of each segment (used with regular termination or
* in selective arithmetic bypass coding mode)
*/
public int[][] segLen;
/**
* Index of the packet where each layer has been found
*/
public int[] pktIdx;
/**
* Constructs a new instance with specified number of layers and
* code-block coordinates. The number corresponds to the maximum
* piece of codeword for one code-block.
*
* @param ulx The uper-left x-coordinate
* @param uly The uper-left y-coordinate
* @param w Width of the code-block
* @param h Height of the code-block
* @param nl The number of layers
*/
public CBlkInfo(int ulx, int uly, int w, int h, int nl) {
this.ulx = ulx;
this.uly = uly;
this.w = w;
this.h = h;
off = new int[nl];
len = new int[nl];
ntp = new int[nl];
segLen = new int[nl][];
pktIdx = new int[nl];
for (int i = nl - 1; i >= 0; i--)
pktIdx[i] = -1;
}
/**
* Adds the number of new truncation for specified layer.
*
* @param l layer index
* @param newtp Number of new truncation points
*/
public void addNTP(int l, int newtp) {
ntp[l] = newtp;
ctp = 0;
for (int lIdx = 0; lIdx <= l; lIdx++) {
ctp += ntp[lIdx];
}
}
/**
* Object information in a string.
*
* @return Object information
*/
public String toString() {
String string = "(ulx,uly,w,h)= " + ulx + "," + uly + "," + w + "," + h;
string += ", " + msbSkipped + " MSB bit(s) skipped\n";
if (len != null)
for (int i = 0; i < len.length; i++) {
string += "\tl:" + i + ", start:" + off[i] +
", len:" + len[i] + ", ntp:" + ntp[i] + ", pktIdx=" +
pktIdx[i];
if (segLen != null && segLen[i] != null) {
string += " { ";
for (int j = 0; j < segLen[i].length; j++)
string += segLen[i][j] + " ";
string += "}";
}
string += "\n";
}
string += "\tctp=" + ctp;
return string;
}
}

View file

@ -0,0 +1,238 @@
/*
* $RCSfile: PktHeaderBitReader.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:01 $
* $State: Exp $
*
* Class: PktHeaderBitReader
*
* Description: Bit based reader for packet headers
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.codestream.reader;
import org.xbib.graphics.jpeg2000.j2k.io.RandomAccessIO;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
/**
* This class provides a bit based reading facility from a byte based one,
* applying the bit unstuffing procedure as required by the packet headers.
*/
class PktHeaderBitReader {
/**
* The byte based source of data
*/
RandomAccessIO in;
/**
* The byte array that is the source of data if the PktHeaderBitReader
* is instantiated with a buffer instead of a RandomAccessIO
*/
ByteArrayInputStream bais;
/**
* Flag indicating whether the data should be read from the buffer
*/
boolean usebais;
/**
* The current bit buffer
*/
int bbuf;
/**
* The position of the next bit to read in the bit buffer (0 means
* empty, 8 full)
*/
int bpos;
/**
* The next bit buffer, if bit stuffing occurred (i.e. current bit
* buffer holds 0xFF)
*/
int nextbbuf;
/**
* Instantiates a 'PktHeaderBitReader' that gets the byte data from the
* given source.
*
* @param in The source of byte data
*/
PktHeaderBitReader(RandomAccessIO in) {
this.in = in;
usebais = false;
}
/**
* Instantiates a 'PktHeaderBitReader' that gets the byte data from the
* given source.
*
* @param bais The source of byte data
*/
PktHeaderBitReader(ByteArrayInputStream bais) {
this.bais = bais;
usebais = true;
}
/**
* Reads a single bit from the input.
*
* @return The read bit (0 or 1)
* @throws IOException If an I/O error occurred
* @throws EOFException If teh end of file has been reached
*/
final int readBit() throws IOException {
if (bpos == 0) { // Is bit buffer empty?
if (bbuf != 0xFF) { // No bit stuffing
bbuf = usebais ? bais.read() : in.read();
bpos = 8;
if (bbuf == 0xFF) { // If new bit stuffing get next byte
nextbbuf = usebais ? bais.read() : in.read();
}
} else { // We had bit stuffing, nextbuf can not be 0xFF
bbuf = nextbbuf;
bpos = 7;
}
if (bbuf < 0) {
throw new EOFException();
}
}
return (bbuf >> --bpos) & 0x01;
}
/**
* Reads a specified number of bits and returns them in a single
* integer. The bits are returned in the 'n' least significant bits of the
* returned integer. The maximum number of bits that can be read is 31.
*
* @param n The number of bits to read
* @return The read bits, packed in the 'n' LSBs.
* @throws IOException If an I/O error occurred
* @throws EOFException If teh end of file has been reached
*/
final int readBits(int n) throws IOException {
int bits; // The read bits
// Can we get all bits from the bit buffer?
if (n <= bpos) {
return (bbuf >> (bpos -= n)) & ((1 << n) - 1);
} else {
// NOTE: The implementation need not be recursive but the not
// recursive one exploits a bug in the IBM x86 JIT and caused
// incorrect decoding (Diego Santa Cruz).
bits = 0;
do {
// Get all the bits we can from the bit buffer
bits <<= bpos;
n -= bpos;
bits |= readBits(bpos);
// Get an extra bit to load next byte (here bpos is 0)
if (bbuf != 0xFF) { // No bit stuffing
bbuf = usebais ? bais.read() : in.read();
bpos = 8;
if (bbuf == 0xFF) { // If new bit stuffing get next byte
nextbbuf = usebais ? bais.read() : in.read();
}
} else { // We had bit stuffing, nextbuf can not be 0xFF
bbuf = nextbbuf;
bpos = 7;
}
if (bbuf < 0) {
throw new EOFException();
}
} while (n > bpos);
// Get the last bits, if any
bits <<= n;
bits |= (bbuf >> (bpos -= n)) & ((1 << n) - 1);
// Return result
return bits;
}
}
/**
* Synchronizes this object with the underlying byte based input. It
* discards and buffered bits and gets ready to read bits from the current
* position in the underlying byte based input.
*
* <P>This method should always be called when some data has been read
* directly from the underlying byte based input since the last call to
* 'readBits()' or 'readBit()' before a new call to any of those methods.
*/
void sync() {
bbuf = 0;
bpos = 0;
}
/**
* Sets the underlying byte based input to the given object. This method
* discards any currently buffered bits and gets ready to start reading
* bits from 'in'.
*
* <P>This method is equivalent to creating a new 'PktHeaderBitReader'
* object.
*
* @param in The source of byte data
*/
void setInput(RandomAccessIO in) {
this.in = in;
bbuf = 0;
bpos = 0;
}
/**
* Sets the underlying byte based input to the given object. This method
* discards any currently buffered bits and gets ready to start reading
* bits from 'in'.
*
* <P>This method is equivalent to creating a new 'PktHeaderBitReader'
* object.
*
* @param bais The source of byte data
*/
void setInput(ByteArrayInputStream bais) {
this.bais = bais;
bbuf = 0;
bpos = 0;
}
}

View file

@ -0,0 +1,110 @@
/*
* $RCSfile: PktInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:02 $
* $State: Exp $
*
* Class: PktInfo
*
* Description: Object containing packet informations.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream.reader;
/**
* This class defines an object used to countain informations about a packet
* to which the current code-block belongs.
*
* @see CBlkInfo
*/
public class PktInfo {
/**
* Index of the packet
*/
public int packetIdx;
/**
* The layer associated with the current code-block in this packet.
*/
public int layerIdx;
/**
* The code-block offset in the codestream (for this packet)
*/
public int cbOff = 0;
/**
* The length of the code-block in this packet (in bytes)
*/
public int cbLength;
/**
* The length of each terminated segment in the packet. The total is the
* same as 'cbLength'. It can be null if there is only one terminated
* segment, in which case 'cbLength' holds the legth of that segment
*/
public int[] segLengths;
/**
* The number of truncation points that appear in this packet, and all
* previous packets, for this code-block. This is the number of passes
* that can be decoded with the information in this packet and all
* previous ones.
*/
public int numTruncPnts;
/**
* Classe's constructor.
*
* @param lyIdx The layer index for the code-block in this packet
* @param pckIdx The packet index
*/
public PktInfo(int lyIdx, int pckIdx) {
layerIdx = lyIdx;
packetIdx = pckIdx;
}
/**
* Object information in a string.
*
* @return Object information
*/
public String toString() {
return "packet " + packetIdx + " (lay:" + layerIdx + ", off:" + cbOff + ", len:" +
cbLength + ", numTruncPnts:" + numTruncPnts + ")\n";
}
}

View file

@ -0,0 +1,262 @@
/*
* $RCSfile: TagTreeDecoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:02 $
* $State: Exp $
*
* Class: TagTreeDecoder
*
* Description: Decoder of tag trees
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.codestream.reader;
import org.xbib.graphics.jpeg2000.j2k.util.ArrayUtil;
import org.xbib.graphics.jpeg2000.j2k.codestream.writer.TagTreeEncoder;
import java.io.EOFException;
import java.io.IOException;
/**
* This class implements the tag tree decoder. A tag tree codes a 2D
* matrix of integer elements in an efficient way. The decoding
* procedure 'update()' updates a value of the matrix from a stream of
* coded data, given a threshold. This procedure decodes enough
* information to identify whether or not the value is greater than
* or equal to the threshold, and updates the value accordingly.
*
* <P>In general the decoding procedure must follow the same sequence
* of elements and thresholds as the encoding one. The encoder is
* implemented by the TagTreeEncoder class.
*
* <P>Tag trees that have one dimension, or both, as 0 are allowed for
* convenience. Of course no values can be set or coded in such cases.
*
* @see TagTreeEncoder
*/
public class TagTreeDecoder {
/**
* The horizontal dimension of the base level
*/
protected int w;
/**
* The vertical dimensions of the base level
*/
protected int h;
/**
* The number of levels in the tag tree
*/
protected int lvls;
/**
* The tag tree values. The first index is the level,
* starting at level 0 (leafs). The second index is the element
* within the level, in lexicographical order.
*/
protected int[][] treeV;
/**
* The tag tree state. The first index is the level, starting at
* level 0 (leafs). The second index is the element within the
* level, in lexicographical order.
*/
protected int[][] treeS;
/**
* Creates a tag tree decoder with 'w' elements along the
* horizontal dimension and 'h' elements along the vertical
* direction. The total number of elements is thus 'vdim' x
* 'hdim'.
*
* <P>The values of all elements are initialized to
* Integer.MAX_VALUE (i.e. no information decoded so far). The
* states are initialized all to 0.
*
* @param h The number of elements along the vertical direction.
* @param w The number of elements along the horizontal direction.
*/
public TagTreeDecoder(int h, int w) {
int i;
// Check arguments
if (w < 0 || h < 0) {
throw new IllegalArgumentException();
}
// Initialize dimensions
this.w = w;
this.h = h;
// Calculate the number of levels
if (w == 0 || h == 0) {
lvls = 0; // Empty tree
} else {
lvls = 1;
while (h != 1 || w != 1) { // Loop until we reach root
w = (w + 1) >> 1;
h = (h + 1) >> 1;
lvls++;
}
}
// Allocate tree values and states
treeV = new int[lvls][];
treeS = new int[lvls][];
w = this.w;
h = this.h;
for (i = 0; i < lvls; i++) {
treeV[i] = new int[h * w];
// Initialize to infinite value
ArrayUtil.intArraySet(treeV[i], Integer.MAX_VALUE);
// (no need to initialize to 0 since it's the default)
treeS[i] = new int[h * w];
w = (w + 1) >> 1;
h = (h + 1) >> 1;
}
}
/**
* Returns the number of leafs along the horizontal direction.
*
* @return The number of leafs along the horizontal direction.
*/
public final int getWidth() {
return w;
}
/**
* Returns the number of leafs along the vertical direction.
*
* @return The number of leafs along the vertical direction.
*/
public final int getHeight() {
return h;
}
/**
* Decodes information for the specified element of the tree,
* given the threshold, and updates its value. The information
* that can be decoded is whether or not the value of the element
* is greater than, or equal to, the value of the
* threshold.
*
* @param m The vertical index of the element.
* @param n The horizontal index of the element.
* @param t The threshold to use in decoding. It must be non-negative.
* @param in The stream from where to read the coded information.
* @return The updated value at position (m,n).
* @throws IOException If an I/O error occurs while reading
* from 'in'.
* @throws EOFException If the ned of the 'in' stream is
* reached before getting all the necessary data.
*/
public int update(int m, int n, int t, PktHeaderBitReader in)
throws IOException {
int k, tmin;
int idx, ts, tv;
// Check arguments
if (m >= h || n >= w || t < 0) {
throw new IllegalArgumentException();
}
// Initialize
k = lvls - 1;
tmin = treeS[k][0];
// Loop on levels
idx = (m >> k) * ((w + (1 << k) - 1) >> k) + (n >> k);
while (true) {
// Cache state and value
ts = treeS[k][idx];
tv = treeV[k][idx];
if (ts < tmin) {
ts = tmin;
}
while (t > ts) {
if (tv >= ts) { // We are not done yet
if (in.readBit() == 0) { // '0' bit
// We know that 'value' > treeS[k][idx]
ts++;
} else { // '1' bit
// We know that 'value' = treeS[k][idx]
tv = ts++;
}
// Increment of treeS[k][idx] done above
} else { // We are done, we can set ts and get out
ts = t;
break; // get out of this while
}
}
// Update state and value
treeS[k][idx] = ts;
treeV[k][idx] = tv;
// Update tmin or terminate
if (k > 0) {
tmin = ts < tv ? ts : tv;
k--;
// Index of element for next iteration
idx = (m >> k) * ((w + (1 << k) - 1) >> k) + (n >> k);
} else {
// Return the updated value
return tv;
}
}
}
/**
* Returns the current value of the specified element in the tag
* tree. This is the value as last updated by the update() method.
*
* @param m The vertical index of the element.
* @param n The horizontal index of the element.
* @return The current value of the element.
* @see #update
*/
public int getValue(int m, int n) {
// Check arguments
if (m >= h || n >= w) {
throw new IllegalArgumentException();
}
// Return value
return treeV[0][m * w + n];
}
}

View file

@ -0,0 +1,256 @@
/*
* $RCSfile: BitOutputBuffer.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:02 $
* $State: Exp $
*
* Class: BitOutputBuffer
*
* Description: <short description of class>
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream.writer;
import org.xbib.graphics.jpeg2000.j2k.util.ArrayUtil;
/**
* This class implements a buffer for writing bits, with the required bit
* stuffing policy for the packet headers. The bits are stored in a byte array
* in the order in which they are written. The byte array is automatically
* reallocated and enlarged whenever necessary. A BitOutputBuffer object may
* be reused by calling its 'reset()' method.
*
* <P>NOTE: The methods implemented in this class are intended to be used only
* in writing packet heads, since a special bit stuffing procedure is used, as
* required for the packet heads.
*/
public class BitOutputBuffer {
/**
* The increment size for the buffer, 16 bytes. This is the
* number of bytes that are added to the buffer each time it is
* needed to enlarge it.
*/
// This must be always 6 or larger.
public final static int SZ_INCR = 16;
/**
* The initial size for the buffer, 32 bytes.
*/
public final static int SZ_INIT = 32;
/**
* The buffer where we store the data
*/
byte[] buf;
/**
* The position of the current byte to write
*/
int curbyte;
/**
* The number of available bits in the current byte
*/
int avbits = 8;
/**
* Creates a new BitOutputBuffer width a buffer of length
* 'SZ_INIT'.
*/
public BitOutputBuffer() {
buf = new byte[SZ_INIT];
}
/**
* Resets the buffer. This rewinds the current position to the start of
* the buffer and sets all tha data to 0. Note that no new buffer is
* allocated, so this will affect any data that was returned by the
* 'getBuffer()' method.
*/
public void reset() {
int i;
// Reinit pointers
curbyte = 0;
avbits = 8;
ArrayUtil.byteArraySet(buf, (byte) 0);
}
/**
* Writes a bit to the buffer at the current position. The value 'bit'
* must be either 0 or 1, otherwise it corrupts the bits that have been
* already written. The buffer is enlarged, by 'SZ_INCR' bytes, if
* necessary.
*
* <P>This method is declared final to increase performance.
*
* @param bit The bit to write, 0 or 1.
*/
public final void writeBit(int bit) {
buf[curbyte] |= bit << --avbits;
if (avbits > 0) {
// There is still place in current byte for next bit
} else { // End of current byte => goto next
if (buf[curbyte] != (byte) 0xFF) { // We don't need bit stuffing
avbits = 8;
} else { // We need to stuff a bit (next MSBit is 0)
avbits = 7;
}
curbyte++;
if (curbyte == buf.length) {
// We are at end of 'buf' => extend it
byte[] oldbuf = buf;
buf = new byte[oldbuf.length + SZ_INCR];
System.arraycopy(oldbuf, 0, buf, 0, oldbuf.length);
}
}
}
/**
* Writes the n least significant bits of 'bits' to the buffer at the
* current position. The least significant bit is written last. The 32-n
* most significant bits of 'bits' must be 0, otherwise corruption of the
* buffer will result. The buffer is enlarged, by 'SZ_INCR' bytes, if
* necessary.
*
* <P>This method is declared final to increase performance.
*
* @param bits The bits to write.
* @param n The number of LSBs in 'bits' to write.
*/
public final void writeBits(int bits, int n) {
// Check that we have enough place in 'buf' for n bits, and that we do
// not fill last byte, taking into account possibly stuffed bits (max
// 2)
if (((buf.length - curbyte) << 3) - 8 + avbits <= n + 2) {
// Not enough place, extend it
byte[] oldbuf = buf;
buf = new byte[oldbuf.length + SZ_INCR];
System.arraycopy(oldbuf, 0, buf, 0, oldbuf.length);
// SZ_INCR is always 6 or more, so it is enough to hold all the
// new bits plus the ones to come after
}
// Now write the bits
if (n >= avbits) {
// Complete the current byte
n -= avbits;
buf[curbyte] |= bits >> n;
if (buf[curbyte] != (byte) 0xFF) { // We don't need bit stuffing
avbits = 8;
} else { // We need to stuff a bit (next MSBit is 0)
avbits = 7;
}
curbyte++;
// Write whole bytes
while (n >= avbits) {
n -= avbits;
buf[curbyte] |= (bits >> n) & (~(1 << avbits));
if (buf[curbyte] != (byte) 0xFF) { // We don't need bit
// stuffing
avbits = 8;
} else { // We need to stuff a bit (next MSBit is 0)
avbits = 7;
}
curbyte++;
}
}
// Finish last byte (we know that now n < avbits)
if (n > 0) {
avbits -= n;
buf[curbyte] |= (bits & ((1 << n) - 1)) << avbits;
}
if (avbits == 0) { // Last byte is full
if (buf[curbyte] != (byte) 0xFF) { // We don't need bit stuffing
avbits = 8;
} else { // We need to stuff a bit (next MSBit is 0)
avbits = 7;
}
curbyte++; // We already ensured that we have enough place
}
}
/**
* Returns the current length of the buffer, in bytes.
*
* <P>This method is declared final to increase performance.
*
* @return The currebt length of the buffer in bytes.
*/
public final int getLength() {
if (avbits == 8) { // A integral number of bytes
return curbyte;
} else { // Some bits in last byte
return curbyte + 1;
}
}
/**
* Returns the byte buffer. This is the internal byte buffer so it should
* not be modified. Only the first N elements have valid data, where N is
* the value returned by 'getLength()'
*
* <P>This method is declared final to increase performance.
*
* @return The internal byte buffer.
*/
public final byte[] getBuffer() {
return buf;
}
/**
* Returns the byte buffer data in a new array. This is a copy of the
* internal byte buffer. If 'data' is non-null it is used to return the
* data. This array should be large enough to contain all the data,
* otherwise a IndexOutOfBoundsException is thrown by the Java system. The
* number of elements returned is what 'getLength()' returns.
*
* @param data If non-null this array is used to return the data, which
* mus be large enough. Otherwise a new one is created and returned.
* @return The byte buffer data.
*/
public byte[] toByteArray(byte[] data) {
if (data == null) {
data = new byte[(avbits == 8) ? curbyte : curbyte + 1];
}
System.arraycopy(buf, 0, data, 0, (avbits == 8) ? curbyte : curbyte + 1);
return data;
}
/**
* Prints information about this object for debugging purposes
*
* @return Information about the object.
*/
public String toString() {
return "bits written = " + (curbyte * 8 + (8 - avbits)) +
", curbyte = " + curbyte + ", avbits = " + avbits;
}
}

View file

@ -0,0 +1,206 @@
/*
* $RCSfile: CodestreamWriter.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:02 $
* $State: Exp $
*
* Class: CodestreamWriter
*
* Description: Interface for writing bit streams
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream.writer;
import java.io.IOException;
/**
* This is the abstract class for writing to a bit stream. Data is
* written in packets, each packet having a head and a body. The
* bit stream always has a maximum number of bytes that can be written
* to it. After that many number of bytes no more data is written to
* the bit stream but the number of bytes is counted so that the value
* returned by getMaxAvailableBytes() is negative. If the number of
* bytes is unlimited a ridicoulosly large value, such as
* Integer.MAX_VALUE, is equivalent.
*
* <P>Data may be written to the bit stream in sumulation mode. When in
* simulation mode no data is written to the bit stream but the
* resulting number of bytes is calculated and returned (although it
* is not accounted in the bit stream). This can be used in rate
* control loops.
*
* <P>Implementing classes should write the header of the bit stream
* before writing any packets. The bit stream header should be written
* with the aid of the HeaderEncoder class.
*
* @see HeaderEncoder
*/
public abstract class CodestreamWriter {
/**
* The number of bytes already written to the bit stream
*/
protected int ndata = 0;
/**
* The maximum number of bytes that can be written to the
* bit stream
*/
protected int maxBytes;
/**
* Allocates this object and initializes the maximum numner of
* bytes.
*
* @param mb The maximum number of bytes that can be written to
* the bit stream.
*/
protected CodestreamWriter(int mb) {
maxBytes = mb;
}
/**
* Returns the number of bytes remaining available in the bit stream. This
* is the maximum allowed number of bytes minus the number of bytes that
* have already been written to the bit stream. If more bytes have been
* written to the bit stream than the maximum number of allowed bytes,
* then a negative value is returned.
*
* @return The number of bytes remaining available in the bit stream.
*/
public abstract int getMaxAvailableBytes();
/**
* Returns the current length of the entire bit stream.
*
* @return the current length of the bit stream
*/
public abstract int getLength();
/**
* Writes a packet head to the bit stream and returns the number of bytes
* used by this header. It returns the total number of bytes that the
* packet head takes in the bit stream. If in simulation mode then no data
* is written to the bit stream but the number of bytes is
* calculated. This can be used for iterative rate allocation.
*
* <P>If the length of the data that is to be written to the bit stream is
* more than the space left (as returned by getMaxAvailableBytes()) only
* the data that does not exceed the allowed length is written, the rest
* is discarded. However the value returned by the method is the total
* length of the packet, as if all of it was written to the bit stream.
*
* <P>If the bit stream header has not been commited yet and 'sim' is
* false, then the bit stream header is automatically commited (see
* commitBitstreamHeader() method) before writting the packet.
*
* @param head The packet head data.
* @param hlen The number of bytes in the packet head.
* @param sim Simulation mode flag. If true nothing is written to the bit
* stream, but the number of bytes that would be written is returned.
* @param sop Start of packet header marker flag. This flag indicates
* whether or not SOP markers should be written. If true, SOP markers
* should be written, if false, they should not.
* @param eph End of Packet Header marker flag. This flag indicates
* whether or not EPH markers should be written. If true, EPH markers
* should be written, if false, they should not.
* @return The number of bytes spent by the packet head.
* @throws IOException If an I/O error occurs while writing to the
* output stream.
* @see #commitBitstreamHeader
*/
public abstract int writePacketHead(byte[] head, int hlen, boolean sim,
boolean sop, boolean eph)
throws IOException;
/**
* Writes a packet body to the bit stream and returns the number of bytes
* used by this body .If in simulation mode then no data is written to the
* bit stream but the number of bytes is calculated. This can be used for
* iterative rate allocation.
*
* <P>If the length of the data that is to be written to the bit stream is
* more than the space left (as returned by getMaxAvailableBytes()) only
* the data that does not exceed the allowed length is written, the rest
* is discarded. However the value returned by the method is the total
* length of the packet body , as if all of it was written to the bit
* stream.
*
* @param body The packet body data.
* @param blen The number of bytes in the packet body.
* @param sim Simulation mode flag. If true nothing is written to the bit
* stream, but the number of bytes that would be written is returned.
* @param roiInPkt Whether or not there is ROI information in this packet
* @param roiLen Number of byte to read in packet body to get all the ROI
* information
* @return The number of bytes spent by the packet body.
* @throws IOException If an I/O error occurs while writing to
* the output stream.
* @see #commitBitstreamHeader
*/
public abstract int writePacketBody(byte[] body, int blen, boolean sim,
boolean roiInPkt, int roiLen)
throws IOException;
/**
* Closes the underlying resource (file, stream, network connection,
* etc.). After a CodestreamWriter is closed no more data can be written
* to it.
*
* @throws IOException If an I/O error occurs while closing the
* resource.
*/
public abstract void close() throws IOException;
/**
* Writes the header data to the bit stream, if it has not been already
* done. In some implementations this method can be called only once, and
* an IllegalArgumentException is thrown if called more than once.
*
* @throws IOException If an I/O error occurs while writing the data.
* @throws IllegalArgumentException If this method has already been
* called.
*/
public abstract void commitBitstreamHeader(HeaderEncoder he)
throws IOException;
/**
* Gives the offset of the end of last packet containing ROI information
*
* @return End of last ROI packet
*/
public abstract int getOffLastROIPkt();
}

View file

@ -0,0 +1,401 @@
/*
* $RCSfile: FileCodestreamWriter.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:02 $
* $State: Exp $
*
* Class: FileCodestreamWriter
*
* Description: Implementation of the bit stream writer for streams.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.codestream.writer;
import org.xbib.graphics.jpeg2000.j2k.codestream.Markers;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* This class implements a CodestreamWriter for Java streams. The streams can
* be files or network connections, or any other resource that presents itself
* as a OutputStream. See the CodestreamWriter abstract class for more details
* on the implementation of the CodestreamWriter abstract class.
*
* <P>Before any packet data is written to the bit stream (even in simulation
* mode) the complete header should be written to the HeaderEncoder object
* supplied to the constructor, following the procedure explained in the
* HeaderEncoder class. Otherwise incorrect estimates are given by
* getMaxAvailableBytes() for rate allocation.
*
* @see CodestreamWriter
* @see HeaderEncoder
*/
public class FileCodestreamWriter extends CodestreamWriter
implements Markers {
/**
* The upper limit for the value of the Nsop field of the SOP marker
*/
private final static int SOP_MARKER_LIMIT = 65535;
/**
* The default buffer length, 1024 bytes
*/
public static int DEF_BUF_LEN = 1024;
/**
* The number of bytes already written to the bit stream, excluding the
* header length, magic number and header length info.
*/
int ndata = 0;
/**
* Array used to store the SOP markers values
*/
byte[] sopMarker;
/**
* Array used to store the EPH markers values
*/
byte[] ephMarker;
/**
* The packet index (when start of packet markers i.e. SOP markers) are
* used.
*/
int packetIdx = 0;
/**
* Index of the current tile
*/
private final int tileIdx = 0;
/**
* The file to write
*/
private final OutputStream out;
/**
* Offset of end of last packet containing ROI information
*/
private int offLastROIPkt = 0;
/**
* Length of last packets containing no ROI information
*/
private int lenLastNoROI = 0;
/**
* Opens the file 'file' for writing the bit stream, using the 'he' header
* encoder. The magic number is written to the bit stream. Normally, the
* header encoder must be empty (i.e. no data has been written to it
* yet). A BufferedOutputStream is used on top of the file to increase
* throughput, the length of the buffer is DEF_BUF_LEN.
*
* @param file The file where to write the bit stream
* @param mb The maximum number of bytes that can be written to the bit
* stream.
* @throws IOException If an error occurs while trying to open the file
* for writing or while writing the magic number.
*/
public FileCodestreamWriter(File file, int mb)
throws IOException {
super(mb);
out = new BufferedOutputStream(new FileOutputStream(file), DEF_BUF_LEN);
initSOP_EPHArrays();
}
/**
* Opens the file named 'fname' for writing the bit stream, using the 'he'
* header encoder. The magic number is written to the bit
* stream. Normally, the header encoder must be empty (i.e. no data has
* been written to it yet). A BufferedOutputStream is used on top of the
* file to increase throughput, the length of the buffer is DEF_BUF_LEN.
*
* @param fname The name of file where to write the bit stream
* @param mb The maximum number of bytes that can be written to the bit
* stream.
* @throws IOException If an error occurs while trying to open the file
* for writing or while writing the magic number.
*/
public FileCodestreamWriter(String fname, int mb)
throws IOException {
super(mb);
out = new BufferedOutputStream(new FileOutputStream(fname),
DEF_BUF_LEN);
initSOP_EPHArrays();
}
/**
* Uses the output stream 'os' for writing the bit stream, using the 'he'
* header encoder. The magic number is written to the bit
* stream. Normally, the header encoder must be empty (i.e. no data has
* been written to it yet). No BufferedOutputStream is used on top of the
* output stream 'os'.
*
* @param os The output stream where to write the bit stream.
* @param mb The maximum number of bytes that can be written to the bit
* stream.
* @throws IOException If an error occurs while writing the magic
* number to the 'os' output stream.
*/
public FileCodestreamWriter(OutputStream os, int mb)
throws IOException {
super(mb);
out = os;
initSOP_EPHArrays();
}
/**
* Returns the number of bytes remaining available in the bit stream. This
* is the maximum allowed number of bytes minus the number of bytes that
* have already been written to the bit stream. If more bytes have been
* written to the bit stream than the maximum number of allowed bytes,
* then a negative value is returned.
*
* @return The number of bytes remaining available in the bit stream.
*/
public final int getMaxAvailableBytes() {
return maxBytes - ndata;
}
/**
* Returns the current length of the entire bit stream.
*
* @return the current length of the bit stream
*/
public int getLength() {
if (getMaxAvailableBytes() >= 0) {
return ndata;
} else {
return maxBytes;
}
}
/**
* Writes a packet head to the bit stream and returns the number of bytes
* used by this header. It returns the total number of bytes that the
* packet head takes in the bit stream. If in simulation mode then no data
* is written to the bit stream but the number of bytes is
* calculated. This can be used for iterative rate allocation.
*
* <P>If the length of the data that is to be written to the bit stream is
* more than the space left (as returned by getMaxAvailableBytes()) only
* the data that does not exceed the allowed length is written, the rest
* is discarded. However the value returned by the method is the total
* length of the packet, as if all of it was written to the bit stream.
*
* <P>If the bit stream header has not been commited yet and 'sim' is
* false, then the bit stream header is automatically commited (see
* commitBitstreamHeader() method) before writting the packet.
*
* @param head The packet head data.
* @param hlen The number of bytes in the packet head.
* @param sim Simulation mode flag. If true nothing is written to the bit
* stream, but the number of bytes that would be written is returned.
* @param sop Start of packet header marker flag. This flag indicates
* whether or not SOP markers should be written. If true, SOP markers
* should be written, if false, they should not.
* @param eph End of Packet Header marker flag. This flag indicates
* whether or not EPH markers should be written. If true, EPH markers
* should be written, if false, they should not.
* @return The number of bytes spent by the packet head.
* @throws IOException If an I/O error occurs while writing to the
* output stream.
* @see #commitBitstreamHeader
*/
public int writePacketHead(byte[] head, int hlen, boolean sim,
boolean sop, boolean eph) throws IOException {
int len = hlen
+ (sop ? Markers.SOP_LENGTH : 0)
+ (eph ? Markers.EPH_LENGTH : 0);
// If not in simulation mode write the data
if (!sim) {
// Write the head bytes
if (getMaxAvailableBytes() < len) {
len = getMaxAvailableBytes();
}
if (len > 0) {
// Write Start Of Packet header markers if necessary
if (sop) {
// The first 4 bytes of the array have been filled in the
// classe's constructor.
sopMarker[4] = (byte) (packetIdx >> 8);
sopMarker[5] = (byte) (packetIdx);
out.write(sopMarker, 0, Markers.SOP_LENGTH);
packetIdx++;
if (packetIdx > SOP_MARKER_LIMIT) {
// Reset SOP value as we have reached its upper limit
packetIdx = 0;
}
}
out.write(head, 0, hlen);
// Update data length
ndata += len;
// Write End of Packet Header markers if necessary
if (eph) {
out.write(ephMarker, 0, Markers.EPH_LENGTH);
}
// Deal with ROI Information
lenLastNoROI += len;
}
}
return len;
}
/**
* Writes a packet body to the bit stream and returns the number of bytes
* used by this body .If in simulation mode then no data is written to the
* bit stream but the number of bytes is calculated. This can be used for
* iterative rate allocation.
*
* <P>If the length of the data that is to be written to the bit stream is
* more than the space left (as returned by getMaxAvailableBytes()) only
* the data that does not exceed the allowed length is written, the rest
* is discarded. However the value returned by the method is the total
* length of the packet body , as if all of it was written to the bit
* stream.
*
* @param body The packet body data.
* @param blen The number of bytes in the packet body.
* @param sim Simulation mode flag. If true nothing is written to the bit
* stream, but the number of bytes that would be written is returned.
* @param roiInPkt Whether or not this packet contains ROI information
* @param roiLen Number of byte to read in packet body to get all the ROI
* information
* @return The number of bytes spent by the packet body.
* @throws IOException If an I/O error occurs while writing to the
* output stream.
* @see #commitBitstreamHeader
*/
public int writePacketBody(byte[] body, int blen, boolean sim,
boolean roiInPkt, int roiLen)
throws IOException {
int len = blen;
// If not in simulation mode write the data
if (!sim) {
// Write the body bytes
len = blen;
if (getMaxAvailableBytes() < len) {
len = getMaxAvailableBytes();
}
if (blen > 0) {
out.write(body, 0, len);
}
// Update data length
ndata += len;
// Deal with ROI information
if (roiInPkt) {
offLastROIPkt += lenLastNoROI + roiLen;
lenLastNoROI = len - roiLen;
} else {
lenLastNoROI += len;
}
}
return len;
}
/**
* Writes the EOC marker and closes the underlying stream.
*
* @throws IOException If an error occurs while closing the underlying
* stream.
*/
public void close() throws IOException {
// Write the EOC marker and close the codestream.
out.write(EOC >> 8);
out.write(EOC);
ndata += 2; // Add two to length of codestream for EOC marker
out.close();
}
/**
* Gives the offset of the end of last packet containing ROI information
*
* @return End of last ROI packet
*/
public int getOffLastROIPkt() {
return offLastROIPkt;
}
/**
* Writes the header data in the codestream and actualize ndata with the
* header length. The header is either a MainHeaderEncoder or a
* TileHeaderEncoder.
*
* @param he The current header encoder.
* @throws IOException If an I/O error occurs while writing the data.
*/
public void commitBitstreamHeader(HeaderEncoder he) throws IOException {
// Actualize ndata
ndata += he.getLength();
he.writeTo(out); // Write the header
// Reset packet index used for SOP markers
packetIdx = 0;
// Deal with ROI information
lenLastNoROI += he.getLength();
}
/**
* Performs the initialisation of the arrays that are used to store the
* values used to write SOP and EPH markers
*/
private void initSOP_EPHArrays() {
// Allocate and set first values of SOP marker as they will not be
// modified
sopMarker = new byte[Markers.SOP_LENGTH];
sopMarker[0] = (byte) (Markers.SOP >> 8);
sopMarker[1] = (byte) Markers.SOP;
sopMarker[2] = (byte) 0x00;
sopMarker[3] = (byte) 0x04;
// Allocate and set values of EPH marker as they will not be
// modified
ephMarker = new byte[Markers.EPH_LENGTH];
ephMarker[0] = (byte) (Markers.EPH >> 8);
ephMarker[1] = (byte) Markers.EPH;
}
}

View file

@ -0,0 +1,518 @@
/*
* $RCSfile: TagTreeEncoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:03 $
* $State: Exp $
*
* Class: TagTreeEncoder
*
* Description: Encoder of tag trees
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.codestream.writer;
import org.xbib.graphics.jpeg2000.j2k.util.ArrayUtil;
import org.xbib.graphics.jpeg2000.j2k.codestream.reader.TagTreeDecoder;
/**
* This class implements the tag tree encoder. A tag tree codes a 2D
* matrix of integer elements in an efficient way. The encoding
* procedure 'encode()' codes information about a value of the matrix,
* given a threshold. The procedure encodes the sufficient information
* to identify whether or not the value is greater than or equal to
* the threshold.
*
* <P>The tag tree saves encoded information to a BitOutputBuffer.
*
* <P>A particular and useful property of tag trees is that it is
* possible to change a value of the matrix, provided both new and old
* values of the element are both greater than or equal to the largest
* threshold which has yet been supplied to the coding procedure
* 'encode()'. This property can be exploited through the 'setValue()'
* method.
*
* <P>This class allows saving the state of the tree at any point and
* restoring it at a later time, by calling save() and restore().
*
* <P>A tag tree can also be reused, or restarted, if one of the
* reset() methods is called.
*
* <P>The TagTreeDecoder class implements the tag tree decoder.
*
* <P>Tag trees that have one dimension, or both, as 0 are allowed for
* convenience. Of course no values can be set or coded in such cases.
*
* @see BitOutputBuffer
* @see TagTreeDecoder
*/
public class TagTreeEncoder {
/**
* The horizontal dimension of the base level
*/
protected int w;
/**
* The vertical dimensions of the base level
*/
protected int h;
/**
* The number of levels in the tag tree
*/
protected int lvls;
/**
* The tag tree values. The first index is the level, starting at
* level 0 (leafs). The second index is the element within the
* level, in lexicographical order.
*/
protected int[][] treeV;
/**
* The tag tree state. The first index is the level, starting at
* level 0 (leafs). The second index is the element within the
* level, in lexicographical order.
*/
protected int[][] treeS;
/**
* The saved tag tree values. The first index is the level,
* starting at level 0 (leafs). The second index is the element
* within the level, in lexicographical order.
*/
protected int[][] treeVbak;
/**
* The saved tag tree state. The first index is the level, starting at
* level 0 (leafs). The second index is the element within the
* level, in lexicographical order.
*/
protected int[][] treeSbak;
/**
* The saved state. If true the values and states of the tree
* have been saved since the creation or last reset.
*/
protected boolean saved;
/**
* Creates a tag tree encoder with 'w' elements along the
* horizontal dimension and 'h' elements along the vertical
* direction. The total number of elements is thus 'vdim' x
* 'hdim'.
*
* <P>The values of all elements are initialized to Integer.MAX_VALUE.
*
* @param h The number of elements along the horizontal direction.
* @param w The number of elements along the vertical direction.
*/
public TagTreeEncoder(int h, int w) {
int k;
// Check arguments
if (w < 0 || h < 0) {
throw new IllegalArgumentException();
}
// Initialize elements
init(w, h);
// Set values to max
for (k = treeV.length - 1; k >= 0; k--) {
ArrayUtil.intArraySet(treeV[k], Integer.MAX_VALUE);
}
}
/**
* Creates a tag tree encoder with 'w' elements along the
* horizontal dimension and 'h' elements along the vertical
* direction. The total number of elements is thus 'vdim' x
* 'hdim'. The values of the leafs in the tag tree are initialized
* to the values of the 'val' array.
*
* <P>The values in the 'val' array are supposed to appear in
* lexicographical order, starting at index 0.
*
* @param h The number of elements along the horizontal direction.
* @param w The number of elements along the vertical direction.
* @param val The values with which initialize the leafs of the
* tag tree.
*/
public TagTreeEncoder(int h, int w, int[] val) {
int k;
// Check arguments
if (w < 0 || h < 0 || val.length < w * h) {
throw new IllegalArgumentException();
}
// Initialize elements
init(w, h);
// Update leaf values
for (k = w * h - 1; k >= 0; k--) {
treeV[0][k] = val[k];
}
// Calculate values at other levels
recalcTreeV();
}
/**
* Returns the number of leafs along the horizontal direction.
*
* @return The number of leafs along the horizontal direction.
*/
public final int getWidth() {
return w;
}
/**
* Returns the number of leafs along the vertical direction.
*
* @return The number of leafs along the vertical direction.
*/
public final int getHeight() {
return h;
}
/**
* Initializes the variables of this class, given the dimensions
* at the base level (leaf level). All the state ('treeS' array)
* and values ('treeV' array) are intialized to 0. This method is
* called by the constructors.
*
* @param w The number of elements along the vertical direction.
* @param h The number of elements along the horizontal direction.
*/
private void init(int w, int h) {
int i;
// Initialize dimensions
this.w = w;
this.h = h;
// Calculate the number of levels
if (w == 0 || h == 0) {
lvls = 0;
} else {
lvls = 1;
while (h != 1 || w != 1) { // Loop until we reach root
w = (w + 1) >> 1;
h = (h + 1) >> 1;
lvls++;
}
}
// Allocate tree values and states
// (no need to initialize to 0 since it's the default)
treeV = new int[lvls][];
treeS = new int[lvls][];
w = this.w;
h = this.h;
for (i = 0; i < lvls; i++) {
treeV[i] = new int[h * w];
treeS[i] = new int[h * w];
w = (w + 1) >> 1;
h = (h + 1) >> 1;
}
}
/**
* Recalculates the values of the elements in the tag tree, in
* levels 1 and up, based on the values of the leafs (level 0).
*/
private void recalcTreeV() {
int m, n, bi, lw, tm1, tm2, lh, k;
// Loop on all other levels, updating minimum
for (k = 0; k < lvls - 1; k++) {
// Visit all elements in level
lw = (w + (1 << k) - 1) >> k;
lh = (h + (1 << k) - 1) >> k;
for (m = ((lh >> 1) << 1) - 2; m >= 0; m -= 2) { // All quads with 2 lines
for (n = ((lw >> 1) << 1) - 2; n >= 0; n -= 2) { // All quads with 2 columns
// Take minimum of 4 elements and put it in higher
// level
bi = m * lw + n;
tm1 = (treeV[k][bi] < treeV[k][bi + 1]) ?
treeV[k][bi] : treeV[k][bi + 1];
tm2 = (treeV[k][bi + lw] < treeV[k][bi + lw + 1]) ?
treeV[k][bi + lw] : treeV[k][bi + lw + 1];
treeV[k + 1][(m >> 1) * ((lw + 1) >> 1) + (n >> 1)] =
tm1 < tm2 ? tm1 : tm2;
}
// Now we may have quad with 1 column, 2 lines
if (lw % 2 != 0) {
n = ((lw >> 1) << 1);
// Take minimum of 2 elements and put it in higher
// level
bi = m * lw + n;
treeV[k + 1][(m >> 1) * ((lw + 1) >> 1) + (n >> 1)] =
(treeV[k][bi] < treeV[k][bi + lw]) ?
treeV[k][bi] : treeV[k][bi + lw];
}
}
// Now we may have quads with 1 line, 2 or 1 columns
if (lh % 2 != 0) {
m = ((lh >> 1) << 1);
for (n = ((lw >> 1) << 1) - 2; n >= 0; n -= 2) { // All quads with 2 columns
// Take minimum of 2 elements and put it in higher
// level
bi = m * lw + n;
treeV[k + 1][(m >> 1) * ((lw + 1) >> 1) + (n >> 1)] =
(treeV[k][bi] < treeV[k][bi + 1]) ?
treeV[k][bi] : treeV[k][bi + 1];
}
// Now we may have quad with 1 column, 1 line
if (lw % 2 != 0) {
// Just copy the value
n = ((lw >> 1) << 1);
treeV[k + 1][(m >> 1) * ((lw + 1) >> 1) + (n >> 1)] =
treeV[k][m * lw + n];
}
}
}
}
/**
* Changes the value of a leaf in the tag tree. The new and old
* values of the element must be not smaller than the largest
* threshold which has yet been supplied to 'encode()'.
*
* @param m The vertical index of the element.
* @param n The horizontal index of the element.
* @param v The new value of the element.
*/
public void setValue(int m, int n, int v) {
int k, idx;
// Check arguments
if (lvls == 0 || n < 0 || n >= w || v < treeS[lvls - 1][0] ||
treeV[0][m * w + n] < treeS[lvls - 1][0]) {
throw new IllegalArgumentException();
}
// Update the leaf value
treeV[0][m * w + n] = v;
// Update all parents
for (k = 1; k < lvls; k++) {
idx = (m >> k) * ((w + (1 << k) - 1) >> k) + (n >> k);
if (v < treeV[k][idx]) {
// We need to update minimum and continue checking
// in higher levels
treeV[k][idx] = v;
} else {
// We are done: v is equal or less to minimum
// in this level, no other minimums to update.
break;
}
}
}
/**
* Sets the values of the leafs to the new set of values and
* updates the tag tree accordingly. No leaf can change its value
* if either the new or old value is smaller than largest
* threshold which has yet been supplied to 'encode()'. However
* such a leaf can keep its old value (i.e. new and old value must
* be identical.
*
* <P>This method is more efficient than the setValue() method if
* a large proportion of the leafs change their value. Note that
* for leafs which don't have their value defined yet the value
* should be Integer.MAX_VALUE (which is the default
* initialization value).
*
* @param val The new values for the leafs, in lexicographical order.
* @see #setValue
*/
public void setValues(int[] val) {
int i, maxt;
if (lvls == 0) { // Can't set values on empty tree
throw new IllegalArgumentException();
}
// Check the values
maxt = treeS[lvls - 1][0];
for (i = w * h - 1; i >= 0; i--) {
if ((treeV[0][i] < maxt || val[i] < maxt) &&
treeV[0][i] != val[i]) {
throw new IllegalArgumentException();
}
// Update leaf value
treeV[0][i] = val[i];
}
// Recalculate tree at other levels
recalcTreeV();
}
/**
* Encodes information for the specified element of the tree,
* given the threshold and sends it to the 'out' stream. The
* information that is coded is whether or not the value of the
* element is greater than or equal to the value of the threshold.
*
* @param m The vertical index of the element.
* @param n The horizontal index of the element.
* @param t The threshold to use for encoding. It must be non-negative.
* @param out The stream where to write the coded information.
*/
public void encode(int m, int n, int t, BitOutputBuffer out) {
int k, ts, idx, tmin;
// Check arguments
if (m >= h || n >= w || t < 0) {
throw new IllegalArgumentException();
}
// Initialize
k = lvls - 1;
tmin = treeS[k][0];
// Loop on levels
while (true) {
// Index of element in level 'k'
idx = (m >> k) * ((w + (1 << k) - 1) >> k) + (n >> k);
// Cache state
ts = treeS[k][idx];
if (ts < tmin) {
ts = tmin;
}
while (t > ts) {
if (treeV[k][idx] > ts) {
out.writeBit(0); // Send '0' bit
} else if (treeV[k][idx] == ts) {
out.writeBit(1); // Send '1' bit
} else { // we are done: set ts and get out of this while
ts = t;
break;
}
// Increment of treeS[k][idx]
ts++;
}
// Update state
treeS[k][idx] = ts;
// Update tmin or terminate
if (k > 0) {
tmin = ts < treeV[k][idx] ? ts : treeV[k][idx];
k--;
} else {
// Terminate
return;
}
}
}
/**
* Saves the current values and state of the tree. Calling
* restore() restores the tag tree the saved state.
*
* @see #restore
*/
public void save() {
int k, i;
if (treeVbak == null) { // Nothing saved yet
// Allocate saved arrays
// treeV and treeS have the same dimensions
treeVbak = new int[lvls][];
treeSbak = new int[lvls][];
for (k = lvls - 1; k >= 0; k--) {
treeVbak[k] = new int[treeV[k].length];
treeSbak[k] = new int[treeV[k].length];
}
}
// Copy the arrays
for (k = treeV.length - 1; k >= 0; k--) {
System.arraycopy(treeV[k], 0, treeVbak[k], 0, treeV[k].length);
System.arraycopy(treeS[k], 0, treeSbak[k], 0, treeS[k].length);
}
// Set saved state
saved = true;
}
/**
* Restores the saved values and state of the tree. An
* IllegalArgumentException is thrown if the tree values and state
* have not been saved yet.
*
* @see #save
*/
public void restore() {
int k, i;
if (!saved) { // Nothing saved yet
throw new IllegalArgumentException();
}
// Copy the arrays
for (k = lvls - 1; k >= 0; k--) {
System.arraycopy(treeVbak[k], 0, treeV[k], 0, treeV[k].length);
System.arraycopy(treeSbak[k], 0, treeS[k], 0, treeS[k].length);
}
}
/**
* Resets the tree values and state. All the values are set to
* Integer.MAX_VALUE and the states to 0.
*/
public void reset() {
int k;
// Set all values to Integer.MAX_VALUE
// and states to 0
for (k = lvls - 1; k >= 0; k--) {
ArrayUtil.intArraySet(treeV[k], Integer.MAX_VALUE);
ArrayUtil.intArraySet(treeS[k], 0);
}
// Invalidate saved tree
saved = false;
}
/**
* Resets the tree values and state. The values are set to the
* values in 'val'. The states are all set to 0.
*
* @param val The new values for the leafs, in lexicographical order.
*/
public void reset(int[] val) {
int k;
// Set values for leaf level
for (k = w * h - 1; k >= 0; k--) {
treeV[0][k] = val[k];
}
// Calculate values at other levels
recalcTreeV();
// Set all states to 0
for (k = lvls - 1; k >= 0; k--) {
ArrayUtil.intArraySet(treeS[k], 0);
}
// Invalidate saved tree
saved = false;
}
}

View file

@ -0,0 +1,221 @@
/*
* $RCSfile: DecoderSpecs.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:03 $
* $State: Exp $
*
* Class: DecoderSpecs
*
* Description: Hold all decoder specifications
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.decoder;
import org.xbib.graphics.jpeg2000.j2k.IntegerSpec;
import org.xbib.graphics.jpeg2000.j2k.ModuleSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.CBlkSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.PrecinctSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.image.CompTransfSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.GuardBitsSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.QuantStepSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.quantization.QuantTypeSpec;
import org.xbib.graphics.jpeg2000.j2k.roi.MaxShiftSpec;
import org.xbib.graphics.jpeg2000.j2k.wavelet.synthesis.SynWTFilterSpec;
/**
* This class holds references to each module specifications used in the
* decoding chain. This avoid big amount of arguments in method calls. A
* specification contains values of each tile-component for one module. All
* members must be instance of ModuleSpec class (or its children).
*
* @see ModuleSpec
*/
public class DecoderSpecs implements Cloneable {
/**
* ICC Profiling specifications
*/
public ModuleSpec iccs;
/**
* ROI maxshift value specifications
*/
public MaxShiftSpec rois;
/**
* Quantization type specifications
*/
public QuantTypeSpec qts;
/**
* Quantization normalized base step size specifications
*/
public QuantStepSizeSpec qsss;
/**
* Number of guard bits specifications
*/
public GuardBitsSpec gbs;
/**
* Analysis wavelet filters specifications
*/
public SynWTFilterSpec wfs;
/**
* Number of decomposition levels specifications
*/
public IntegerSpec dls;
/**
* Number of layers specifications
*/
public IntegerSpec nls;
/**
* Progression order specifications
*/
public IntegerSpec pos;
/**
* The Entropy decoder options specifications
*/
public ModuleSpec ecopts;
/**
* The component transformation specifications
*/
public CompTransfSpec cts;
/**
* The progression changes specifications
*/
public ModuleSpec pcs;
/**
* The error resilience specifications concerning the entropy
* decoder
*/
public ModuleSpec ers;
/**
* Precinct partition specifications
*/
public PrecinctSizeSpec pss;
/**
* The Start Of Packet (SOP) markers specifications
*/
public ModuleSpec sops;
/**
* The End of Packet Headers (EPH) markers specifications
*/
public ModuleSpec ephs;
/**
* Code-blocks sizes specification
*/
public CBlkSizeSpec cblks;
/**
* Packed packet header specifications
*/
public ModuleSpec pphs;
/**
* Initialize all members with the given number of tiles and components.
*
* @param nt Number of tiles
* @param nc Number of components
*/
public DecoderSpecs(int nt, int nc) {
// Quantization
qts = new QuantTypeSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
qsss = new QuantStepSizeSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
gbs = new GuardBitsSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
// Wavelet transform
wfs = new SynWTFilterSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
dls = new IntegerSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
// Component transformation
cts = new CompTransfSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
// Entropy decoder
ecopts = new ModuleSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
ers = new ModuleSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
cblks = new CBlkSizeSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP);
// Precinct partition
pss = new PrecinctSizeSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE_COMP, dls);
// Codestream
nls = new IntegerSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE);
pos = new IntegerSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE);
pcs = new ModuleSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE);
sops = new ModuleSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE);
ephs = new ModuleSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE);
pphs = new ModuleSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE);
iccs = new ModuleSpec(nt, nc, ModuleSpec.SPEC_TYPE_TILE);
pphs.setDefault(Boolean.FALSE);
}
/**
* Returns a copy of the current object.
*/
public DecoderSpecs getCopy() {
DecoderSpecs decSpec2;
try {
decSpec2 = (DecoderSpecs) this.clone();
} catch (CloneNotSupportedException e) {
throw new Error("Cannot clone the DecoderSpecs instance");
}
// Quantization
decSpec2.qts = (QuantTypeSpec) qts.getCopy();
decSpec2.qsss = (QuantStepSizeSpec) qsss.getCopy();
decSpec2.gbs = (GuardBitsSpec) gbs.getCopy();
// Wavelet transform
decSpec2.wfs = (SynWTFilterSpec) wfs.getCopy();
decSpec2.dls = (IntegerSpec) dls.getCopy();
// Component transformation
decSpec2.cts = (CompTransfSpec) cts.getCopy();
// ROI
if (rois != null) {
decSpec2.rois = (MaxShiftSpec) rois.getCopy();
}
return decSpec2;
}
}

View file

@ -0,0 +1,454 @@
/*
* $RCSfile: CBlkSizeSpec.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:04 $
* $State: Exp $
*
* Class: CBlkSizeSpec
*
* Description: Specification of the code-blocks size
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy;
import org.xbib.graphics.jpeg2000.J2KWriteParam;
import org.xbib.graphics.jpeg2000.j2k.ModuleSpec;
import org.xbib.graphics.jpeg2000.j2k.util.MathUtil;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
/**
* This class extends ModuleSpec class for code-blocks sizes holding purposes.
*
* <P>It stores the size a of code-block.
*/
public class CBlkSizeSpec extends ModuleSpec {
/**
* Name of the option
*/
private static final String optName = "Cblksiz";
private final String defaultValue = "64 64";
/**
* The maximum code-block width
*/
private int maxCBlkWidth = 0;
/**
* The maximum code-block height
*/
private int maxCBlkHeight = 0;
/**
* Creates a new CBlkSizeSpec object for the specified number of tiles and
* components.
*
* @param nt The number of tiles
* @param nc The number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both.
*/
public CBlkSizeSpec(int nt, int nc, byte type) {
super(nt, nc, type);
}
/**
* Creates a new CBlkSizeSpec object for the specified number of tiles and
* components and the ParameterList instance.
*
* @param nt The number of tiles
* @param nc The number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both.
* @param wp the write params
* @param values the values
*/
public CBlkSizeSpec(int nt, int nc, byte type, J2KWriteParam wp, String values) {
super(nt, nc, type);
boolean firstVal = true;
specified = values;
String param = values; //"64 64";
if (param == null)
param = defaultValue; // the default
//pl.getParameter(optName);
// Precinct partition is used : parse arguments
StringTokenizer stk = new StringTokenizer(param);
byte curSpecType = SPEC_DEF; // Specification type of the
// current parameter
boolean[] tileSpec = null; // Tiles concerned by the specification
boolean[] compSpec = null; // Components concerned by the specification
int i, xIdx, ci, ti;
String word = null; // current word
String errMsg = null;
while (stk.hasMoreTokens()) {
word = stk.nextToken();
switch (word.charAt(0)) {
case 't': // Tiles specification
tileSpec = parseIdx(word, nTiles);
if (curSpecType == SPEC_COMP_DEF) {
curSpecType = SPEC_TILE_COMP;
} else {
curSpecType = SPEC_TILE_DEF;
}
break;
case 'c': // Components specification
compSpec = parseIdx(word, nComp);
if (curSpecType == SPEC_TILE_DEF) {
curSpecType = SPEC_TILE_COMP;
} else {
curSpecType = SPEC_COMP_DEF;
}
break;
default:
if (!Character.isDigit(word.charAt(0))) {
errMsg = "Bad construction for parameter: " + word;
throw new IllegalArgumentException(errMsg);
}
Integer[] dim = new Integer[2];
// Get code-block's width
try {
dim[0] = Integer.valueOf(word);
// Check that width is not >
// StdEntropyCoderOptions.MAX_CB_DIM
if (dim[0].intValue() > StdEntropyCoderOptions.MAX_CB_DIM) {
errMsg = "'" + optName + "' option : the code-block's " +
"width cannot be greater than " +
StdEntropyCoderOptions.MAX_CB_DIM;
throw new IllegalArgumentException(errMsg);
}
// Check that width is not <
// StdEntropyCoderOptions.MIN_CB_DIM
if (dim[0].intValue() < StdEntropyCoderOptions.MIN_CB_DIM) {
errMsg = "'" + optName + "' option : the code-block's " +
"width cannot be less than " +
StdEntropyCoderOptions.MIN_CB_DIM;
throw new IllegalArgumentException(errMsg);
}
// Check that width is a power of 2
if (dim[0].intValue() !=
(1 << MathUtil.log2(dim[0].intValue()))) {
errMsg = "'" + optName + "' option : the code-block's " +
"width must be a power of 2";
throw new IllegalArgumentException(errMsg);
}
} catch (NumberFormatException e) {
errMsg = "'" + optName + "' option : the code-block's " +
"width could not be parsed.";
throw new IllegalArgumentException(errMsg);
}
// Get the next word in option
try {
word = stk.nextToken();
} catch (NoSuchElementException e) {
errMsg = "'" + optName + "' option : could not parse the " +
"code-block's height";
throw new IllegalArgumentException(errMsg);
}
// Get the code-block's height
try {
dim[1] = Integer.valueOf(word);
// Check that height is not >
// StdEntropyCoderOptions.MAX_CB_DIM
if (dim[1].intValue() > StdEntropyCoderOptions.MAX_CB_DIM) {
errMsg = "'" + optName + "' option : the code-block's " +
"height cannot be greater than " +
StdEntropyCoderOptions.MAX_CB_DIM;
throw new IllegalArgumentException(errMsg);
}
// Check that height is not <
// StdEntropyCoderOptions.MIN_CB_DIM
if (dim[1].intValue() < StdEntropyCoderOptions.MIN_CB_DIM) {
errMsg = "'" + optName + "' option : the code-block's " +
"height cannot be less than " +
StdEntropyCoderOptions.MIN_CB_DIM;
throw new IllegalArgumentException(errMsg);
}
// Check that height is a power of 2
if (dim[1].intValue() !=
(1 << MathUtil.log2(dim[1].intValue()))) {
errMsg = "'" + optName + "' option : the code-block's " +
"height must be a power of 2";
throw new IllegalArgumentException(errMsg);
}
// Check that the code-block 'area' (i.e. width*height) is
// not greater than StdEntropyCoderOptions.MAX_CB_AREA
if (dim[0].intValue() * dim[1].intValue() >
StdEntropyCoderOptions.MAX_CB_AREA) {
errMsg = "'" + optName + "' option : The " +
"code-block's area (i.e. width*height) " +
"cannot be greater than " +
StdEntropyCoderOptions.MAX_CB_AREA;
throw new IllegalArgumentException(errMsg);
}
} catch (NumberFormatException e) {
errMsg = "'" + optName + "' option : the code-block's height " +
"could not be parsed.";
throw new IllegalArgumentException(errMsg);
}
// Store the maximum dimensions if necessary
if (dim[0].intValue() > maxCBlkWidth) {
maxCBlkWidth = dim[0].intValue();
}
if (dim[1].intValue() > maxCBlkHeight) {
maxCBlkHeight = dim[1].intValue();
}
if (firstVal) {
// This is the first time a value is given so we set it as
// the default one
setDefault(dim);
firstVal = false;
}
switch (curSpecType) {
case SPEC_DEF:
setDefault(dim);
break;
case SPEC_TILE_DEF:
for (ti = tileSpec.length - 1; ti >= 0; ti--) {
if (tileSpec[ti]) {
setTileDef(ti, dim);
}
}
break;
case SPEC_COMP_DEF:
for (ci = compSpec.length - 1; ci >= 0; ci--) {
if (compSpec[ci]) {
setCompDef(ci, dim);
}
}
break;
default:
for (ti = tileSpec.length - 1; ti >= 0; ti--) {
for (ci = compSpec.length - 1; ci >= 0; ci--) {
if (tileSpec[ti] && compSpec[ci]) {
setTileCompVal(ti, ci, dim);
}
}
}
break;
}
} // end switch
}
}
/**
* Returns the maximum code-block's width
*/
public int getMaxCBlkWidth() {
return maxCBlkWidth;
}
/**
* Returns the maximum code-block's height
*/
public int getMaxCBlkHeight() {
return maxCBlkHeight;
}
/**
* Returns the code-block width :
*
* <ul>
* <li>for the specified tile/component</li>
* <li>for the specified tile</li>
* <li>for the specified component</li>
* <li>default value</li>
* </ul>
* <p>
* The value returned depends on the value of the variable 'type' which
* can take the following values :<br>
*
* <ul>
* <li>SPEC_DEF -> Default value is returned. t and c values are
* ignored</li>
* <li>SPEC_COMP_DEF -> Component default value is returned. t value is
* ignored</li>
* <li>SPEC_TILE_DEF -> Tile default value is returned. c value is
* ignored</li>
* <li>SPEC_TILE_COMP -> Tile/Component value is returned.</li>
* </ul>
*
* @param type The type of the value we want to be returned
* @param t The tile index
* @param c the component index
* @return The code-block width for the specified tile and component
*/
public int getCBlkWidth(byte type, int t, int c) {
Integer[] dim = null;
switch (type) {
case SPEC_DEF:
dim = (Integer[]) getDefault();
break;
case SPEC_COMP_DEF:
dim = (Integer[]) getCompDef(c);
break;
case SPEC_TILE_DEF:
dim = (Integer[]) getTileDef(t);
break;
case SPEC_TILE_COMP:
dim = (Integer[]) getTileCompVal(t, c);
}
return dim[0].intValue();
}
/**
* Returns the code-block height:
*
* <ul>
* <li>for the specified tile/component</li>
* <li>for the specified tile</li>
* <li>for the specified component</li>
* <li>default value</li>
* </ul>
* <p>
* The value returned depends on the value of the variable 'type' which
* can take the following values :
*
* <ul>
* <li>SPEC_DEF -> Default value is returned. t and c values are
* ignored</li>
* <li>SPEC_COMP_DEF -> Component default value is returned. t value is
* ignored</li>
* <li>SPEC_TILE_DEF -> Tile default value is returned. c value is
* ignored</li>
* <li>SPEC_TILE_COMP -> Tile/Component value is returned.</li>
* </ul>
*
* @param type The type of the value we want to be returned
* @param t The tile index
* @param c the component index
* @return The code-block height for the specified tile and component
*/
public int getCBlkHeight(byte type, int t, int c) {
Integer[] dim = null;
switch (type) {
case SPEC_DEF:
dim = (Integer[]) getDefault();
break;
case SPEC_COMP_DEF:
dim = (Integer[]) getCompDef(c);
break;
case SPEC_TILE_DEF:
dim = (Integer[]) getTileDef(t);
break;
case SPEC_TILE_COMP:
dim = (Integer[]) getTileCompVal(t, c);
}
return dim[1].intValue();
}
/**
* Sets default value for this module
*
* @param value Default value
*/
public void setDefault(Object value) {
super.setDefault(value);
// Store the biggest code-block dimensions
storeHighestDims((Integer[]) value);
}
/**
* Sets default value for specified tile and specValType tag if allowed by
* its priority.
*
* @param t Tile index.
* @param value Tile's default value
*/
public void setTileDef(int t, Object value) {
super.setTileDef(t, value);
// Store the biggest code-block dimensions
storeHighestDims((Integer[]) value);
}
/**
* Sets default value for specified component and specValType tag if
* allowed by its priority.
*
* @param c Component index
* @param value Component's default value
*/
public void setCompDef(int c, Object value) {
super.setCompDef(c, value);
// Store the biggest code-block dimensions
storeHighestDims((Integer[]) value);
}
/**
* Sets value for specified tile-component.
*
* @param t Tie index
* @param c Component index
* @param value Tile-component's value
*/
public void setTileCompVal(int t, int c, Object value) {
super.setTileCompVal(t, c, value);
// Store the biggest code-block dimensions
storeHighestDims((Integer[]) value);
}
/**
* Stores the highest code-block width and height
*
* @param dim The 2 elements array that contains the code-block width and
* height.
*/
private void storeHighestDims(Integer[] dim) {
// Store the biggest code-block dimensions
if (dim[0].intValue() > maxCBlkWidth) {
maxCBlkWidth = dim[0].intValue();
}
if (dim[1].intValue() > maxCBlkHeight) {
maxCBlkHeight = dim[1].intValue();
}
}
}

View file

@ -0,0 +1,117 @@
/*
* $RCSfile: CodedCBlk.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:04 $
* $State: Exp $
*
* Class: CodedCBlk
*
* Description: The generic coded (compressed) code-block
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy;
import org.xbib.graphics.jpeg2000.j2k.entropy.decoder.DecLyrdCBlk;
import org.xbib.graphics.jpeg2000.j2k.entropy.encoder.CBlkRateDistStats;
/**
* This is the generic class to store coded (compressed) code-block. It stores
* the compressed data as well as the necessary side-information.
*
* <P>This class is normally not used. Instead the EncRDCBlk, EncLyrdCBlk and
* the DecLyrdCBlk subclasses are used.
*
* @see CBlkRateDistStats
* @see DecLyrdCBlk
*/
public class CodedCBlk {
/**
* The horizontal index of the code-block, within the subband.
*/
public int n;
/**
* The vertical index of the code-block, within the subband.
*/
public int m;
/**
* The number of skipped most significant bit-planes.
*/
public int skipMSBP;
/**
* The compressed data
*/
public byte[] data;
/**
* Creates a new CodedCBlk object wit the default values and without
* allocating any space for its members.
*/
public CodedCBlk() {
}
/**
* Creates a new CodedCBlk object with the specified values.
*
* @param m The horizontal index of the code-block, within the subband.
* @param n The vertical index of the code-block, within the subband.
* @param skipMSBP The number of skipped most significant bit-planes for
* this code-block.
* @param data The compressed data. This array is referenced by this
* object so it should not be modified after.
*/
public CodedCBlk(int m, int n, int skipMSBP, byte[] data) {
this.m = m;
this.n = n;
this.skipMSBP = skipMSBP;
this.data = data;
}
/**
* Returns the contents of the object in a string. The string contains the
* following data: 'm', 'n', 'skipMSBP' and 'data.length. This is used for
* debugging.
*
* @return A string with the contents of the object
*/
public String toString() {
return "m= " + m + ", n= " + n + ", skipMSBP= " + skipMSBP +
", data.length= " + ((data != null) ? "" + data.length : "(null)");
}
}

View file

@ -0,0 +1,403 @@
/*
* $RCSfile: PrecinctSizeSpec.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:04 $
* $State: Exp $
*
* Class: PrecinctSizeSpec
*
* Description: Specification of the precinct sizes
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy;
import org.xbib.graphics.jpeg2000.J2KWriteParam;
import org.xbib.graphics.jpeg2000.j2k.IntegerSpec;
import org.xbib.graphics.jpeg2000.j2k.ModuleSpec;
import org.xbib.graphics.jpeg2000.j2k.codestream.Markers;
import org.xbib.graphics.jpeg2000.j2k.image.BlkImgDataSrc;
import org.xbib.graphics.jpeg2000.j2k.util.MathUtil;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* This class extends ModuleSpec class for precinct partition sizes holding
* purposes.
*
* <p>It stores the size a of precinct when precinct partition is used or not.
* If precinct partition is used, we can have several packets for a given
* resolution level whereas there is only one packet per resolution level if
* no precinct partition is used.
*/
public class PrecinctSizeSpec extends ModuleSpec {
/**
* Name of the option
*/
private static final String optName = "Cpp";
/**
* Reference to wavelet number of decomposition levels for each
* tile-component.
*/
private final IntegerSpec dls;
/**
* Creates a new PrecinctSizeSpec object for the specified number of tiles
* and components.
*
* @param nt The number of tiles
* @param nc The number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both.
* @param dls Reference to the number of decomposition levels
* specification
*/
public PrecinctSizeSpec(int nt, int nc, byte type, IntegerSpec dls) {
super(nt, nc, type);
this.dls = dls;
}
/**
* Creates a new PrecinctSizeSpec object for the specified number of tiles
* and components and the J2KWriteParam instance.
*
* @param nt The number of tiles
* @param nc The number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both.
* @param imgsrc The image source (used to get the image size)
* @param wp The J2KWriteParam instance
*/
public PrecinctSizeSpec(int nt, int nc, byte type, BlkImgDataSrc imgsrc,
IntegerSpec dls, J2KWriteParam wp, String values) {
super(nt, nc, type);
this.dls = dls;
// The precinct sizes are stored in a 2 elements vector array, the
// first element containing a vector for the precincts width for each
// resolution level and the second element containing a vector for the
// precincts height for each resolution level. The precincts sizes are
// specified from the highest resolution level to the lowest one
// (i.e. 0). If there are less elements than the number of
// decomposition levels, the last element is used for all remaining
// resolution levels (i.e. if the precincts sizes are specified only
// for resolutions levels 5, 4 and 3, then the precincts size for
// resolution levels 2, 1 and 0 will be the same as the size used for
// resolution level 3).
// Boolean used to know if we were previously reading a precinct's
// size or if we were reading something else.
boolean wasReadingPrecinctSize = false;
String param = values;
/*
if (values == null)
param = defaultValue; // the default is null
*/
// Set precinct sizes to default i.e. 2^15 =
// Markers.PRECINCT_PARTITION_DEF_SIZE
Vector[] tmpv = new Vector[2];
tmpv[0] = new Vector(); // ppx
tmpv[0].addElement(Integer.valueOf(Markers.PRECINCT_PARTITION_DEF_SIZE));
tmpv[1] = new Vector(); // ppy
tmpv[1].addElement(Integer.valueOf(Markers.PRECINCT_PARTITION_DEF_SIZE));
setDefault(tmpv);
if (param == null) {
// No precinct size specified in the command line so we do not try
// to parse it.
return;
}
// Precinct partition is used : parse arguments
StringTokenizer stk = new StringTokenizer(param);
byte curSpecType = SPEC_DEF; // Specification type of the
// current parameter
boolean[] tileSpec = null; // Tiles concerned by the specification
boolean[] compSpec = null; // Components concerned by the specification
int i, xIdx, ci, ti;
boolean endOfParamList = false;
String word = null; // current word
Integer w, h;
String errMsg = null;
while ((stk.hasMoreTokens() || wasReadingPrecinctSize) &&
!endOfParamList) {
Vector[] v = new Vector[2]; // v[0] : ppx, v[1] : ppy
// We do not read the next token if we were reading a precinct's
// size argument as we have already read the next token into word.
if (!wasReadingPrecinctSize) {
word = stk.nextToken();
}
wasReadingPrecinctSize = false;
switch (word.charAt(0)) {
case 't': // Tiles specification
tileSpec = parseIdx(word, nTiles);
if (curSpecType == SPEC_COMP_DEF) {
curSpecType = SPEC_TILE_COMP;
} else {
curSpecType = SPEC_TILE_DEF;
}
break;
case 'c': // Components specification
compSpec = parseIdx(word, nComp);
if (curSpecType == SPEC_TILE_DEF) {
curSpecType = SPEC_TILE_COMP;
} else {
curSpecType = SPEC_COMP_DEF;
}
break;
default:
if (!Character.isDigit(word.charAt(0))) {
errMsg = "Bad construction for parameter: " + word;
throw new IllegalArgumentException(errMsg);
}
// Initialises Vector objects
v[0] = new Vector(); // ppx
v[1] = new Vector(); // ppy
while (true) {
// Now get the precinct dimensions
try {
// Get precinct width
w = Integer.valueOf(word);
// Get next word in argument list
try {
word = stk.nextToken();
} catch (NoSuchElementException e) {
errMsg = "'" + optName + "' option : could not " +
"parse the precinct's width";
throw new IllegalArgumentException(errMsg);
}
// Get precinct height
h = Integer.valueOf(word);
if (w.intValue() != (1 << MathUtil.log2(w.intValue()))
|| h.intValue() !=
(1 << MathUtil.log2(h.intValue()))) {
errMsg = "Precinct dimensions must be powers of 2";
throw new IllegalArgumentException(errMsg);
}
} catch (NumberFormatException e) {
errMsg = "'" + optName + "' option : the argument '" + word +
"' could not be parsed.";
throw new IllegalArgumentException(errMsg);
}
// Store packet's dimensions in Vector arrays
v[0].addElement(w);
v[1].addElement(h);
// Try to get the next token
if (stk.hasMoreTokens()) {
word = stk.nextToken();
if (!Character.isDigit(word.charAt(0))) {
// The next token does not start with a digit so
// it is not a precinct's size argument. We set
// the wasReadingPrecinctSize booleen such that we
// know that we don't have to read another token
// and check for the end of the parameters list.
wasReadingPrecinctSize = true;
if (curSpecType == SPEC_DEF) {
setDefault(v);
} else if (curSpecType == SPEC_TILE_DEF) {
for (ti = tileSpec.length - 1; ti >= 0; ti--) {
if (tileSpec[ti]) {
setTileDef(ti, v);
}
}
} else if (curSpecType == SPEC_COMP_DEF) {
for (ci = compSpec.length - 1; ci >= 0; ci--) {
if (compSpec[ci]) {
setCompDef(ci, v);
}
}
} else {
for (ti = tileSpec.length - 1; ti >= 0; ti--) {
for (ci = compSpec.length - 1; ci >= 0; ci--) {
if (tileSpec[ti] && compSpec[ci]) {
setTileCompVal(ti, ci, v);
}
}
}
}
// Re-initialize
curSpecType = SPEC_DEF;
tileSpec = null;
compSpec = null;
// Go back to 'normal' parsing
break;
} else {
// Next token starts with a digit so read it
}
} else {
// We have reached the end of the parameters list so
// we store the last precinct's sizes and we stop
if (curSpecType == SPEC_DEF) {
setDefault(v);
} else if (curSpecType == SPEC_TILE_DEF) {
for (ti = tileSpec.length - 1; ti >= 0; ti--) {
if (tileSpec[ti]) {
setTileDef(ti, v);
}
}
} else if (curSpecType == SPEC_COMP_DEF) {
for (ci = compSpec.length - 1; ci >= 0; ci--) {
if (compSpec[ci]) {
setCompDef(ci, v);
}
}
} else {
for (ti = tileSpec.length - 1; ti >= 0; ti--) {
for (ci = compSpec.length - 1; ci >= 0; ci--) {
if (tileSpec[ti] && compSpec[ci]) {
setTileCompVal(ti, ci, v);
}
}
}
}
endOfParamList = true;
break;
}
} // while (true)
break;
} // switch
} // while
}
/**
* Returns the precinct partition width in component 'n' and tile 't' at
* resolution level 'rl'. If the tile index is equal to -1 or if the
* component index is equal to -1 it means that those should not be taken
* into account.
*
* @param t The tile index, in raster scan order. Specify -1 if it is not
* a specific tile.
* @param c The component index. Specify -1 if it is not a specific
* component.
* @param rl The resolution level
* @return The precinct partition width in component 'c' and tile 't' at
* resolution level 'rl'.
*/
public int getPPX(int t, int c, int rl) {
int mrl, idx;
Vector[] v = null;
boolean tileSpecified = (t != -1);
boolean compSpecified = (c != -1);
// Get the maximum number of decomposition levels and the object
// (Vector array) containing the precinct dimensions (width and
// height) for the specified (or not) tile/component
if (tileSpecified && compSpecified) {
mrl = ((Integer) dls.getTileCompVal(t, c)).intValue();
v = (Vector[]) getTileCompVal(t, c);
} else if (tileSpecified && !compSpecified) {
mrl = ((Integer) dls.getTileDef(t)).intValue();
v = (Vector[]) getTileDef(t);
} else if (!tileSpecified && compSpecified) {
mrl = ((Integer) dls.getCompDef(c)).intValue();
v = (Vector[]) getCompDef(c);
} else {
mrl = ((Integer) dls.getDefault()).intValue();
v = (Vector[]) getDefault();
}
idx = mrl - rl;
if (v[0].size() > idx) {
return ((Integer) v[0].elementAt(idx)).intValue();
} else {
return ((Integer) v[0].elementAt(v[0].size() - 1)).intValue();
}
}
/**
* Returns the precinct partition height in component 'n' and tile 't' at
* resolution level 'rl'. If the tile index is equal to -1 or if the
* component index is equal to -1 it means that those should not be taken
* into account.
*
* @param t The tile index, in raster scan order. Specify -1 if it is not
* a specific tile.
* @param c The component index. Specify -1 if it is not a specific
* component.
* @param rl The resolution level.
* @return The precinct partition width in component 'n' and tile 't' at
* resolution level 'rl'.
*/
public int getPPY(int t, int c, int rl) {
int mrl, idx;
Vector[] v = null;
boolean tileSpecified = (t != -1);
boolean compSpecified = (c != -1);
// Get the maximum number of decomposition levels and the object
// (Vector array) containing the precinct dimensions (width and
// height) for the specified (or not) tile/component
if (tileSpecified && compSpecified) {
mrl = ((Integer) dls.getTileCompVal(t, c)).intValue();
v = (Vector[]) getTileCompVal(t, c);
} else if (tileSpecified && !compSpecified) {
mrl = ((Integer) dls.getTileDef(t)).intValue();
v = (Vector[]) getTileDef(t);
} else if (!tileSpecified && compSpecified) {
mrl = ((Integer) dls.getCompDef(c)).intValue();
v = (Vector[]) getCompDef(c);
} else {
mrl = ((Integer) dls.getDefault()).intValue();
v = (Vector[]) getDefault();
}
idx = mrl - rl;
if (v[1].size() > idx) {
return ((Integer) v[1].elementAt(idx)).intValue();
} else {
return ((Integer) v[1].elementAt(v[1].size() - 1)).intValue();
}
}
}

View file

@ -0,0 +1,142 @@
/*
* $RCSfile: Progression.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:05 $
* $State: Exp $
*
* Class: Progression
*
* Description: Holds the type(s) of progression
*
*
* Modified by:
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy;
import org.xbib.graphics.jpeg2000.j2k.codestream.ProgressionType;
/**
* This class holds one of the different progression orders defined in
* the bit stream. The type(s) of progression order are defined in the
* ProgressionType interface. A Progression object is totally defined
* by its component start and end, resolution level start and end and
* layer start and end indexes. If no progression order change is
* defined, there is only Progression instance.
*
* @see ProgressionType
*/
public class Progression implements ProgressionType {
/**
* Progression type as defined in ProgressionType interface
*/
public int type;
/**
* Component index for the start of a progression
*/
public int cs;
/**
* Component index for the end of a progression.
*/
public int ce;
/**
* Resolution index for the start of a progression
*/
public int rs;
/**
* Resolution index for the end of a progression.
*/
public int re;
/**
* The index of the last layer.
*/
public int lye;
/**
* Constructor.
* <p>
* Builds a new Progression object with specified type and bounds
* of progression.
*
* @param type The progression type
* @param cs The component index start
* @param ce The component index end
* @param rs The resolution level index start
* @param re The resolution level index end
* @param lye The layer index end
*/
public Progression(int type, int cs, int ce, int rs, int re, int lye) {
this.type = type;
this.cs = cs;
this.ce = ce;
this.rs = rs;
this.re = re;
this.lye = lye;
}
public String toString() {
String str = "type= ";
switch (type) {
case LY_RES_COMP_POS_PROG:
str += "layer, ";
break;
case RES_LY_COMP_POS_PROG:
str += "res, ";
break;
case RES_POS_COMP_LY_PROG:
str += "res-pos, ";
break;
case POS_COMP_RES_LY_PROG:
str += "pos-comp, ";
break;
case COMP_POS_RES_LY_PROG:
str += "pos-comp, ";
break;
default:
str += "Unknown progression type=" + type;
}
str += "comp.: " + cs + "-" + ce + ", ";
str += "res.: " + rs + "-" + re + ", ";
str += "layer: up to " + lye;
return str;
}
}

View file

@ -0,0 +1,378 @@
/*
* $RCSfile: ProgressionSpec.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:05 $
* $State: Exp $
*
* Class: ProgressionSpec
*
* Description: Specification of the progression(s) type(s) and
* changes of progression.
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy;
import org.xbib.graphics.jpeg2000.J2KWriteParam;
import org.xbib.graphics.jpeg2000.j2k.IntegerSpec;
import org.xbib.graphics.jpeg2000.j2k.ModuleSpec;
import org.xbib.graphics.jpeg2000.j2k.codestream.ProgressionType;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* This class extends ModuleSpec class for progression type(s) and progression
* order changes holding purposes.
*
* <P>It stores the progression type(s) used in the codestream. There can be
* only one progression type or several ones if progression order changes are
* used (POC markers).
*/
public class ProgressionSpec extends ModuleSpec {
/**
* Creates a new ProgressionSpec object for the specified number of tiles
* and components.
*
* @param nt The number of tiles
* @param nc The number of components
* @param type the type of the specification module i.e. tile specific,
* component specific or both. The ProgressionSpec class should only be
* used only with the type ModuleSpec.SPEC_TYPE_TILE.
*/
public ProgressionSpec(int nt, int nc, byte type) {
super(nt, nc, type);
if (type != ModuleSpec.SPEC_TYPE_TILE) {
throw new IllegalArgumentException("Illegal use of class ProgressionSpec !");
}
}
/**
* Creates a new ProgressionSpec object for the specified number of
* tiles, components and the J2KWriteParam instance.
*
* @param nt The number of tiles
* @param nc The number of components
* @param nl The number of layer
* @param dls The number of decomposition levels specifications
* @param type the type of the specification module. The ProgressionSpec
* class should only be used only with the type ModuleSpec.SPEC_TYPE_TILE.
* @param wp The J2KWriteParam instance
*/
public ProgressionSpec(int nt, int nc, int nl, IntegerSpec dls, byte type,
J2KWriteParam wp, String values) {
super(nt, nc, type);
specified = values;
String param = values;
Progression[] prog;
int mode = -1;
if (values == null) { // No parameter specified
if (wp.getROIs() == null) {
mode = checkProgMode("res");
} else {
mode = checkProgMode("layer");
}
if (mode == -1) {
String errMsg = "Unknown progression type : '" + param + "'";
throw new IllegalArgumentException(errMsg);
}
prog = new Progression[1];
prog[0] = new Progression(mode, 0, nc, 0, dls.getMax() + 1, nl);
setDefault(prog);
return;
}
StringTokenizer stk = new StringTokenizer(param);
byte curSpecType = SPEC_DEF; // Specification type of the
// current parameter
boolean[] tileSpec = null; // Tiles concerned by the specification
String word = null; // current word
String errMsg = null; // Error message
boolean needInteger = false; // True if an integer value is expected
int intType = 0; // Type of read integer value (0=index of first
// component, 1= index of first resolution level, 2=index of last
// layer, 3= index of last component, 4= index of last resolution
// level)
Vector progression = new Vector();
int tmp = 0;
Progression curProg = null;
while (stk.hasMoreTokens()) {
word = stk.nextToken();
switch (word.charAt(0)) {
case 't':
// If progression were previously found, store them
if (progression.size() > 0) {
// Ensure that all information has been taken
curProg.ce = nc;
curProg.lye = nl;
curProg.re = dls.getMax() + 1;
prog = new Progression[progression.size()];
progression.copyInto(prog);
if (curSpecType == SPEC_DEF) {
setDefault(prog);
} else if (curSpecType == SPEC_TILE_DEF) {
for (int i = tileSpec.length - 1; i >= 0; i--)
if (tileSpec[i]) {
setTileDef(i, prog);
}
}
}
progression.removeAllElements();
intType = -1;
needInteger = false;
// Tiles specification
tileSpec = parseIdx(word, nTiles);
curSpecType = SPEC_TILE_DEF;
break;
default:
// Here, words is either a Integer (progression bound
// index) or a String (progression order type). This
// is determined by the value of needInteger.
if (needInteger) { // Progression bound info
try {
tmp = (Integer.valueOf(word)).intValue();
} catch (NumberFormatException e) {
// Progression has missing parameters
throw new IllegalArgumentException("Progression " +
"order" +
" specification " +
"has missing " +
"parameters: " +
param);
}
switch (intType) {
case 0: // cs
if (tmp < 0 || tmp > dls.getMax() + 1)
throw new
IllegalArgumentException("Invalid comp_start " +
"in '-Aptype' option");
curProg.cs = tmp;
break;
case 1: // rs
if (tmp < 0 || tmp > nc)
throw new
IllegalArgumentException("Invalid res_start " +
"in '-Aptype' option");
curProg.rs = tmp;
break;
case 2: // lye
if (tmp < 0)
throw new
IllegalArgumentException("Invalid layer_end " +
"in '-Aptype' option");
if (tmp > nl) {
tmp = nl;
}
curProg.lye = tmp;
break;
case 3: // ce
if (tmp < 0)
throw new
IllegalArgumentException("Invalid comp_end " +
"in '-Aptype' option");
if (tmp > (dls.getMax() + 1)) {
tmp = dls.getMax() + 1;
}
curProg.ce = tmp;
break;
case 4: // re
if (tmp < 0)
throw new
IllegalArgumentException("Invalid res_end " +
"in '-Aptype' option");
if (tmp > nc) {
tmp = nc;
}
curProg.re = tmp;
break;
}
if (intType < 4) {
intType++;
needInteger = true;
break;
} else if (intType == 4) {
intType = 0;
needInteger = false;
break;
} else {
throw new IllegalStateException("Error in usage of 'Aptype' " +
"option: " + param);
}
}
if (!needInteger) { // Progression type info
mode = checkProgMode(word);
if (mode == -1) {
errMsg = "Unknown progression type : '" + word + "'";
throw new IllegalArgumentException(errMsg);
}
needInteger = true;
intType = 0;
if (progression.size() == 0)
curProg = new Progression(mode, 0, nc, 0, dls.getMax() + 1,
nl);
else {
curProg = new Progression(mode, 0, nc, 0, dls.getMax() + 1,
nl);
}
progression.addElement(curProg);
}
} // switch
} // while
if (progression.size() == 0) { // No progression defined
// Set it arbitrarily to layer progressive
if (wp.getROIs() == null) {
mode = checkProgMode("res");
} else {
mode = checkProgMode("layer");
}
if (mode == -1) {
errMsg = "Unknown progression type : '" + param + "'";
throw new IllegalArgumentException(errMsg);
}
prog = new Progression[1];
prog[0] = new Progression(mode, 0, nc, 0, dls.getMax() + 1, nl);
setDefault(prog);
return;
}
// Ensure that all information has been taken
curProg.ce = nc;
curProg.lye = nl;
curProg.re = dls.getMax() + 1;
// Store found progression
prog = new Progression[progression.size()];
progression.copyInto(prog);
if (curSpecType == SPEC_DEF) {
setDefault(prog);
} else if (curSpecType == SPEC_TILE_DEF) {
for (int i = tileSpec.length - 1; i >= 0; i--)
if (tileSpec[i]) {
setTileDef(i, prog);
}
}
// Check that default value has been specified
if (getDefault() == null) {
int ndefspec = 0;
for (int t = nt - 1; t >= 0; t--) {
for (int c = nc - 1; c >= 0; c--) {
if (specValType[t][c] == SPEC_DEF) {
ndefspec++;
}
}
}
// If some tile-component have received no specification, they are
// arbitrarily set to 'layer' progressive.
if (ndefspec != 0) {
if (wp.getROIs() == null) {
mode = checkProgMode("res");
} else {
mode = checkProgMode("layer");
}
if (mode == -1) {
errMsg = "Unknown progression type : '" + param + "'";
throw new IllegalArgumentException(errMsg);
}
prog = new Progression[1];
prog[0] = new Progression(mode, 0, nc, 0, dls.getMax() + 1, nl);
setDefault(prog);
} else {
// All tile-component have been specified, takes the first
// tile-component value as default.
setDefault(getTileCompVal(0, 0));
switch (specValType[0][0]) {
case SPEC_TILE_DEF:
for (int c = nc - 1; c >= 0; c--) {
if (specValType[0][c] == SPEC_TILE_DEF)
specValType[0][c] = SPEC_DEF;
}
tileDef[0] = null;
break;
case SPEC_COMP_DEF:
for (int t = nt - 1; t >= 0; t--) {
if (specValType[t][0] == SPEC_COMP_DEF)
specValType[t][0] = SPEC_DEF;
}
compDef[0] = null;
break;
case SPEC_TILE_COMP:
specValType[0][0] = SPEC_DEF;
tileCompVal.put("t0c0", null);
break;
}
}
}
}
/**
* Check if the progression mode exists and if so, return its integer
* value. It returns -1 otherwise.
*
* @param mode The progression mode stored in a string
* @return The integer value of the progression mode or -1 if the
* progression mode does not exist.
* @see ProgressionType
*/
private int checkProgMode(String mode) {
if (mode.equals("res")) {
return ProgressionType.RES_LY_COMP_POS_PROG;
} else if (mode.equals("layer")) {
return ProgressionType.LY_RES_COMP_POS_PROG;
} else if (mode.equals("pos-comp")) {
return ProgressionType.POS_COMP_RES_LY_PROG;
} else if (mode.equals("comp-pos")) {
return ProgressionType.COMP_POS_RES_LY_PROG;
} else if (mode.equals("res-pos")) {
return ProgressionType.RES_POS_COMP_LY_PROG;
} else {
// No corresponding progression mode, we return -1.
return -1;
}
}
}

View file

@ -0,0 +1,148 @@
/*
* $RCSfile: StdEntropyCoderOptions.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:05 $
* $State: Exp $
*
* Class: StdEntropyCoderOptions
*
* Description: Entropy coding engine of stripes in
* code-blocks options
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy;
/**
* This interface define the constants that identify the possible options for
* the entropy coder, as well some fixed parameters of the JPEG 2000 entropy
* coder.
*/
public interface StdEntropyCoderOptions {
/**
* The flag bit to indicate that selective arithmetic coding bypass
* should be used. In this mode some of the coding passes bypass the
* arithmetic coder and raw bits are output. If this flag is turned on and
* the 'OPT_TERM_PASS' one is turned off then the any non-bypass coding
* pass before a bypass coding pass must use MQ termination.
*/
int OPT_BYPASS = 1;
/**
* The flag bit to indicate that the MQ states for all contexts should be
* reset at the end of each non-bypass coding pass.
*/
int OPT_RESET_MQ = 1 << 1;
/**
* The flag bit to indicate that regular termination should be used. When
* this is specified termination is performed after each coding
* pass. Termination is applied to both arithmetically coded and bypass
* (i.e. raw) passes .
*/
int OPT_TERM_PASS = 1 << 2;
/**
* The flag bit to indicate the vertically stripe-causal context
* formation should be used.
*/
int OPT_VERT_STR_CAUSAL = 1 << 3;
/**
* The flag bit to indicate that error resilience info is embedded on MQ
* termination. The predictable error-resilient MQ termination at the
* encoder is necessary in this case.
*/
int OPT_PRED_TERM = 1 << 4;
/**
* The flag bit to indicate that a segmentation marker is to be
* inserted at the end of each normalization coding pass. The segment
* marker is the four symbol sequence 1010 that are sent through the MQ
* coder using the UNIFORM context.
*/
int OPT_SEG_SYMBOLS = 1 << 5;
/**
* The minimum code-block dimension. The nominal width or height of a
* code-block must never be less than this. It is 4.
*/
int MIN_CB_DIM = 4;
/**
* The maximum code-block dimension. No code-block should be larger,
* either in width or height, than this value. It is 1024.
*/
int MAX_CB_DIM = 1024;
/**
* The maximum code-block area (width x height). The surface covered by
* a nominal size block should never be larger than this. It is 4096
*/
int MAX_CB_AREA = 4096;
/**
* The stripe height. This is the nominal value of the stripe height. It
* is 4.
*/
int STRIPE_HEIGHT = 4;
/**
* The number of coding passes per bit-plane. This is the number of
* passes per bit-plane. It is 3.
*/
int NUM_PASSES = 3;
/**
* The number of most significant bit-planes where bypass mode is not to
* be used, even if bypass mode is on: 4.
*/
int NUM_NON_BYPASS_MS_BP = 4;
/**
* The number of empty passes in the most significant bit-plane. It is
* 2.
*/
int NUM_EMPTY_PASSES_IN_MS_BP = 2;
/**
* The index of the first "raw" pass, if bypass mode is on.
*/
int FIRST_BYPASS_PASS_IDX =
NUM_PASSES * NUM_NON_BYPASS_MS_BP - NUM_EMPTY_PASSES_IN_MS_BP;
}

View file

@ -0,0 +1,233 @@
/*
* $RCSfile: ByteInputBuffer.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:05 $
* $State: Exp $
*
* Class: ByteInputBuffer
*
* Description: Provides buffering for byte based input, similar
* to the standard class ByteArrayInputStream
*
* the old jj2000.j2k.io.ByteArrayInput class by
* Diego SANTA CRUZ, Apr-26-1999
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy.decoder;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
/**
* This class provides a byte input facility from byte buffers. It is similar
* to the ByteArrayInputStream class, but adds the possibility to add data to
* the stream after the creation of the object.
*
* <P>Unlike the ByteArrayInputStream this class is not thread safe (i.e. no
* two threads can use the same object at the same time, but different objects
* may be used in different threads).
*
* <P>This class can modify the contents of the buffer given to the
* constructor, when the addByteArray() method is called.
*
* @see InputStream
*/
public class ByteInputBuffer {
/**
* The byte array containing the data
*/
private byte[] buf;
/**
* The index one greater than the last valid character in the input
* stream buffer
*/
private int count;
/**
* The index of the next character to read from the input stream buffer
*/
private int pos;
/**
* Creates a new byte array input stream that reads data from the
* specified byte array. The byte array is not copied.
*
* @param buf the input buffer.
*/
public ByteInputBuffer(byte[] buf) {
this.buf = buf;
count = buf.length;
}
/**
* Creates a new byte array input stream that reads data from the
* specified byte array. Up to length characters are to be read
* from the byte array, starting at the indicated offset.
*
* <P>The byte array is not copied.
*
* @param buf the input buffer.
* @param offset the offset in the buffer of the first byte to
* read.
* @param length the maximum number of bytes to read from the
* buffer.
*/
public ByteInputBuffer(byte[] buf, int offset, int length) {
this.buf = buf;
pos = offset;
count = offset + length;
}
/**
* Sets the underlying buffer byte array to the given one, with the given
* offset and length. If 'buf' is null then the current byte buffer is
* assumed. If 'offset' is negative, then it will be assumed to be
* 'off+len', where 'off' and 'len' are the offset and length of the
* current byte buffer.
*
* <P>The byte array is not copied.
*
* @param buf the input buffer. If null it is the current input buffer.
* @param offset the offset in the buffer of the first byte to read. If
* negative it is assumed to be the byte just after the end of the current
* input buffer, only permitted if 'buf' is null.
* @param length the maximum number of bytes to read frmo the buffer.
*/
public void setByteArray(byte[] buf, int offset, int length) {
// In same buffer?
if (buf == null) {
if (length < 0 || count + length > this.buf.length) {
throw new IllegalArgumentException();
}
if (offset < 0) {
pos = count;
count += length;
} else {
count = offset + length;
pos = offset;
}
} else { // New input buffer
if (offset < 0 || length < 0 || offset + length > buf.length) {
throw new IllegalArgumentException();
}
this.buf = buf;
count = offset + length;
pos = offset;
}
}
/**
* Adds the specified data to the end of the byte array stream. This
* method modifies the byte array buffer. It can also discard the already
* read input.
*
* @param data The data to add. The data is copied.
* @param off The index, in data, of the first element to add to
* the stream.
* @param len The number of elements to add to the array.
*/
public synchronized void addByteArray(byte[] data, int off, int len) {
// Check integrity
if (len < 0 || off < 0 || len + off > buf.length) {
throw new IllegalArgumentException();
}
// Copy new data
if (count + len <= buf.length) { // Enough place in 'buf'
System.arraycopy(data, off, buf, count, len);
count += len;
} else {
if (count - pos + len <= buf.length) {
// Enough place in 'buf' if we move input data
// Move buffer
System.arraycopy(buf, pos, buf, 0, count - pos);
} else { // Not enough place in 'buf', use new buffer
byte[] oldbuf = buf;
buf = new byte[count - pos + len];
// Copy buffer
System.arraycopy(oldbuf, count, buf, 0, count - pos);
}
count -= pos;
pos = 0;
// Copy new data
System.arraycopy(data, off, buf, count, len);
count += len;
}
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an int in the range 0 to 255. If no byte is
* available because the end of the stream has been reached, the
* EOFException exception is thrown.
*
* <P>This method is not synchronized, so it is not thread safe.
*
* @return The byte read in the range 0-255.
* @throws EOFException If the end of the stream is reached.
*/
public int readChecked() throws IOException {
if (pos < count) {
return (int) buf[pos++] & 0xFF;
} else {
throw new EOFException();
}
}
/**
* Reads the next byte of data from this input stream. The value byte is
* returned as an int in the range 0 to 255. If no byte is available
* because the end of the stream has been reached, -1 is returned.
*
* <P>This method is not synchronized, so it is not thread safe.
*
* @return The byte read in the range 0-255, or -1 if the end of stream
* has been reached.
*/
public int read() {
if (pos < count) {
return (int) buf[pos++] & 0xFF;
} else {
return -1;
}
}
}

View file

@ -0,0 +1,172 @@
/*
* $RCSfile: ByteToBitInput.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:06 $
* $State: Exp $
*
* Class: ByteToBitInput
*
* Description: Adapter to perform bit based input from a byte
* based one.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy.decoder;
/**
* This class provides an adapter to perform bit based input on byte based
* output obejcts that inherit from a 'ByteInputBuffer' class. This class also
* performs the bit unstuffing procedure specified for the 'selective
* arithmetic coding bypass' mode of the JPEG 2000 entropy coder.
*/
class ByteToBitInput {
/**
* The byte based input
*/
ByteInputBuffer in;
/**
* The bit buffer
*/
int bbuf;
/**
* The position of the next bit to get from the byte buffer. When it is
* -1 the bit buffer is empty.
*/
int bpos = -1;
/**
* Instantiates a new 'ByteToBitInput' object that uses 'in' as the
* underlying byte based input.
*
* @param in The underlying byte based input.
*/
ByteToBitInput(ByteInputBuffer in) {
this.in = in;
}
/**
* Reads from the bit stream one bit. If 'bpos' is -1 then a byte is read
* and loaded into the bit buffer, from where the bit is read. If
* necessary the bit unstuffing will be applied.
*
* @return The read bit (0 or 1).
*/
final int readBit() {
if (bpos < 0) {
if ((bbuf & 0xFF) != 0xFF) { // Normal byte to read
bbuf = in.read();
bpos = 7;
} else { // Previous byte is 0xFF => there was bit stuffing
bbuf = in.read();
bpos = 6;
}
}
return (bbuf >> bpos--) & 0x01;
}
/**
* Checks for past errors in the decoding process by verifying the byte
* padding with an alternating sequence of 0's and 1's. If an error is
* detected it means that the raw bit stream has been wrongly decoded or
* that the raw terminated segment length is too long. If no errors are
* detected it does not necessarily mean that the raw bit stream has been
* correctly decoded.
*
* @return True if errors are found, false otherwise.
*/
public boolean checkBytePadding() {
int seq; // Byte padding sequence in last byte
// If there are no spare bits and bbuf is 0xFF (not EOF), then there
// is a next byte with bit stuffing that we must load.
if (bpos < 0 && (bbuf & 0xFF) == 0xFF) {
bbuf = in.read();
bpos = 6;
}
// 1) Not yet read bits in the last byte must be an alternating
// sequence of 0s and 1s, starting with 0.
if (bpos >= 0) {
seq = bbuf & ((1 << (bpos + 1)) - 1);
if (seq != (0x55 >> (7 - bpos))) return true;
}
// 2) We must have already reached the last byte in the terminated
// segment, unless last bit read is LSB of FF in which case an encoder
// can output an extra byte which is smaller than 0x80.
if (bbuf != -1) {
if (bbuf == 0xFF && bpos == 0) {
return (in.read() & 0xFF) >= 0x80;
} else {
return in.read() != -1;
}
}
// Nothing detected
return false;
}
/**
* Flushes (i.e. empties) the bit buffer, without loading any new
* bytes. This realigns the input at the next byte boundary, if not
* already at one.
*/
final void flush() {
bbuf = 0; // reset any bit stuffing state
bpos = -1;
}
/**
* Resets the underlying byte input to start a new segment. The bit buffer
* is flushed.
*
* @param buf The byte array containing the byte data. If null the
* current byte array is assumed.
* @param off The index of the first element in 'buf' to be decoded. If
* negative the byte just after the previous segment is assumed, only
* valid if 'buf' is null.
* @param len The number of bytes in 'buf' to be decoded. Any subsequent
* bytes are taken to be 0xFF.
*/
final void setByteArray(byte[] buf, int off, int len) {
in.setByteArray(buf, off, len);
bbuf = 0; // reset any bit stuffing state
bpos = -1;
}
}

View file

@ -0,0 +1,115 @@
/*
* $RCSfile: CodedCBlkDataSrcDec.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:06 $
* $State: Exp $
*
* Class: CodedCBlkDataSrcDec
*
* Description: Interface that defines a source of entropy coded
* data that is transferred in a code-block by
* code-block basis (decoder side).
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy.decoder;
import org.xbib.graphics.jpeg2000.j2k.wavelet.synthesis.InvWTData;
import org.xbib.graphics.jpeg2000.j2k.wavelet.synthesis.SubbandSyn;
import org.xbib.graphics.jpeg2000.j2k.codestream.reader.BitstreamReaderAgent;
/**
* This interface defines a source of entropy coded data and methods to
* transfer it in a code-block by code-block basis. In each call to
* 'geCodeBlock()' a specified coded code-block is returned.
*
* <P>This interface is the source of data for the entropy decoder. See the
* 'EntropyDecoder' class.
*
* <P>For each coded-code-block the entropy-coded data is returned along with
* its truncation point information in a 'DecLyrdCBlk' object.
*
* @see EntropyDecoder
* @see DecLyrdCBlk
* @see BitstreamReaderAgent
*/
public interface CodedCBlkDataSrcDec extends InvWTData {
/**
* Returns the specified coded code-block, for the specified component, in
* the current tile. The first layer to return is indicated by 'fl'. The
* number of layers that is returned depends on 'nl' and the amount of
* data available.
*
* <P>The argument 'fl' is to be used by subsequent calls to this method
* for the same code-block. In this way supplamental data can be retrieved
* at a later time. The fact that data from more than one layer can be
* returned means that several packets from the same code-block, of the
* same component, and the same tile, have been concatenated.
*
* <P>The returned compressed code-block can have its progressive
* attribute set. If this attribute is set it means that more data can be
* obtained by subsequent calls to this method (subject to transmission
* delays, etc). If the progressive attribute is not set it means that the
* returned data is all the data that can be obtained for the specified
* subblock.
*
* <P>The compressed code-block is uniquely specified by the current tile,
* the component (identified by 'c'), the subband (indentified by 'sb')
* and the code-bock vertical and horizontal indexes 'm' and 'n'.
*
* <P>The 'ulx' and 'uly' members of the returned 'DecLyrdCBlk' object
* contain the coordinates of the top-left corner of the block, with
* respect to the tile, not the subband.
*
* @param c The index of the component, from 0 to N-1.
* @param m The vertical index of the code-block to return, in the
* specified subband.
* @param n The horizontal index of the code-block to return, in the
* specified subband.
* @param sb The subband in whic the requested code-block is.
* @param fl The first layer to return.
* @param nl The number of layers to return, if negative all available
* layers are returned, starting at 'fl'.
* @param ccb If not null this object is used to return the compressed
* code-block. If null a new object is created and returned. If the data
* array in ccb is not null then it can be reused to return the compressed
* data.
* @return The compressed code-block, with a certain number of layers
* determined by the available data and 'nl'.
*/
DecLyrdCBlk getCodeBlock(int c, int m, int n,
SubbandSyn sb, int fl, int nl,
DecLyrdCBlk ccb);
}

View file

@ -0,0 +1,149 @@
/*
* $RCSfile: DecLyrdCBlk.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:06 $
* $State: Exp $
*
* Class: DecLyrdCBlk
*
* Description: The coded (compressed) code-block
* with layered organization for the decoder.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy.decoder;
import org.xbib.graphics.jpeg2000.j2k.entropy.CodedCBlk;
/**
* This class stores coded (compressed) code-blocks that are organized
* in layers. This object can contain either all the data of the
* code-block (i.e. all layers), or a subset of all the layers that
* make up the whole compressed-code-block. It is applicable to the
* decoder engine only. Some data of the coded-block is stored
* in the super class, see CodedCBlk.
*
* <P>A code-block may have its progressive attribute set (i.e. the
* 'prog' flag is true). If a code-block is progressive then it means
* that more data for it may be obtained for an improved quality. If
* the progressive flag is false then no more data is available from
* the source for this code-block.
*
* @see CodedCBlk
*/
public class DecLyrdCBlk extends CodedCBlk {
/**
* The horizontal coordinate of the upper-left corner of the code-block
*/
public int ulx;
/**
* The vertical coordinate of the upper left corner of the code-block
*/
public int uly;
/**
* The width of the code-block
*/
public int w;
/**
* The height of the code-block
*/
public int h;
/**
* The coded (compressed) data length. The data is stored in the
* 'data' array (see super class).
*/
public int dl;
/**
* The progressive flag, false by default (see above).
*/
public boolean prog;
/**
* The number of layers in the coded data.
*/
public int nl;
/**
* The index of the first truncation point returned
*/
public int ftpIdx;
/**
* The total number of truncation points from layer 1 to the last one in
* this object. The number of truncation points in 'data' is
* 'nTrunc-ftpIdx'.
*/
public int nTrunc;
/**
* The length of each terminated segment. If null then there is only one
* terminated segment, and its length is 'dl'. The number of terminated
* segments is to be deduced from 'ftpIdx', 'nTrunc' and the coding
* options. This array contains all terminated segments from the 'ftpIdx'
* truncation point, upto, and including, the 'nTrunc-1' truncation
* point. Any data after 'nTrunc-1' is not included in any length.
*/
public int[] tsLengths;
/**
* Object information in a string
*
* @return Information in a string
*/
public String toString() {
String str =
"Coded code-block (" + m + "," + n + "): " + skipMSBP + " MSB skipped, " +
dl + " bytes, " + nTrunc + " truncation points, " + nl + " layers, " +
"progressive= " + prog + ", ulx= " + ulx + ", uly= " + uly +
", w= " + w + ", h= " + h + ", ftpIdx=" + ftpIdx;
if (tsLengths != null) {
str += " {";
for (int i = 0; i < tsLengths.length; i++)
str += " " + tsLengths[i];
str += " }";
}
return str;
}
}

View file

@ -0,0 +1,161 @@
/*
* $RCSfile: EntropyDecoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:06 $
* $State: Exp $
*
* Class: EntropyDecoder
*
* Description: The abstract class for all entropy decoders.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy.decoder;
import org.xbib.graphics.jpeg2000.j2k.quantization.dequantizer.CBlkQuantDataSrcDec;
import org.xbib.graphics.jpeg2000.j2k.wavelet.synthesis.MultiResImgData;
import org.xbib.graphics.jpeg2000.j2k.wavelet.synthesis.MultiResImgDataAdapter;
import org.xbib.graphics.jpeg2000.j2k.wavelet.synthesis.SubbandSyn;
/**
* This is the abstract class from which all entropy decoders must
* inherit. This class implements the 'MultiResImgData', therefore it has the
* concept of a current tile and all operations are performed on the current
* tile.
*
* <P>Default implementations of the methods in 'MultiResImgData' are provided
* through the 'MultiResImgDataAdapter' abstract class.
*
* <P>Sign magnitude representation is used (instead of two's complement) for
* the output data. The most significant bit is used for the sign (0 if
* positive, 1 if negative). Then the magnitude of the quantized coefficient
* is stored in the next most significat bits. The most significant magnitude
* bit corresponds to the most significant bit-plane and so on.
*
* @see MultiResImgData
* @see MultiResImgDataAdapter
*/
public abstract class EntropyDecoder extends MultiResImgDataAdapter
implements CBlkQuantDataSrcDec {
/**
* The prefix for entropy decoder optiojns: 'C'
*/
public final static char OPT_PREFIX = 'C';
/**
* The list of parameters that is accepted by the entropy
* decoders. They start with 'C'.
*/
private final static String[][] pinfo = {
{"Cverber", "[true|false]",
"Specifies if the entropy decoder should be verbose about detected " +
"errors. If 'true' a message is printed whenever an error is detected.",
"true"},
{"Cer", "[true|false]",
"Specifies if error detection should be performed by the entropy " +
"decoder engine. If errors are detected they will be concealed and " +
"the resulting distortion will be less important. Note that errors " +
"can only be detected if the encoder that generated the data " +
"included error resilience information.", "true"},
};
/**
* The bit stream transport from where to get the compressed data
* (the source)
*/
protected CodedCBlkDataSrcDec src;
/**
* Initializes the source of compressed data.
*
* @param src From where to obtain the compressed data.
*/
public EntropyDecoder(CodedCBlkDataSrcDec src) {
super(src);
this.src = src;
}
/**
* Returns the parameters that are used in this class and
* implementing classes. It returns a 2D String array. Each of the
* 1D arrays is for a different option, and they have 3
* elements. The first element is the option name, the second one
* is the synopsis and the third one is a long description of what
* the parameter is. The synopsis or description may be 'null', in
* which case it is assumed that there is no synopsis or
* description of the option, respectively. Null may be returned
* if no options are supported.
*
* @return the options name, their synopsis and their explanation,
* or null if no options are supported.
*/
public static String[][] getParameterInfo() {
return pinfo;
}
/**
* Returns the subband tree, for the specified tile-component. This method
* returns the root element of the subband tree structure, see Subband and
* SubbandSyn. The tree comprises all the available resolution levels.
*
* <P>The number of magnitude bits ('magBits' member variable) for
* each subband is not initialized.
*
* @param t The index of the tile, from 0 to T-1.
* @param c The index of the component, from 0 to C-1.
* @return The root of the tree structure.
*/
public SubbandSyn getSynSubbandTree(int t, int c) {
return src.getSynSubbandTree(t, c);
}
/**
* Returns the horizontal code-block partition origin. Allowable values
* are 0 and 1, nothing else.
*/
public int getCbULX() {
return src.getCbULX();
}
/**
* Returns the vertical code-block partition origin. Allowable values are
* 0 and 1, nothing else.
*/
public int getCbULY() {
return src.getCbULY();
}
}

View file

@ -0,0 +1,714 @@
/*
* $RCSfile: MQDecoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:06 $
* $State: Exp $
*
* Class: MQDecoder
*
* Description: Class that encodes a number of bits using the
* MQ arithmetic decoder
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy.decoder;
import org.xbib.graphics.jpeg2000.j2k.util.ArrayUtil;
/**
* This class implements the MQ arithmetic decoder. It is implemented using
* the software conventions decoder for better performance (i.e. execution
* time performance). The initial states for each context of the MQ-coder are
* specified in the constructor.
*/
// A trick to test for increased speed: merge the Qe and mPS into 1 thing by
// using the sign bit of Qe to signal mPS (positive-or-0 is 0, negative is 1),
// and doubling the Qe, nMPS and nLPS tables. This gets rid of the swicthLM
// table since it can be integrated as special cases in the doubled nMPS and
// nLPS tables. See the JPEG book, chapter 13. The decoded decision can be
// calculated as (q>>>31).
public class MQDecoder {
/**
* The data structures containing the probabilities for the LPS
*/
final static
int[] qe = {0x5601, 0x3401, 0x1801, 0x0ac1, 0x0521, 0x0221, 0x5601,
0x5401, 0x4801, 0x3801, 0x3001, 0x2401, 0x1c01, 0x1601,
0x5601, 0x5401, 0x5101, 0x4801, 0x3801, 0x3401, 0x3001,
0x2801, 0x2401, 0x2201, 0x1c01, 0x1801, 0x1601, 0x1401,
0x1201, 0x1101, 0x0ac1, 0x09c1, 0x08a1, 0x0521, 0x0441,
0x02a1, 0x0221, 0x0141, 0x0111, 0x0085, 0x0049, 0x0025,
0x0015, 0x0009, 0x0005, 0x0001, 0x5601};
/**
* The indexes of the next MPS
*/
final static
int[] nMPS = {1, 2, 3, 4, 5, 38, 7, 8, 9, 10, 11, 12, 13, 29, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46};
/**
* The indexes of the next LPS
*/
final static
int[] nLPS = {1, 6, 9, 12, 29, 33, 6, 14, 14, 14, 17, 18, 20, 21, 14, 14, 15,
16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46};
/**
* Whether LPS and MPS should be switched
*/
final static
int[] switchLM = {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
* The initial state of each context
*/
final int[] initStates;
/**
* The ByteInputBuffer used to read the compressed bit stream.
*/
ByteInputBuffer in;
/**
* The current most probable signal for each context
*/
int[] mPS;
/**
* The current index of each context
*/
int[] I;
/**
* The current bit code
*/
int c;
/**
* The bit code counter
*/
int cT;
/**
* The current interval
*/
int a;
/**
* The last byte read
*/
int b;
/**
* Flag indicating if a marker has been found
*/
boolean markerFound;
/**
* Instantiates a new MQ-decoder, with the specified number of contexts and
* initial states. The compressed bytestream is read from the 'iStream'
* object.
*
* @param iStream the stream that contains the coded bits
* @param nrOfContexts The number of contexts used
* @param initStates The initial state for each context. A reference is
* kept to this array to reinitialize the contexts whenever 'reset()' or
* 'resetCtxts()' is called.
*/
public MQDecoder(ByteInputBuffer iStream, int nrOfContexts,
int[] initStates) {
in = iStream;
// Default initialization of the statistics bins is MPS=0 and
// I=0
I = new int[nrOfContexts];
mPS = new int[nrOfContexts];
// Save the initial states
this.initStates = initStates;
// Initialize
init();
// Set the contexts
resetCtxts();
}
/**
* Decodes 'n' symbols from the bit stream using the same context
* 'ctxt'. If possible the MQ-coder speedup mode will be used to speed up
* decoding. The speedup mode is used if Q (the LPS probability for 'ctxt'
* is low enough) and the A and C registers permit decoding several MPS
* symbols without renormalization.
*
* <P>Speedup mode should be used when decoding long runs of MPS with high
* probability with the same context.
*
* <P>This methiod will return the decoded symbols differently if speedup
* mode was used or not. If true is returned, then speedup mode was used
* and the 'n' decoded symbols are all the same and it is returned ain
* bits[0] only. If false is returned then speedup mode was not used, the
* decoded symbols are probably not all the same and they are returned in
* bits[0], bits[1], ... bits[n-1].
*
* @param bits The array where to put the decoded symbols. Must be of
* length 'n' or more.
* @param ctxt The context to use in decoding the symbols.
* @param n The number of symbols to decode.
* @return True if speedup mode was used, false if not. If speedup mode
* was used then all the decoded symbols are the same and its value is
* returned in 'bits[0]' only (not in bits[1], bits[2], etc.).
*/
public final boolean fastDecodeSymbols(int[] bits, int ctxt, int n) {
int q; // LPS probability for context
int idx; // Index of current state
int la; // cache for A register
int i; // counter
idx = I[ctxt];
q = qe[idx];
// This is a first attempt to implement speedup mode, it is probably
// not the most efficient way of doing it.
if ((q < 0x4000) && (n <= (a - (c >>> 16) - 1) / q) &&
(n <= (a - 0x8000) / q + 1)) {
// Q is small enough. There will be no modification of C that
// affects decoding, and Q can be substracted from A several
// times. We will decode all MPS.
a -= n * q;
if (a >= 0x8000) { // No renormalization needed
bits[0] = mPS[ctxt];
return true; // Done, used speedup mode
} else { // renormalization needed
I[ctxt] = nMPS[idx];
// Renormalize (MPS: no need for while loop)
if (cT == 0)
byteIn();
a <<= 1;
c <<= 1;
cT--;
// End renormalization
bits[0] = mPS[ctxt];
return true; // Done, used speedup mode
}
} else { // Normal mode
la = a; // cache A register
for (i = 0; i < n; i++) {
la -= q;
if ((c >>> 16) < la) {
if (la >= 0x8000) {
bits[i] = mPS[ctxt];
} else {
// -- MPS Exchange
if (la >= q) {
bits[i] = mPS[ctxt];
idx = nMPS[idx];
q = qe[idx];
// I[ctxt] set at end of loop
// -- Renormalize (MPS: no need for while loop)
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
// -- End renormalization
} else {
bits[i] = 1 - mPS[ctxt];
if (switchLM[idx] == 1)
mPS[ctxt] = 1 - mPS[ctxt];
idx = nLPS[idx];
q = qe[idx];
// I[ctxt] set at end of loop
// -- Renormalize
do {
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
} while (la < 0x8000);
// -- End renormalization
}
// -- End MPS Exchange
}
} else {
c -= (la << 16);
// -- LPS Exchange
if (la < q) {
la = q;
bits[i] = mPS[ctxt];
idx = nMPS[idx];
q = qe[idx];
// I[ctxt] set at end of loop
// -- Renormalize (MPS: no need for while loop)
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
// -- End renormalization
} else {
la = q;
bits[i] = 1 - mPS[ctxt];
if (switchLM[idx] == 1)
mPS[ctxt] = 1 - mPS[ctxt];
idx = nLPS[idx];
q = qe[idx];
// I[ctxt] set at end of loop
// -- Renormalize
do {
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
} while (la < 0x8000);
// -- End renormalization
}
// -- End LPS Exchange
}
}
a = la; // save cached A register
I[ctxt] = idx; // save current index for context
return false; // done, did not use speedup mode
} // End normal mode
}
/**
* This function performs the arithmetic decoding. The function receives
* an array in which to put the decoded symbols and an array of contexts
* with which to decode them.
*
* <P>Each context has a current MPS and an index describing what the
* current probability is for the LPS. Each bit is decoded and if the
* probability of the LPS exceeds .5, the MPS and LPS are switched.
*
* @param bits The array where to place the decoded symbols. It should be
* long enough to contain 'n' elements.
* @param cX The context to use in decoding each symbol.
* @param n The number of symbols to decode
*/
public final void decodeSymbols(int[] bits, int[] cX, int n) {
int q;
int ctxt;
int la; // cache for A register value
int index;
int i;
// NOTE: (a < 0x8000) is equivalent to ((a & 0x8000)==0)
// since 'a' is always less than or equal to 0xFFFF
// NOTE: conditional exchange guarantees that A for MPS is
// always greater than 0x4000 (i.e. 0.375)
// => one renormalization shift is enough for MPS
// => no need to do a renormalization while loop for MPS
for (i = 0; i < n; i++) {
ctxt = cX[i];
index = I[ctxt];
q = qe[index];
a -= q;
if ((c >>> 16) < a) {
if (a >= 0x8000) {
bits[i] = mPS[ctxt];
} else {
la = a;
// -- MPS Exchange
if (la >= q) {
bits[i] = mPS[ctxt];
I[ctxt] = nMPS[index];
// -- Renormalize (MPS: no need for while loop)
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
// -- End renormalization
} else {
bits[i] = 1 - mPS[ctxt];
if (switchLM[index] == 1)
mPS[ctxt] = 1 - mPS[ctxt];
I[ctxt] = nLPS[index];
// -- Renormalize
do {
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
} while (la < 0x8000);
// -- End renormalization
}
// -- End MPS Exchange
a = la;
}
} else {
la = a;
c -= (la << 16);
// -- LPS Exchange
if (la < q) {
la = q;
bits[i] = mPS[ctxt];
I[ctxt] = nMPS[index];
// -- Renormalize (MPS: no need for while loop)
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
// -- End renormalization
} else {
la = q;
bits[i] = 1 - mPS[ctxt];
if (switchLM[index] == 1)
mPS[ctxt] = 1 - mPS[ctxt];
I[ctxt] = nLPS[index];
// -- Renormalize
do {
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
} while (la < 0x8000);
// -- End renormalization
}
// -- End LPS Exchange
a = la;
}
}
}
/**
* Arithmetically decodes one symbol from the bit stream with the given
* context and returns its decoded value.
*
* <P>Each context has a current MPS and an index describing what the
* current probability is for the LPS. Each bit is encoded and if the
* probability of the LPS exceeds .5, the MPS and LPS are switched.
*
* @param context The context to use in decoding the symbol
* @return The decoded symbol, 0 or 1.
*/
public final int decodeSymbol(int context) {
int q;
int la;
int index;
int decision;
index = I[context];
q = qe[index];
// NOTE: (a < 0x8000) is equivalent to ((a & 0x8000)==0)
// since 'a' is always less than or equal to 0xFFFF
// NOTE: conditional exchange guarantees that A for MPS is
// always greater than 0x4000 (i.e. 0.375)
// => one renormalization shift is enough for MPS
// => no need to do a renormalization while loop for MPS
a -= q;
if ((c >>> 16) < a) {
if (a >= 0x8000) {
decision = mPS[context];
} else {
la = a;
// -- MPS Exchange
if (la >= q) {
decision = mPS[context];
I[context] = nMPS[index];
// -- Renormalize (MPS: no need for while loop)
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
// -- End renormalization
} else {
decision = 1 - mPS[context];
if (switchLM[index] == 1)
mPS[context] = 1 - mPS[context];
I[context] = nLPS[index];
// -- Renormalize
do {
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
} while (la < 0x8000);
// -- End renormalization
}
// -- End MPS Exchange
a = la;
}
} else {
la = a;
c -= (la << 16);
// -- LPS Exchange
if (la < q) {
la = q;
decision = mPS[context];
I[context] = nMPS[index];
// -- Renormalize (MPS: no need for while loop)
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
// -- End renormalization
} else {
la = q;
decision = 1 - mPS[context];
if (switchLM[index] == 1)
mPS[context] = 1 - mPS[context];
I[context] = nLPS[index];
// -- Renormalize
do {
if (cT == 0)
byteIn();
la <<= 1;
c <<= 1;
cT--;
} while (la < 0x8000);
// -- End renormalization
}
// -- End LPS Exchange
a = la;
}
return decision;
}
/**
* Checks for past errors in the decoding process using the predictable
* error resilient termination. This works only if the encoder used the
* predictable error resilient MQ termination, otherwise it reports wrong
* results. If an error is detected it means that the MQ bit stream has
* been wrongly decoded or that the MQ terminated segment length is too
* long. If no errors are detected it does not necessarily mean that the
* MQ bit stream has been correctly decoded.
*
* @return True if errors are found, false otherwise.
*/
public boolean checkPredTerm() {
int k; // Number of bits that where added in the termination process
int q;
// 1) if everything has been OK, 'b' must be 0xFF if a terminating
// marker has not yet been found
if (b != 0xFF && !markerFound) return true;
// 2) if cT is not 0, we must have already reached the terminating
// marker
if (cT != 0 && !markerFound) return true;
// 3) If cT is 1 there where no spare bits at the encoder, this is all
// that we can check
if (cT == 1) return false;
// 4) if cT is 0, then next byte must be the second byte of a
// terminating marker (i.e. larger than 0x8F) if the terminating
// marker has not been reached yet
if (cT == 0) {
if (!markerFound) {
// Get next byte and check
b = in.read() & 0xFF;
if (b <= 0x8F) return true;
}
// Adjust cT for last byte
cT = 8;
}
// 5) Now we can calculate the number 'k' of bits having error
// resilience information, which is the number of bits left to
// normalization in the C register, minus 1.
k = cT - 1;
// 6) The predictable termination policy is as if an LPS interval was
// coded that caused a renormalization of 'k' bits, before the
// termination marker started
// We first check if an LPS is decoded, that causes a renormalization
// of 'k' bits. Worst case is smallest LPS probability 'q' that causes
// a renormalization of 'k' bits.
q = 0x8000 >> k;
// Check that we can decode an LPS interval of probability 'q'
a -= q;
if ((c >>> 16) < a) {
// Error: MPS interval decoded
return true;
}
// OK: LPS interval decoded
c -= (a << 16);
// -- LPS Exchange
// Here 'a' can not be smaller than 'q' because the minimum value
// for 'a' is 0x8000-0x4000=0x4000 and 'q' is set to a value equal
// to or smaller than that.
a = q;
// -- Renormalize
do {
if (cT == 0) byteIn();
a <<= 1;
c <<= 1;
cT--;
} while (a < 0x8000);
// -- End renormalization
// -- End LPS Exchange
// 7) Everything seems OK, we have checked the C register for the LPS
// symbols and ensured that it is followed by bits synthetized by the
// termination marker.
return false;
}
/**
* This function gets one byte of compressed bits from the in-stream.
* the byte is added to c. If the byte is 0xFF and the next byte is greater
* than 0x8F, the byte after 0xFF is a marker.
*/
private void byteIn() {
if (!markerFound) {
if (b == 0xFF) {
b = in.read() & 0xFF; // Convert EOFs (-1) to 0xFF
if (b > 0x8F) {
markerFound = true;
// software-convention decoder: c unchanged
cT = 8;
} else {
c += 0xFE00 - (b << 9);
cT = 7;
}
} else {
b = in.read() & 0xFF; // Convert EOFs (-1) to 0xFF
c += 0xFF00 - (b << 8);
cT = 8;
}
} else {
// software-convention decoder: c unchanged
cT = 8;
}
}
/**
* Returns the number of contexts in the arithmetic coder.
*
* @return The number of contexts
**/
public final int getNumCtxts() {
return I.length;
}
/**
* Resets a context to the original probability distribution.
*
* @param c The number of the context (it starts at 0).
*/
public final void resetCtxt(int c) {
I[c] = initStates[c];
mPS[c] = 0;
}
/**
* Resets a context to the original probability distribution. The
* original probability distribution depends on the actual
* implementation of the arithmetic coder or decoder.
*
*/
public final void resetCtxts() {
System.arraycopy(initStates, 0, I, 0, I.length);
ArrayUtil.intArraySet(mPS, 0);
}
/**
* Resets the MQ decoder to start a new segment. This is like recreating a
* new MQDecoder object with new input data.
*
* @param buf The byte array containing the MQ encoded data. If null the
* current byte array is assumed.
* @param off The index of the first element in 'buf' to be decoded. If
* negative the byte just after the previous segment is assumed, only
* valid if 'buf' is null.
* @param len The number of bytes in 'buf' to be decoded. Any subsequent
* bytes are taken to be 0xFF.
*/
public final void nextSegment(byte[] buf, int off, int len) {
// Set the new input
in.setByteArray(buf, off, len);
// Reinitialize MQ
init();
}
/**
* Returns the underlying 'ByteInputBuffer' from where the MQ
* coded input bytes are read.
*
* @return The underlying ByteInputBuffer.
*/
public ByteInputBuffer getByteInputBuffer() {
return in;
}
/**
* Initializes the state of the MQ coder, without modifying the current
* context states. It sets the registers (A,C,B) and the "marker found"
* state to the initial state, to start the decoding of a new segment.
*
* <P>To have a complete reset of the MQ (as if a new MQDecoder object was
* created) 'resetCtxts()' should be called after this method.
*/
private void init() {
// --- INITDEC
markerFound = false;
// Read first byte
b = in.read() & 0xFF;
// Software conventions decoder
c = (b ^ 0xFF) << 16;
byteIn();
c = c << 7;
cT = cT - 7;
a = 0x8000;
// End of INITDEC ---
}
}

View file

@ -0,0 +1,257 @@
/*
* $RCSfile: BitToByteOutput.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:07 $
* $State: Exp $
*
* Class: BitToByteOutput
*
* Description: Adapter to perform bit based output on a byte
* based one.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy.encoder;
/**
* This class provides an adapter to perform bit based output on byte based
* output objects that inherit from a 'ByteOutputBuffer' class. This class
* implements the bit stuffing policy needed for the 'selective arithmetic
* coding bypass' mode of the entropy coder. This class also delays the output
* of a trailing 0xFF, since they are synthetized be the decoder.
*/
class BitToByteOutput {
/**
* The alternating sequence of 0's and 1's used for byte padding
*/
static final int PAD_SEQ = 0x2A;
/**
* Flag that indicates if an FF has been delayed
*/
boolean delFF = false;
/**
* The byte based output
*/
ByteOutputBuffer out;
/**
* The bit buffer
*/
int bbuf;
/**
* The position of the next bit to put in the bit buffer. When it is 7
* the bit buffer 'bbuf' is empty. The value should always be between 7
* and 0 (i.e. if it gets to -1, the bit buffer should be immediately
* written to the byte output).
*/
int bpos = 7;
/**
* The number of written bytes (excluding the bit buffer)
*/
int nb = 0;
/**
* Whether or not predictable termination is requested. This value is
* important when the last byte before termination is an 0xFF
*/
private boolean isPredTerm = false;
/**
* Instantiates a new 'BitToByteOutput' object that uses 'out' as the
* underlying byte based output.
*
* @param out The underlying byte based output
*/
BitToByteOutput(ByteOutputBuffer out) {
this.out = out;
}
/**
* Writes to the bit stream the symbols contained in the 'symbuf'
* buffer. The least significant bit of each element in 'symbuf'is
* written.
*
* @param symbuf The symbols to write
* @param nsym The number of symbols in symbuf
*/
final void writeBits(int[] symbuf, int nsym) {
int i;
int bbuf, bpos;
bbuf = this.bbuf;
bpos = this.bpos;
// Write symbol by symbol to bit buffer
for (i = 0; i < nsym; i++) {
bbuf |= (symbuf[i] & 0x01) << (bpos--);
if (bpos < 0) { // Bit buffer is full, write it
if (bbuf != 0xFF) { // No bit-stuffing needed
if (delFF) { // Output delayed 0xFF if any
out.write(0xFF);
delFF = false;
nb++;
}
out.write(bbuf);
nb++;
bpos = 7;
} else { // We need to do bit stuffing on next byte
delFF = true;
bpos = 6; // One less bit in next byte
}
bbuf = 0;
}
}
this.bbuf = bbuf;
this.bpos = bpos;
}
/**
* Write a bit to the output. The least significant bit of 'bit' is
* written to the output.
*
* @param bit
*/
final void writeBit(int bit) {
bbuf |= (bit & 0x01) << (bpos--);
if (bpos < 0) {
if (bbuf != 0xFF) { // No bit-stuffing needed
if (delFF) { // Output delayed 0xFF if any
out.write(0xFF);
delFF = false;
nb++;
}
// Output the bit buffer
out.write(bbuf);
nb++;
bpos = 7;
} else { // We need to do bit stuffing on next byte
delFF = true;
bpos = 6; // One less bit in next byte
}
bbuf = 0;
}
}
/**
* Writes the contents of the bit buffer and byte aligns the output by
* filling bits with an alternating sequence of 0's and 1's.
*/
void flush() {
if (delFF) { // There was a bit stuffing
if (bpos != 6) { // Bit buffer is not empty
// Output delayed 0xFF
out.write(0xFF);
delFF = false;
nb++;
// Pad to byte boundary with an alternating sequence of 0's
// and 1's.
bbuf |= (PAD_SEQ >>> (6 - bpos));
// Output the bit buffer
out.write(bbuf);
nb++;
bpos = 7;
bbuf = 0;
} else if (isPredTerm) {
out.write(0xFF);
nb++;
out.write(0x2A);
nb++;
bpos = 7;
bbuf = 0;
delFF = false;
}
} else { // There was no bit stuffing
if (bpos != 7) { // Bit buffer is not empty
// Pad to byte boundary with an alternating sequence of 0's and
// 1's.
bbuf |= (PAD_SEQ >>> (6 - bpos));
// Output the bit buffer (bbuf can not be 0xFF)
out.write(bbuf);
nb++;
bpos = 7;
bbuf = 0;
}
}
}
/**
* Terminates the bit stream by calling 'flush()' and then 'reset()'.
*/
public int terminate() {
flush();
int savedNb = nb;
reset();
return savedNb;
}
/**
* Resets the bit buffer to empty, without writing anything to the
* underlying byte output, and resets the byte count. The underlying byte
* output is NOT reset.
*/
void reset() {
delFF = false;
bpos = 7;
bbuf = 0;
nb = 0;
}
/**
* Returns the length, in bytes, of the output bit stream as written by
* this object. If the output bit stream does not have an integer number
* of bytes in length then it is rounded to the next integer.
*
* @return The length, in bytes, of the output bit stream.
*/
int length() {
if (delFF) {
// If bit buffer is empty we just need 'nb' bytes. If not we need
// the delayed FF and the padded bit buffer.
return nb + 2;
} else {
// If the bit buffer is empty, we just need 'nb' bytes. If not, we
// add length of the padded bit buffer
return nb + ((bpos == 7) ? 0 : 1);
}
}
/**
* Set the flag according to whether or not the predictable termination is
* requested.
*
* @param isPredTerm Whether or not predictable termination is requested.
*/
void setPredTerm(boolean isPredTerm) {
this.isPredTerm = isPredTerm;
}
}

View file

@ -0,0 +1,169 @@
/*
* $RCSfile: ByteOutputBuffer.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:07 $
* $State: Exp $
*
* Class: ByteOutputBuffer
*
* Description: Provides buffering for byte based output, similar
* to the standard class ByteArrayOutputStream
*
* the old jj2000.j2k.io.ByteArrayOutput class by
* Diego SANTA CRUZ, Apr-26-1999
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy.encoder;
/**
* This class provides a buffering output stream similar to
* ByteArrayOutputStream, with some additional methods.
*
* <P>Once an array has been written to an output stream or to a byte
* array, the object can be reused as a new stream if the reset()
* method is called.
*
* <P>Unlike the ByteArrayOutputStream class, this class is not thread safe.
*
* @see #reset
*/
public class ByteOutputBuffer {
/**
* The buffer increase size
*/
public final static int BUF_INC = 512;
/**
* The default initial buffer size
*/
public final static int BUF_DEF_LEN = 256;
/**
* The buffer where the data is stored
*/
byte[] buf;
/**
* The number of valid bytes in the buffer
*/
int count;
/**
* Creates a new byte array output stream. The buffer capacity is
* initially BUF_DEF_LEN bytes, though its size increases if necessary.
*/
public ByteOutputBuffer() {
buf = new byte[BUF_DEF_LEN];
}
/**
* Creates a new byte array output stream, with a buffer capacity
* of the specified size, in bytes.
*
* @param size the initial size.
*/
public ByteOutputBuffer(int size) {
buf = new byte[size];
}
/**
* Writes the specified byte to this byte array output stream. The
* functionality provided by this implementation is the same as for the
* one in the superclass, however this method is not synchronized and
* therefore not safe thread, but faster.
*
* @param b The byte to write
*/
public final void write(int b) {
if (count == buf.length) { // Resize buffer
byte[] tmpbuf = buf;
buf = new byte[buf.length + BUF_INC];
System.arraycopy(tmpbuf, 0, buf, 0, count);
}
buf[count++] = (byte) b;
}
/**
* Copies the specified part of the stream to the 'outbuf' byte
* array.
*
* @param off The index of the first element in the stream to
* copy.
* @param len The number of elements of the array to copy
* @param outbuf The destination array
* @param outoff The index of the first element in 'outbuf' where
* to write the data.
*/
public void toByteArray(int off, int len, byte[] outbuf, int outoff) {
// Copy the data
System.arraycopy(buf, off, outbuf, outoff, len);
}
/**
* Returns the number of valid bytes in the output buffer (count class
* variable).
*
* @return The number of bytes written to the buffer
*/
public int size() {
return count;
}
/**
* Discards all the buffered data, by resetting the counter of written
* bytes to 0.
*/
public void reset() {
count = 0;
}
/**
* Returns the byte buffered at the given position in the buffer. The
* position in the buffer is the index of the 'write()' method call after
* the last call to 'reset()'.
*
* @param pos The position of the byte to return
* @return The value (betweeb 0-255) of the byte at position 'pos'.
*/
public int getByte(int pos) {
if (pos >= count) {
throw new IllegalArgumentException();
}
return buf[pos] & 0xFF;
}
}

View file

@ -0,0 +1,342 @@
/*
* $RCSfile: CBlkRateDistStats.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:07 $
* $State: Exp $
*
* Class: CBlkRateDistStats
*
* Description: The coded (compressed) code-block with
* rate-distortion statistics.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy.encoder;
import org.xbib.graphics.jpeg2000.j2k.entropy.CodedCBlk;
import org.xbib.graphics.jpeg2000.j2k.wavelet.analysis.SubbandAn;
/**
* This class stores coded (compressed) code-blocks with their associated
* rate-distortion statistics. This object should always contain all the
* compressed data of the code-block. It is applicable to the encoder engine
* only. Some data of the coded-block is stored in the super class, see
* CodedCBlk.
*
* <P>The rate-distortion statistics (i.e. R-D slope) is stored for valid
* points only. The set of valid points is determined by the entropy coder
* engine itself. Normally they are selected so as to lye in a convex hull,
* which can be achived by using the 'selectConvexHull' method of this class,
* but some other strategies might be employed.
*
* <P>The rate (in bytes) for each truncation point (valid or not) is stored
* in the 'truncRates' array. The rate of a truncation point is the total
* number of bytes in 'data' (see super class) that have to be decoded to
* reach the truncation point.
*
* <P>The slope (reduction of distortion divided by the increase in rate) at
* each of the valid truncation points is stored in 'truncSlopes'.
*
* <P>The index of each valid truncation point is stored in 'truncIdxs'. The
* index should be interpreted in the following way: a valid truncation point
* at position 'n' has the index 'truncIdxs[n]', the rate
* 'truncRates[truncIdxs[n]]' and the slope 'truncSlopes[n]'. The arrays
* 'truncIdxs' and 'truncRates' have at least 'nVldTrunc' elements. The
* 'truncRates' array has at least 'nTotTrunc' elements.
*
* <P>In addition the 'isTermPass' array contains a flag for each truncation
* point (valid and non-valid ones) that tells if the pass is terminated or
* not. If this variable is null then it means that no pass is terminated,
* except the last one which always is.
*
* <P>The compressed data is stored in the 'data' member variable of the super
* class.
*
* @see CodedCBlk
*/
public class CBlkRateDistStats extends CodedCBlk {
/**
* The subband to which the code-block belongs
*/
public SubbandAn sb;
/**
* The total number of truncation points
*/
public int nTotTrunc;
/**
* The number of valid truncation points
*/
public int nVldTrunc;
/**
* The rate (in bytes) for each truncation point (valid and non-valid
* ones)
*/
public int[] truncRates;
/**
* The distortion for each truncation point (valid and non-valid ones)
*/
public double[] truncDists;
/**
* The negative of the rate-distortion slope for each valid truncation
* point
*/
public float[] truncSlopes;
/**
* The indices of the valid truncation points, in increasing
* order.
*/
public int[] truncIdxs;
/**
* Array of flags indicating terminated passes (valid or non-valid
* truncation points).
*/
public boolean[] isTermPass;
/**
* The number of ROI coefficients in the code-block
*/
public int nROIcoeff = 0;
/**
* Number of ROI coding passes
*/
public int nROIcp = 0;
/**
* Creates a new CBlkRateDistStats object without allocating any space for
* 'truncRates', 'truncSlopes', 'truncDists' and 'truncIdxs' or 'data'.
*/
public CBlkRateDistStats() {
}
/**
* Creates a new CBlkRateDistStats object and initializes the valid
* truncation points, their rates and their slopes, from the 'rates' and
* 'dist' arrays. The 'rates', 'dist' and 'termp' arrays must contain the
* rate (in bytes), the reduction in distortion (from nothing coded) and
* the flag indicating if termination is used, respectively, for each
* truncation point.
*
* <P>The valid truncation points are selected by taking them as lying on
* a convex hull. This is done by calling the method selectConvexHull().
*
* <P> Note that the arrays 'rates' and 'termp' are copied, not
* referenced, so they can be modified after a call to this constructor.
*
* @param m The horizontal index of the code-block, within the subband.
* @param n The vertical index of the code-block, within the subband.
* @param skipMSBP The number of skipped most significant bit-planes for
* this code-block.
* @param data The compressed data. This array is referenced by this
* object so it should not be modified after.
* @param rates The rates (in bytes) for each truncation point in the
* compressed data. This array is modified by the method but no reference
* is kept to it.
* @param dists The reduction in distortion (with respect to no information
* coded) for each truncation point. This array is modified by the method
* but no reference is kept to it.
* @param termp An array of boolean flags indicating, for each pass, if a
* pass is terminated or not (true if terminated). If null then it is
* assumed that no pass is terminated except the last one which always is.
* @param np The number of truncation points contained in 'rates', 'dist'
* and 'termp'.
* @param inclast If false the convex hull is constructed as for lossy
* coding. If true it is constructed as for lossless coding, in which case
* it is ensured that all bit-planes are sent (i.e. the last truncation
* point is always included).
*/
public CBlkRateDistStats(int m, int n, int skipMSBP, byte[] data,
int[] rates, double[] dists, boolean[] termp,
int np, boolean inclast) {
super(m, n, skipMSBP, data);
selectConvexHull(rates, dists, termp, np, inclast);
}
/**
* Compute the rate-distorsion slopes and selects those that lie in a
* convex hull. It will compute the slopes, select the ones that form the
* convex hull and initialize the 'truncIdxs' and 'truncSlopes' arrays, as
* well as 'nVldTrunc', with the selected truncation points. It will also
* initialize 'truncRates' and 'isTermPass' arrays, as well as
* 'nTotTrunc', with all the truncation points (selected or not).
*
* <P> Note that the arrays 'rates' and 'termp' are copied, not
* referenced, so they can be modified after a call to this method.
*
* @param rates The rates (in bytes) for each truncation point in the
* compressed data. This array is modified by the method.
* @param dists The reduction in distortion (with respect to no
* information coded) for each truncation point. This array is modified by
* the method.
* @param termp An array of boolean flags indicating, for each pass, if a
* pass is terminated or not (true if terminated). If null then it is
* assumed that no pass is terminated except the last one which always is.
* @param n The number of truncation points contained in 'rates', 'dist'
* and 'termp'.
* @param inclast If false the convex hull is constructed as for lossy
* coding. If true it is constructed as for lossless coding, in which case
* it is ensured that all bit-planes are sent (i.e. the last truncation
* point is always included).
*/
public void selectConvexHull(int[] rates, double[] dists, boolean[] termp,
int n, boolean inclast) {
int first_pnt; // The first point containing some coded data
int p; // last selected point
int k; // current point
int i; // current valid point
int npnt; // number of selected (i.e. valid) points
int delta_rate; // Rate difference
double delta_dist; // Distortion difference
float k_slope; // R-D slope for the current point
float p_slope; // R-D slope for the last selected point
int ll_rate; // Rate for "lossless" coding (i.e. all coded info)
// Convention: when a negative value is stored in 'rates' it meas an
// invalid point. The absolute value is always the rate for that point.
// Look for first point with some coded info (rate not 0)
first_pnt = 0;
while (first_pnt < n && rates[first_pnt] <= 0) {
first_pnt++;
}
// Select the valid points
npnt = n - first_pnt;
p_slope = 0f; // To keep compiler happy
ploop:
do {
p = -1;
for (k = first_pnt; k < n; k++) {
if (rates[k] < 0) { // Already invalidated point
continue;
}
// Calculate decrease in distortion and rate
if (p >= 0) {
delta_rate = rates[k] - rates[p];
delta_dist = dists[k] - dists[p];
} else { // This is with respect to no info coded
delta_rate = rates[k];
delta_dist = dists[k];
}
// If exactly same distortion don't eliminate if the rates are
// equal, otherwise it can lead to infinite slope in lossless
// coding.
if (delta_dist < 0f || (delta_dist == 0f && delta_rate > 0)) {
// This point increases distortion => invalidate
rates[k] = -rates[k];
npnt--;
continue; // Goto next point
}
k_slope = (float) (delta_dist / delta_rate);
// Check that there is a decrease in distortion, slope is not
// infinite (i.e. delta_dist is not 0) and slope is
// decreasing.
if (p >= 0 &&
(delta_rate <= 0 || k_slope >= p_slope)) {
// Last point was not good
rates[p] = -rates[p]; // Remove p from valid points
npnt--;
continue ploop; // Restart from the first one
} else {
p_slope = k_slope;
p = k;
}
}
// If we get to last point we are done
break;
} while (true); // We end the loop with the break statement
// If in lossless mode make sure we don't eliminate any last bit-planes
// from being sent.
if (inclast && n > 0 && rates[n - 1] < 0) {
rates[n - 1] = -rates[n - 1];
// This rate can never be equal to any previous selected rate,
// given the selection algorithm above, so no problem arises of
// infinite slopes.
npnt++;
}
// Initialize the arrays of this object
nTotTrunc = n;
nVldTrunc = npnt;
truncRates = new int[n];
truncDists = new double[n];
truncSlopes = new float[npnt];
truncIdxs = new int[npnt];
if (termp != null) {
isTermPass = new boolean[n];
System.arraycopy(termp, 0, isTermPass, 0, n);
} else {
isTermPass = null;
}
System.arraycopy(rates, 0, truncRates, 0, n);
for (k = first_pnt, p = -1, i = 0; k < n; k++) {
if (rates[k] > 0) { // A valid point
truncDists[k] = dists[k];
if (p < 0) { // Only arrives at first valid point
truncSlopes[i] = (float) (dists[k] / rates[k]);
} else {
truncSlopes[i] = (float) ((dists[k] - dists[p]) /
(rates[k] - rates[p]));
}
truncIdxs[i] = k;
i++;
p = k;
} else {
truncDists[k] = -1;
truncRates[k] = -truncRates[k];
}
}
}
/**
* Returns the contents of the object in a string. This is used for
* debugging.
*
* @return A string with the contents of the object
*/
public String toString() {
return super.toString() +
"\n nVldTrunc = " + nVldTrunc + ", nTotTrunc=" + nTotTrunc + ", num. ROI" +
" coeff=" + nROIcoeff + ", num. ROI coding passes=" + nROIcp;
}
}

View file

@ -0,0 +1,134 @@
/*
* $RCSfile: CodedCBlkDataSrcEnc.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:08 $
* $State: Exp $
*
* Class: CodedCBlkDataSrcEnc
*
* Description: Interface that defines a source of entropy coded
* data that is transferred in a code-block by
* code-block basis.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy.encoder;
import org.xbib.graphics.jpeg2000.j2k.wavelet.analysis.ForwWTDataProps;
/**
* This interface defines a source of entropy coded data and methods to
* transfer it in a code-block by code-block basis. In each call to
* 'getNextCodeBlock()' a new coded code-block is returned. The code-block are
* retruned in no specific-order.
*
* <P>This interface is the source of data for the rate allocator. See the
* 'PostCompRateAllocator' class.
*
* <P>For each coded-code-block the entropy-coded data is returned along with
* the rate-distortion statistics in a 'CBlkRateDistStats' object.
*
* @see PostCompRateAllocator
* @see CBlkRateDistStats
* @see EntropyCoder
*/
public interface CodedCBlkDataSrcEnc extends ForwWTDataProps {
/**
* Returns the next coded code-block in the current tile for the specified
* component, as a copy (see below). The order in which code-blocks are
* returned is not specified. However each code-block is returned only
* once and all code-blocks will be returned if the method is called 'N'
* times, where 'N' is the number of code-blocks in the tile. After all
* the code-blocks have been returned for the current tile calls to this
* method will return 'null'.
*
* <P>When changing the current tile (through 'setTile()' or 'nextTile()')
* this method will always return the first code-block, as if this method
* was never called before for the new current tile.
*
* <P>The data returned by this method is always a copy of the internal
* data of this object, if any, and it can be modified "in place" without
* any problems after being returned.
*
* @param c The component for which to return the next code-block.
* @param ccb If non-null this object might be used in returning the coded
* code-block in this or any subsequent call to this method. If null a new
* one is created and returned. If the 'data' array of 'cbb' is not null
* it may be reused to return the compressed data.
* @return The next coded code-block in the current tile for component
* 'n', or null if all code-blocks for the current tile have been
* returned.
* @see CBlkRateDistStats
*/
CBlkRateDistStats getNextCodeBlock(int c, CBlkRateDistStats ccb);
/**
* Returns the width of a packet for the specified tile-
* component and resolution level.
*
* @param t The tile
* @param c The component
* @param r The resolution level
* @return The width of a packet for the specified tile-
* component and resolution level.
*/
int getPPX(int t, int c, int r);
/**
* Returns the height of a packet for the specified tile-
* component and resolution level.
*
* @param t The tile
* @param c The component
* @param r The resolution level
* @return The height of a packet for the specified tile-
* component and resolution level.
*/
int getPPY(int t, int c, int r);
/**
* Returns true if the precinct partition is used for the
* specified component and tile, returns false otherwise
*
* @param c The component
* @param t The tile
*/
boolean precinctPartitionUsed(int c, int t);
}

View file

@ -0,0 +1,90 @@
/*
* $RCSfile: EBCOTLayer.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:08 $
* $State: Exp $
*
* Class: EBCOTLayer
*
* Description: Storage for layer information,
* used by EBCOTRateAllocator
*
* class that was in EBCOTRateAllocator.
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy.encoder;
/**
* This class holds information about each layer that is to be, or has already
* been, allocated . It is used in the rate-allocation process to keep the
* necessary layer information. It is used by EBCOTRateAllocator.
*
* @see EBCOTRateAllocator
**/
class EBCOTLayer {
/**
* This is the maximum number of bytes that should be allocated for
* this and previous layers. This is actually the target length for
* the layer.
**/
int maxBytes;
/**
* The actual number of bytes which are consumed by the the current and
* any previous layers. This is the result from a simulation when the
* threshold for the layer has been set.
**/
int actualBytes;
/**
* If true the `maxBytes' value is the hard maximum and the threshold is
* determined iteratively. If false the `maxBytes' value is a target
* bitrate and the threshold is estimated from summary information
* accumulated during block coding.
**/
boolean optimize;
/**
* The rate-distortion threshold associated with the bit-stream
* layer. When set the layer includes data up to the truncation points
* that have a slope no smaller than 'rdThreshold'.
**/
float rdThreshold;
}

View file

@ -0,0 +1,307 @@
/*
* $RCSfile: EntropyCoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:08 $
* $State: Exp $
*
* Class: EntropyCoder
*
* Description: The abstract class for entropy encoders
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package org.xbib.graphics.jpeg2000.j2k.entropy.encoder;
import org.xbib.graphics.jpeg2000.J2KWriteParam;
import org.xbib.graphics.jpeg2000.j2k.StringSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.CBlkSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.PrecinctSizeSpec;
import org.xbib.graphics.jpeg2000.j2k.entropy.StdEntropyCoderOptions;
import org.xbib.graphics.jpeg2000.j2k.image.ImgDataAdapter;
import org.xbib.graphics.jpeg2000.j2k.quantization.quantizer.CBlkQuantDataSrcEnc;
import org.xbib.graphics.jpeg2000.j2k.quantization.quantizer.Quantizer;
import org.xbib.graphics.jpeg2000.j2k.wavelet.Subband;
import org.xbib.graphics.jpeg2000.j2k.wavelet.analysis.SubbandAn;
import org.xbib.graphics.jpeg2000.j2k.roi.encoder.ROIScaler;
/**
* This abstract class provides the general interface for block-based entropy
* encoders. The input to the entropy coder is the quantized wavelet
* coefficients, or codewords, represented in sign magnitude. The output is a
* compressed code-block with rate-distortion information.
*
* <P>The source of data for objects of this class are 'CBlkQuantDataSrcEnc'
* objects.
*
* <P>For more details on the sign magnitude representation used see the
* Quantizer class.
*
* <P>This class provides default implemenations for most of the methods
* (wherever it makes sense), under the assumption that the image and
* component dimensions, and the tiles, are not modifed by the entropy
* coder. If that is not the case for a particular implementation then the
* methods should be overriden.
*
* @see Quantizer
* @see CBlkQuantDataSrcEnc
*/
public abstract class EntropyCoder extends ImgDataAdapter
implements CodedCBlkDataSrcEnc, StdEntropyCoderOptions {
/**
* The prefix for entropy coder options: 'C'
*/
public final static char OPT_PREFIX = 'C';
/**
* The list of parameters that is accepted for entropy coding. Options
* for entropy coding start with 'C'.
*/
private final static String[][] pinfo = {
{"Cblksiz", "[<tile-component idx>] <width> <height> " +
"[[<tile-component idx>] <width> <height>]",
"Specifies the maximum code-block size to use for tile-component. " +
"The maximum width and height is 1024, however the surface area " +
"(i.e. width x height) must not exceed 4096. The minimum width and " +
"height is 4.", "64 64"},
{"Cbypass", "[<tile-component idx>] true|false" +
"[ [<tile-component idx>] true|false ...]",
"Uses the lazy coding mode with the entropy coder. This will bypass " +
"the MQ coder for some of the coding passes, where the distribution " +
"is often close to uniform. Since the MQ codeword will be " +
"terminated " +
"at least once per lazy pass, it is important to use an efficient " +
"termination algorithm, see the 'Cterm' option." +
"'true' enables, 'false' disables it.", "false"},
{"CresetMQ", "[<tile-component idx>] true|false" +
"[ [<tile-component idx>] true|false ...]",
"If this is enabled the probability estimates of the MQ coder are " +
"reset after each arithmetically coded (i.e. non-lazy) coding pass. " +
"'true' enables, 'false' disables it.", "false"},
{"Creg_term", "[<tile-component idx>] true|false" +
"[ [<tile-component idx>] true|false ...]",
"If this is enabled the codeword (raw or MQ) is terminated on a " +
"byte boundary after each coding pass. In this case it is important " +
"to use an efficient termination algorithm, see the 'Cterm' option. " +
"'true' enables, 'false' disables it.", "false"},
{"Ccausal", "[<tile-component idx>] true|false" +
"[ [<tile-component idx>] true|false ...]",
"Uses vertically stripe causal context formation. If this is " +
"enabled " +
"the context formation process in one stripe is independant of the " +
"next stripe (i.e. the one below it). 'true' " +
"enables, 'false' disables it.", "false"},
{"Cseg_symbol", "[<tile-component idx>] true|false" +
"[ [<tile-component idx>] true|false ...]",
"Inserts an error resilience segmentation symbol in the MQ " +
"codeword at the end of " +
"each bit-plane (cleanup pass). Decoders can use this " +
"information to detect and " +
"conceal errors.'true' enables, 'false' disables " +
"it.", "false"},
{"Cterm", "[<tile-component idx>] near_opt|easy|predict|full" +
"[ [<tile-component idx>] near_opt|easy|predict|full ...]",
"Specifies the algorithm used to terminate the MQ codeword. " +
"The most efficient one is 'near_opt', which delivers a codeword " +
"which in almost all cases is the shortest possible. The 'easy' is " +
"a simpler algorithm that delivers a codeword length that is close " +
"to the previous one (in average 1 bit longer). The 'predict' is" +
" almost " +
"the same as the 'easy' but it leaves error resilient information " +
"on " +
"the spare least significant bits (in average 3.5 bits), which can " +
"be used by a decoder to detect errors. The 'full' algorithm " +
"performs a full flush of the MQ coder and is highly inefficient.\n" +
"It is important to use a good termination policy since the MQ " +
"codeword can be terminated quite often, specially if the 'Cbypass'" +
" or " +
"'Creg_term' options are enabled (in the normal case it would be " +
"terminated once per code-block, while if 'Creg_term' is specified " +
"it will be done almost 3 times per bit-plane in each code-block).",
"near_opt"},
{"Clen_calc", "[<tile-component idx>] near_opt|lazy_good|lazy" +
"[ [<tile-component idx>] ...]",
"Specifies the algorithm to use in calculating the necessary MQ " +
"length for each decoding pass. The best one is 'near_opt', which " +
"performs a rather sophisticated calculation and provides the best " +
"results. The 'lazy_good' and 'lazy' are very simple algorithms " +
"that " +
"provide rather conservative results, 'lazy_good' one being " +
"slightly " +
"better. Do not change this option unless you want to experiment " +
"the effect of different length calculation algorithms.", "near_opt"},
{"Cpp", "[<tile-component idx>] <dim> <dim> [<dim> <dim>] " +
"[ [<tile-component idx>] ...]",
"Specifies precinct partition dimensions for tile-component. The " +
"first " +
"two values apply to the highest resolution and the following ones " +
"(if " +
"any) apply to the remaining resolutions in decreasing order. If " +
"less " +
"values than the number of decomposition levels are specified, " +
"then the " +
"last two values are used for the remaining resolutions.", null},
};
/**
* The source of quantized wavelet coefficients
*/
protected CBlkQuantDataSrcEnc src;
/**
* Initializes the source of quantized wavelet coefficients.
*
* @param src The source of quantized wavelet coefficients.
*/
public EntropyCoder(CBlkQuantDataSrcEnc src) {
super(src);
this.src = src;
}
/**
* Returns the parameters that are used in this class and
* implementing classes. It returns a 2D String array. Each of the
* 1D arrays is for a different option, and they have 3
* elements. The first element is the option name, the second one
* is the synopsis, the third one is a long description of what
* the parameter is and the fourth is its default value. The
* synopsis or description may be 'null', in which case it is
* assumed that there is no synopsis or description of the option,
* respectively. Null may be returned if no options are supported.
*
* @return the options name, their synopsis and their explanation,
* or null if no options are supported.
*/
public static String[][] getParameterInfo() {
return pinfo;
}
/**
* Creates a EntropyCoder object for the appropriate entropy coding
* parameters in the parameter list 'pl', and having 'src' as the source
* of quantized data.
*
* @param src The source of data to be entropy coded
* @param wp The parameter list (or options).
* @param cblks Code-block size specifications
* @param pss Precinct partition specifications
* @param bms By-pass mode specifications
* @param mqrs MQ-reset specifications
* @param rts Regular termination specifications
* @param css Causal stripes specifications
* @param sss Error resolution segment symbol use specifications
* @param lcs Length computation specifications
* @param tts Termination type specifications
* @throws IllegalArgumentException If an error occurs while parsing
* the options in 'pl'
*/
public static EntropyCoder createInstance(CBlkQuantDataSrcEnc src,
J2KWriteParam wp,
CBlkSizeSpec cblks,
PrecinctSizeSpec pss,
StringSpec bms, StringSpec mqrs,
StringSpec rts, StringSpec css,
StringSpec sss, StringSpec lcs,
StringSpec tts) {
// Check parameters
//pl.checkList(OPT_PREFIX,pl.toNameArray(pinfo));
return new StdEntropyCoder(src, cblks, pss, bms, mqrs, rts, css, sss, lcs, tts);
}
/**
* Returns the code-block width for the specified tile and component.
*
* @param t The tile index
* @param c the component index
* @return The code-block width for the specified tile and component
*/
public abstract int getCBlkWidth(int t, int c);
/**
* Returns the code-block height for the specified tile and component.
*
* @param t The tile index
* @param c the component index
* @return The code-block height for the specified tile and component
*/
public abstract int getCBlkHeight(int t, int c);
/**
* Returns the reversibility of the tile-component data that is provided
* by the object. Data is reversible when it is suitable for lossless and
* lossy-to-lossless compression.
*
* <P>Since entropy coders themselves are always reversible, it returns
* the reversibility of the data that comes from the 'CBlkQuantDataSrcEnc'
* source object (i.e. ROIScaler).
*
* @param t Tile index
* @param c Component index
* @return true is the data is reversible, false if not.
* @see ROIScaler
*/
public boolean isReversible(int t, int c) {
return src.isReversible(t, c);
}
/**
* Returns a reference to the root of subband tree structure representing
* the subband decomposition for the specified tile-component.
*
* @param t The index of the tile.
* @param c The index of the component.
* @return The root of the subband tree structure, see Subband.
* @see SubbandAn
* @see Subband
*/
public SubbandAn getAnSubbandTree(int t, int c) {
return src.getAnSubbandTree(t, c);
}
/**
* Returns the horizontal offset of the code-block partition. Allowable
* values are 0 and 1, nothing else.
*/
public int getCbULX() {
return src.getCbULX();
}
/**
* Returns the vertical offset of the code-block partition. Allowable
* values are 0 and 1, nothing else.
*/
public int getCbULY() {
return src.getCbULY();
}
}

View file

@ -0,0 +1,234 @@
/*
* $RCSfile: LayersInfo.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:08 $
* $State: Exp $
*
* Class: LayersInfo
*
* Description: Specification of a layer
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package org.xbib.graphics.jpeg2000.j2k.entropy.encoder;
/**
* This class stores the specification of a layer distribution in the
* bit stream. The specification is made of optimization points and a number of
* extra layers to add between the optimization points. Each optimization
* point creates a layer which is optimized by the rate allocator to the
* specified target bitrate. The extra layers are added by the rate allocator
* between the optimized layers, with the difference that they are not
* optimized (i.e. they have no precise target bitrate).
*
* <P>The overall target bitrate for the bit stream is always added as the last
* optimization point without any extra layers after it. If there are some
* optimization points whose target bitrate is larger than the overall target
* bitrate, the overall target bitrate will still appear as the last
* optimization point, even though it does not follow the increasing target
* bitrate order of the other optimization points. The rate allocator is
* responsible for eliminating layers that have target bitrates larger than
* the overall target bitrate.
*
* <P>Optimization points can be added with the addOptPoint() method. It takes
* the target bitrate for the optimized layer and the number of extra layers
* to add after it.
*
* <P>Information about the total number of layers, total number of
* optimization points, target bitrates, etc. can be obtained with the other
* methods.
*/
public class LayersInfo {
/**
* The initial size for the arrays: 10
*/
private final static int SZ_INIT = 10;
/**
* The size increment for the arrays
*/
private final static int SZ_INCR = 5;
/**
* The total number of layers
*/
// Starts at 1: overall target bitrate is always an extra optimized layer
int totlyrs = 1;
/**
* The overall target bitrate, for the whole bit stream
*/
float totbrate;
/**
* The number of optimized layers, or optimization points, without
* counting the extra one coming from the overall target bitrate
*/
int nopt;
/**
* The target bitrate to which specified layers should be optimized.
*/
float[] optbrate = new float[SZ_INIT];
/**
* The number of extra layers to be added after an optimized layer. After
* the layer that is optimized to optbrate[i], extralyrs[i] extra layers
* should be added. These layers are allocated between the bitrate
* optbrate[i] and the next optimized bitrate optbrate[i+1] or, if it does
* not exist, the overall target bitrate.
*/
int[] extralyrs = new int[SZ_INIT];
/**
* Creates a new LayersInfo object. The overall target bitrate 'brate' is
* always an extra optimization point, with no extra layers are after
* it. Note that any optimization points that are added with addOptPoint()
* are always added before the overall target bitrate.
*
* @param brate The overall target bitrate for the bit stream
*/
public LayersInfo(float brate) {
if (brate <= 0) {
throw new IllegalArgumentException("Overall target bitrate must " +
"be a positive number");
}
totbrate = brate;
}
/**
* Returns the overall target bitrate for the entire bit stream.
*
* @return The overall target bitrate
*/
public float getTotBitrate() {
return totbrate;
}
/**
* Returns the total number of layers, according to the layer
* specification of this object and the overall target bitrate.
*
* @return The total number of layers, according to the layer spec.
*/
public int getTotNumLayers() {
return totlyrs;
}
/**
* Returns the number of layers to optimize, or optimization points, as
* specified by this object.
*
* @return The number of optimization points
*/
public int getNOptPoints() {
// overall target bitrate is counted as extra
return nopt + 1;
}
/**
* Returns the target bitrate of the optmimization point 'n'.
*
* @param n The optimization point index (starts at 0).
* @return The target bitrate (in bpp) for the optimization point 'n'.
*/
public float getTargetBitrate(int n) {
// overall target bitrate is counted as extra
return (n < nopt) ? optbrate[n] : totbrate;
}
/**
* Returns the number of extra layers to add after the optimization point
* 'n', but before optimization point 'n+1'. If there is no optimization
* point 'n+1' then they should be added before the overall target bitrate.
*
* @param n The optimization point index (starts at 0).
* @return The number of extra (unoptimized) layers to add after the
* optimization point 'n'
*/
public int getExtraLayers(int n) {
// overall target bitrate is counted as extra
return (n < nopt) ? extralyrs[n] : 0;
}
/**
* Adds a new optimization point, with target bitrate 'brate' and with
* 'elyrs' (unoptimized) extra layers after it. The target bitrate 'brate'
* must be larger than the previous optimization point. The arguments are
* checked and IllegalArgumentException is thrown if they are not correct.
*
* @param brate The target bitrate for the optimized layer.
* @param elyrs The number of extra (unoptimized) layers to add after the
* optimized layer.
*/
public void addOptPoint(float brate, int elyrs) {
// Check validity of arguments
if (brate <= 0) {
throw new
IllegalArgumentException("Target bitrate must be positive");
}
if (elyrs < 0) {
throw new IllegalArgumentException("The number of extra layers " +
"must be 0 or more");
}
if (nopt > 0 && optbrate[nopt - 1] >= brate) {
throw new
IllegalArgumentException("New optimization point must have " +
"a target bitrate higher than the " +
"preceding one");
}
// Check room for new optimization point
if (optbrate.length == nopt) { // Need more room
float[] tbr = optbrate;
int[] tel = extralyrs;
// both arrays always have same size
optbrate = new float[optbrate.length + SZ_INCR];
extralyrs = new int[extralyrs.length + SZ_INCR];
System.arraycopy(tbr, 0, optbrate, 0, nopt);
System.arraycopy(tel, 0, extralyrs, 0, nopt);
}
// Add new optimization point
optbrate[nopt] = brate;
extralyrs[nopt] = elyrs;
nopt++;
// Update total number of layers
totlyrs += 1 + elyrs;
}
}

Some files were not shown because too many files have changed in this diff Show more