first commit

This commit is contained in:
MaddoScientisto 2026-03-14 20:04:39 +01:00
commit 4d332ef662
27586 changed files with 3281783 additions and 0 deletions

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,124 @@
Manifest-Version: 1.0
Export-Package: org.apache.sanselan.formats.transparencyfilters;uses:=
"org.apache.sanselan,org.apache.sanselan.common",org.apache.sanselan.
formats.jpeg.exifRewrite;uses:="org.apache.sanselan,org.apache.sansel
an.formats.jpeg,org.apache.sanselan.util,org.apache.sanselan.common,o
rg.apache.sanselan.formats.tiff.write,org.apache.sanselan.common.byte
Sources",org.apache.sanselan.formats.jpeg.iptc;uses:="org.apache.sans
elan,org.apache.sanselan.formats.jpeg.xmp,org.apache.sanselan.formats
.jpeg,org.apache.sanselan.util,org.apache.sanselan.common,org.apache.
sanselan.common.byteSources",org.apache.sanselan.formats.tiff.datarea
ders;uses:="org.apache.sanselan.formats.tiff.constants,org.apache.san
selan.common.mylzw,org.apache.sanselan.formats.tiff,org.apache.sansel
an,org.apache.sanselan.common,org.apache.sanselan.formats.tiff.photom
etricinterpreters",org.apache.sanselan.formats.jpeg;uses:="org.apache
.sanselan.formats.tiff,org.apache.sanselan.formats.jpeg.iptc,org.apac
he.sanselan.util,org.apache.sanselan.common,org.apache.sanselan.forma
ts.jpeg.segments,org.apache.sanselan.formats.tiff.constants,org.apach
e.sanselan,org.apache.sanselan.formats.jpeg.xmp,org.apache.sanselan.c
ommon.byteSources",org.apache.sanselan.util;uses:="org.apache.sansela
n,org.apache.sanselan.common",org.apache.sanselan.formats.tiff.write;
uses:="org.apache.sanselan.formats.tiff,org.apache.sanselan.formats.t
iff.fieldtypes,org.apache.sanselan.util,org.apache.sanselan.common,or
g.apache.sanselan.formats.tiff.constants,org.apache.sanselan.common.m
ylzw,org.apache.sanselan,org.apache.sanselan.common.byteSources",org.
apache.sanselan.formats.jpeg.segments;uses:="org.apache.sanselan.form
ats.jpeg.iptc,org.apache.sanselan,org.apache.sanselan.formats.jpeg,or
g.apache.sanselan.util,org.apache.sanselan.common",org.apache.sansela
n.formats.psd;uses:="org.apache.sanselan,org.apache.sanselan.formats.
psd.datareaders,org.apache.sanselan.formats.psd.dataparsers,org.apach
e.sanselan.util,org.apache.sanselan.common,org.apache.sanselan.common
.byteSources",org.apache.sanselan.common.mylzw;uses:="org.apache.sans
elan.common",org.apache.sanselan.color;uses:="org.apache.sanselan.uti
l",org.apache.sanselan.palette;uses:="org.apache.sanselan,org.apache.
sanselan.util",org.apache.sanselan.formats.ico;uses:="org.apache.sans
elan,org.apache.sanselan.util,org.apache.sanselan.common,org.apache.s
anselan.common.byteSources",org.apache.sanselan.common.byteSources;us
es:="org.apache.sanselan.util,org.apache.sanselan.common",org.apache.
sanselan.formats.png;uses:="org.apache.sanselan.formats.transparencyf
ilters,org.apache.sanselan.icc,org.apache.sanselan.formats.png.chunks
,org.apache.sanselan.util,org.apache.sanselan.common,org.apache.sanse
lan,org.apache.sanselan.palette,org.apache.sanselan.formats.png.scanl
inefilters,org.apache.sanselan.common.byteSources",org.apache.sansela
n.formats.tiff;uses:="org.apache.sanselan.formats.tiff.fieldtypes,org
.apache.sanselan.formats.tiff.datareaders,org.apache.sanselan.util,or
g.apache.sanselan.common,org.apache.sanselan.formats.tiff.write,org.a
pache.sanselan.formats.tiff.constants,org.apache.sanselan,org.apache.
sanselan.formats.tiff.photometricinterpreters,org.apache.sanselan.com
mon.byteSources",org.apache.sanselan.icc;uses:="org.apache.sanselan,o
rg.apache.sanselan.util,org.apache.sanselan.common,org.apache.sansela
n.common.byteSources",org.apache.sanselan.formats.psd.datareaders;use
s:="org.apache.sanselan.common.mylzw,org.apache.sanselan,org.apache.s
anselan.formats.psd.dataparsers,org.apache.sanselan.common,org.apache
.sanselan.formats.psd",org.apache.sanselan.formats.png.chunks;uses:="
org.apache.sanselan.formats.png,org.apache.sanselan,org.apache.sansel
an.common",org.apache.sanselan.formats.gif;uses:="org.apache.sanselan
.common.mylzw,org.apache.sanselan,org.apache.sanselan.palette,org.apa
che.sanselan.util,org.apache.sanselan.common,org.apache.sanselan.comm
on.byteSources",org.apache.sanselan.formats.tiff.fieldtypes;uses:="or
g.apache.sanselan.formats.tiff.constants,org.apache.sanselan.formats.
tiff,org.apache.sanselan,org.apache.sanselan.util,org.apache.sanselan
.common",org.apache.sanselan.formats.pnm;uses:="org.apache.sanselan,o
rg.apache.sanselan.util,org.apache.sanselan.common,org.apache.sansela
n.common.byteSources",org.apache.sanselan.common;uses:="org.apache.sa
nselan.common.mylzw,org.apache.sanselan",org.apache.sanselan.formats.
bmp.writers;uses:="org.apache.sanselan.palette,org.apache.sanselan.co
mmon",org.apache.sanselan.formats.bmp.pixelparsers;uses:="org.apache.
sanselan,org.apache.sanselan.formats.bmp,org.apache.sanselan.common",
org.apache.sanselan.formats.tiff.constants;uses:="org.apache.sanselan
.formats.tiff,org.apache.sanselan,org.apache.sanselan.formats.tiff.fi
eldtypes,org.apache.sanselan.util,org.apache.sanselan.common",org.apa
che.sanselan.formats.bmp;uses:="org.apache.sanselan.util,org.apache.s
anselan.common,org.apache.sanselan.formats.bmp.writers,org.apache.san
selan.formats.bmp.pixelparsers,org.apache.sanselan,org.apache.sansela
n.palette,org.apache.sanselan.common.byteSources",org.apache.sanselan
;uses:="org.apache.sanselan.formats.png,org.apache.sanselan.formats.t
iff,org.apache.sanselan.icc,org.apache.sanselan.formats.gif,org.apach
e.sanselan.formats.jpeg,org.apache.sanselan.util,org.apache.sanselan.
formats.pnm,org.apache.sanselan.common,org.apache.sanselan.formats.ps
d,org.apache.sanselan.formats.bmp,org.apache.sanselan.formats.ico,org
.apache.sanselan.common.byteSources",org.apache.sanselan.formats.jpeg
.xmp;uses:="org.apache.sanselan.formats.jpeg.iptc,org.apache.sanselan
,org.apache.sanselan.formats.jpeg,org.apache.sanselan.common,org.apac
he.sanselan.common.byteSources",org.apache.sanselan.formats.tga,org.a
pache.sanselan.formats.psd.dataparsers;uses:="org.apache.sanselan.col
or,org.apache.sanselan.formats.psd",org.apache.sanselan.formats.png.s
canlinefilters;uses:="org.apache.sanselan",org.apache.sanselan.format
s.tiff.photometricinterpreters;uses:="org.apache.sanselan,org.apache.
sanselan.color"
Implementation-Title: Apache Sanselan
Implementation-Version: 0.97-incubator
Built-By: matthew
Tool: Bnd-0.0.238
Bundle-Name: Apache Sanselan
Created-By: Apache Maven Bundle Plugin
Implementation-Vendor-Id: org.apache
Build-Jdk: 1.6.0_10
Bundle-Version: 0.97.0.incubator
Bnd-LastModified: 1232506235720
Specification-Title: Apache Sanselan
Bundle-ManifestVersion: 2
Bundle-Description: Apache Sanselan is a pure-Java image library.
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-DocURL: http://cwiki.apache.org/SANSELAN/
Bundle-SymbolicName: org.apache.sanselan.sanselan
Import-Package: org.apache.sanselan,org.apache.sanselan.color,org.apac
he.sanselan.common,org.apache.sanselan.common.byteSources,org.apache.
sanselan.common.mylzw,org.apache.sanselan.formats.bmp,org.apache.sans
elan.formats.bmp.pixelparsers,org.apache.sanselan.formats.bmp.writers
,org.apache.sanselan.formats.gif,org.apache.sanselan.formats.ico,org.
apache.sanselan.formats.jpeg,org.apache.sanselan.formats.jpeg.exifRew
rite,org.apache.sanselan.formats.jpeg.iptc,org.apache.sanselan.format
s.jpeg.segments,org.apache.sanselan.formats.jpeg.xmp,org.apache.sanse
lan.formats.png,org.apache.sanselan.formats.png.chunks,org.apache.san
selan.formats.png.scanlinefilters,org.apache.sanselan.formats.pnm,org
.apache.sanselan.formats.psd,org.apache.sanselan.formats.psd.datapars
ers,org.apache.sanselan.formats.psd.datareaders,org.apache.sanselan.f
ormats.tga,org.apache.sanselan.formats.tiff,org.apache.sanselan.forma
ts.tiff.constants,org.apache.sanselan.formats.tiff.datareaders,org.ap
ache.sanselan.formats.tiff.fieldtypes,org.apache.sanselan.formats.tif
f.photometricinterpreters,org.apache.sanselan.formats.tiff.write,org.
apache.sanselan.formats.transparencyfilters,org.apache.sanselan.icc,o
rg.apache.sanselan.palette,org.apache.sanselan.util
Specification-Version: 0.97-incubator

View file

@ -0,0 +1,6 @@
Apache Sanselan
Copyright 2007-2009 The Apache Software Foundation.
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View file

@ -0,0 +1,91 @@
=============================================================
Welcome to Apache Sanselan <http://sanselan.apache.org/>
=============================================================
Apache Sanselan is an effort undergoing incubation at the Apache Software
Foundation (ASF), sponsored by the Apache Incubator PMC. Incubation is required
of all newly accepted projects until a further review indicates that the
infrastructure, communications, and decision making process have stabilized in a
manner consistent with other successful ASF projects. While incubation status is
not necessarily a reflection of the completeness or stability of the code, it
does indicate that the project has yet to be fully endorsed by the ASF.
License (see also LICENSE)
==============================
Collective work: Copyright 2007-2009 The Apache Software Foundation.
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Getting Started
===============
Mailing Lists
-------------
To get involved with the Apache Sanselan project, start by having a
look at our website (link at top of page) and join our mailing
lists by sending an empty message to
sanselan-dev-subscribe :at: incubator.apache.org
and
sanselan-commits-subscribe :at: incubator.apache.org
and the dev mailing list archives can be found at
http://incubator.apache.org/mail/sanselan-dev/
Downloading
-----------
The Sanselan source code is available via Subversion at
https://svn.apache.org/repos/asf/incubator/sanselan/trunk
and anonymous access is available at
http://svn.apache.org/repos/asf/incubator/sanselan/trunk
or with ViewVC at
http://svn.apache.org/viewvc/incubator/sanselan/trunk/
To checkout the main Sanselan source tree, run
svn checkout http://svn.apache.org/repos/asf/incubator/sanselan/trunk/sanselan
Once you have a copy of the source code tree, you can use Apache Maven
http://maven.apache.org/
to build the project. You should use Maven 2 to build Sanselan.
The minimal command to build, test, and package all the main Sanselan
components is:
mvn install
For more instructions, please see the documentation at:
http://sanselan.apache.org/doc/building.html
Credits
=======
See http://sanselan.apache.org/team-list.html for the list of
Sanselan committers and main contributors.

View file

@ -0,0 +1,5 @@
#Generated by org.apache.felix.bundleplugin
#Tue Jan 20 21:50:37 EST 2009
version=0.97-incubator
groupId=org.apache.sanselan
artifactId=sanselan

View file

@ -0,0 +1,357 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- ====================================================================== -->
<!-- P R O J E C T D E S C R I P T I O N -->
<!-- ====================================================================== -->
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
<version>4</version>
</parent>
<groupId>org.apache.sanselan</groupId>
<artifactId>sanselan</artifactId>
<name>Apache Sanselan</name>
<version>0.97-incubator</version>
<packaging>bundle</packaging>
<!--
Keep the description on a single line. Otherwise Maven might generate
a corrupted MANIFEST.MF (see http://jira.codehaus.org/browse/MJAR-4)
-->
<description>Apache Sanselan is a pure-Java image library.</description>
<url>http://sanselan.apache.org/</url>
<organization>
<url>http://cwiki.apache.org/SANSELAN/</url>
</organization>
<scm>
<connection>scm:svn:http://svn.apache.org/repos/asf/incubator/sanselan/tags/sanselan-0.97-incubator</connection>
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/incubator/sanselan/tags/sanselan-0.97-incubator</developerConnection>
<url>http://svn.apache.org/viewvc/incubator/sanselan/tags/sanselan-0.97-incubator</url>
</scm>
<issueManagement>
<system>Jira</system>
<url>http://issues.apache.org/jira/browse/SANSELAN</url>
</issueManagement>
<inceptionYear>2007</inceptionYear>
<prerequisites>
<maven>2.0.7</maven>
</prerequisites>
<build>
<plugins>
<!-- Use Java 1.4 everywhere -->
<plugin>
<inherited>true</inherited>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<target>1.4</target>
<source>1.4</source>
</configuration>
</plugin>
<!-- Add manifest entries for OSGi environments -->
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName>
<Export-Package>org.apache.sanselan.*</Export-Package>
<Specification-Title>${project.name}</Specification-Title>
<Specification-Version>${project.version}</Specification-Version>
<Specification-Vendor>${project.organization.name}</Specification-Vendor>
<Implementation-Title>${project.name}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
<Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.0.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<tagBase>https://svn.apache.org/repos/asf/incubator/sanselan/tags</tagBase>
<useReleaseProfile>false</useReleaseProfile>
<goals>deploy</goals>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xmx512m</argLine>
</configuration>
</plugin>
</plugins>
<!-- Let's be sure that we use the latest releases of the plugins -->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.0-beta-7</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.0-alpha-4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-2</version>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>1.4.0</version>
</plugin>
</plugins>
</pluginManagement>
<!-- Include LICENSE and NOTICE in binary distributions -->
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>.</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>LICENSE</include>
<include>NOTICE</include>
<include>README.txt</include>
</includes>
</resource>
</resources>
</build>
<profiles>
<profile>
<!-- The release profile contains specifal configuration of releases. -->
<id>release</id>
<build>
<plugins>
<!-- Sign the release -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>generate-javadocs</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<!--
- Copy LICENSE.txt and NOTICE.txt so that they are included
- in the -javadoc jar file for the component.
-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>javadoc.resources</id>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy todir="${project.build.directory}/apidocs/META-INF">
<fileset dir="${basedir}">
<include name="LICENSE" />
<include name="NOTICE" />
</fileset>
</copy>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>create-source-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- The release artifacts -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/assemble/bin.xml</descriptor>
<descriptor>src/assemble/src.xml</descriptor>
</descriptors>
<descriptorRefs>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</reporting>
<!-- ====================================================================== -->
<!-- M A I L I N G L I S T S -->
<!-- ====================================================================== -->
<mailingLists>
<mailingList>
<name>Sanselan Development List</name>
<subscribe>sanselan-dev-subscribe at incubator.apache.org</subscribe>
<unsubscribe>sanselan-dev-unsubscribe at incubator.apache.org</unsubscribe>
<post>sanselan-dev at incubator.apache.org</post>
<archive>http://mail-archives.apache.org/mod_mbox/sanselan-dev/</archive>
</mailingList>
<mailingList>
<name>Sanselan Source Control List</name>
<subscribe>sanselan-commits-subscribe@apache.org</subscribe>
<unsubscribe>sanselan-commits-unsubscribe@apache.org</unsubscribe>
<archive>http://mail-archives.apache.org/mod_mbox/sanselan-commits/</archive>
</mailingList>
</mailingLists>
<!-- ====================================================================== -->
<!-- P E O P L E -->
<!-- ====================================================================== -->
<developers>
<developer>
<name>Charles M. Chen</name>
<id>cmchen</id>
</developer>
<developer>
<name>Philipp Koch</name>
<id>pkoch</id>
</developer>
<developer>
<name>Jeremias Maerki</name>
<id>jeremias</id>
</developer>
<developer>
<name>Craig Russell</name>
<id>clr</id>
</developer>
<developer>
<name>Yoav Shapira</name>
<id>yoavs</id>
</developer>
<developer>
<name>Carsten Ziegeler</name>
<id>cziegeler</id>
</developer>
</developers>
</project>

View file

@ -0,0 +1,132 @@
package org.apache.sanselan;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.ImagingOpException;
import java.io.File;
import java.io.IOException;
public class ColorTools {
public BufferedImage correctImage(BufferedImage src, File file) throws ImageReadException, IOException {
ICC_Profile icc = Sanselan.getICCProfile(file);
if (icc == null)
return src;
ICC_ColorSpace cs = new ICC_ColorSpace(icc);
BufferedImage dst = convertFromColorSpace(src, cs);
return dst;
}
public BufferedImage relabelColorSpace(BufferedImage bi, ICC_Profile profile) throws ImagingOpException {
ICC_ColorSpace cs = new ICC_ColorSpace(profile);
return relabelColorSpace(bi, cs);
}
public BufferedImage relabelColorSpace(BufferedImage bi, ColorSpace cs) throws ImagingOpException {
ColorModel cm = deriveColorModel(bi, cs);
return relabelColorSpace(bi, cm);
}
public BufferedImage relabelColorSpace(BufferedImage bi, ColorModel cm) throws ImagingOpException {
BufferedImage result = new BufferedImage(cm, bi.getRaster(), false, null);
return result;
}
public ColorModel deriveColorModel(BufferedImage bi, ColorSpace cs) throws ImagingOpException {
return deriveColorModel(bi, cs, false);
}
public ColorModel deriveColorModel(BufferedImage bi, ColorSpace cs, boolean force_no_alpha) throws ImagingOpException {
return deriveColorModel(bi.getColorModel(), cs, force_no_alpha);
}
public ColorModel deriveColorModel(ColorModel old_cm, ColorSpace cs, boolean force_no_alpha) throws ImagingOpException {
if (old_cm instanceof ComponentColorModel) {
ComponentColorModel ccm = (ComponentColorModel)old_cm;
if (force_no_alpha)
return new ComponentColorModel(cs, false, false, 1, ccm.getTransferType());
return new ComponentColorModel(cs, ccm.hasAlpha(), ccm.isAlphaPremultiplied(), ccm.getTransparency(), ccm.getTransferType());
}
if (old_cm instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel)old_cm;
int old_mask = dcm.getRedMask() | dcm.getGreenMask() | dcm.getBlueMask() | dcm.getAlphaMask();
int old_bits = count_bits_in_mask(old_mask);
return new DirectColorModel(cs, old_bits, dcm.getRedMask(), dcm.getGreenMask(), dcm.getBlueMask(), dcm.getAlphaMask(), dcm.isAlphaPremultiplied(), dcm.getTransferType());
}
throw new ImagingOpException("Could not clone unknown ColorModel Type.");
}
private int count_bits_in_mask(int i) {
int count = 0;
while (i != 0) {
count += i & 0x1;
i >>>= 1;
}
return count;
}
public BufferedImage convertToColorSpace(BufferedImage bi, ColorSpace to) {
ColorSpace from = bi.getColorModel().getColorSpace();
RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
ColorConvertOp op = new ColorConvertOp(from, to, hints);
BufferedImage result = op.filter(bi, null);
result = relabelColorSpace(result, to);
return result;
}
public BufferedImage convertTosRGB(BufferedImage bi) {
ColorSpace cs_sRGB = ColorSpace.getInstance(1000);
ColorModel srgbCM = ColorModel.getRGBdefault();
cs_sRGB = srgbCM.getColorSpace();
return convertToColorSpace(bi, cs_sRGB);
}
protected BufferedImage convertFromColorSpace(BufferedImage bi, ColorSpace from) {
ColorModel srgbCM = ColorModel.getRGBdefault();
ColorSpace cs_sRGB = srgbCM.getColorSpace();
return convertBetweenColorSpaces(bi, from, cs_sRGB);
}
public BufferedImage convertBetweenICCProfiles(BufferedImage bi, ICC_Profile from, ICC_Profile to) {
ICC_ColorSpace cs_from = new ICC_ColorSpace(from);
ICC_ColorSpace cs_to = new ICC_ColorSpace(to);
return convertBetweenColorSpaces(bi, cs_from, cs_to);
}
public BufferedImage convertToICCProfile(BufferedImage bi, ICC_Profile to) {
ICC_ColorSpace cs_to = new ICC_ColorSpace(to);
return convertToColorSpace(bi, cs_to);
}
public BufferedImage convertBetweenColorSpacesX2(BufferedImage bi, ColorSpace from, ColorSpace to) {
RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
bi = relabelColorSpace(bi, from);
ColorConvertOp op = new ColorConvertOp(from, to, hints);
bi = op.filter(bi, null);
bi = relabelColorSpace(bi, from);
bi = op.filter(bi, null);
bi = relabelColorSpace(bi, to);
return bi;
}
public BufferedImage convertBetweenColorSpaces(BufferedImage bi, ColorSpace from, ColorSpace to) {
RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
ColorConvertOp op = new ColorConvertOp(from, to, hints);
bi = relabelColorSpace(bi, from);
BufferedImage result = op.filter(bi, null);
result = relabelColorSpace(result, to);
return result;
}
}

View file

@ -0,0 +1,112 @@
package org.apache.sanselan;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
public class FormatCompliance {
private final boolean failOnError;
private final String description;
private final ArrayList comments = new ArrayList();
public FormatCompliance(String description) {
this.description = description;
this.failOnError = false;
}
public FormatCompliance(String description, boolean fail_on_error) {
this.description = description;
this.failOnError = fail_on_error;
}
public static final FormatCompliance getDefault() {
return new FormatCompliance("ignore", false);
}
public void addComment(String s) throws ImageReadException {
this.comments.add(s);
if (this.failOnError)
throw new ImageReadException(s);
}
public void addComment(String s, int value) throws ImageReadException {
addComment(s + ": " + getValueDescription(value));
}
public String toString() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
dump(pw);
return sw.getBuffer().toString();
}
public void dump() {
dump(new PrintWriter(new OutputStreamWriter(System.out)));
}
public void dump(PrintWriter pw) {
pw.println("Format Compliance: " + this.description);
if (this.comments.size() == 0) {
pw.println("\tNo comments.");
} else {
for (int i = 0; i < this.comments.size(); i++)
pw.println("\t" + (i + 1) + ": " + this.comments.get(i));
}
pw.println("");
pw.flush();
}
private String getValueDescription(int value) {
return value + " (" + Integer.toHexString(value) + ")";
}
public boolean compare_bytes(String name, byte[] expected, byte[] actual) throws ImageReadException {
if (expected.length != actual.length) {
addComment(name + ": " + "Unexpected length: (expected: " + expected.length + ", actual: " + actual.length + ")");
return false;
}
for (int i = 0; i < expected.length; i++) {
if (expected[i] != actual[i]) {
addComment(name + ": " + "Unexpected value: (expected: " + getValueDescription(expected[i]) + ", actual: " + getValueDescription(actual[i]) + ")");
return false;
}
}
return true;
}
public boolean checkBounds(String name, int min, int max, int actual) throws ImageReadException {
if (actual < min || actual > max) {
addComment(name + ": " + "bounds check: " + min + " <= " + actual + " <= " + max + ": false");
return false;
}
return true;
}
public boolean compare(String name, int valid, int actual) throws ImageReadException {
return compare(name, new int[] { valid }, actual);
}
public boolean compare(String name, int[] valid, int actual) throws ImageReadException {
for (int i = 0; i < valid.length; i++) {
if (actual == valid[i])
return true;
}
StringBuffer result = new StringBuffer();
result.append(name + ": " + "Unexpected value: (valid: ");
if (valid.length > 1)
result.append("{");
for (int j = 0; j < valid.length; j++) {
if (j > 0)
result.append(", ");
result.append(getValueDescription(valid[j]));
}
if (valid.length > 1)
result.append("}");
result.append(", actual: " + getValueDescription(actual) + ")");
addComment(result.toString());
return false;
}
}

View file

@ -0,0 +1,67 @@
package org.apache.sanselan;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.apache.sanselan.icc.IccProfileInfo;
import org.apache.sanselan.icc.IccProfileParser;
public class ImageDump {
private String colorSpaceTypeToName(ColorSpace cs) {
switch (cs.getType()) {
case 9:
return "TYPE_CMYK";
case 5:
return "TYPE_RGB";
case 1000:
return "CS_sRGB";
case 1003:
return "CS_GRAY";
case 1001:
return "CS_CIEXYZ";
case 1004:
return "CS_LINEAR_RGB";
case 1002:
return "CS_PYCC";
}
return "unknown";
}
public void dumpColorSpace(String prefix, ColorSpace cs) throws ImageReadException, IOException {
System.out.println(prefix + ": " + "type: " + cs.getType() + " (" + colorSpaceTypeToName(cs) + ")");
if (!(cs instanceof ICC_ColorSpace)) {
System.out.println(prefix + ": " + "Unknown ColorSpace: " + cs.getClass().getName());
return;
}
ICC_ColorSpace fICC_ColorSpace = (ICC_ColorSpace)cs;
ICC_Profile fICC_Profile = fICC_ColorSpace.getProfile();
byte[] bytes = fICC_Profile.getData();
IccProfileParser parser = new IccProfileParser();
IccProfileInfo info = parser.getICCProfileInfo(bytes);
info.dump(prefix);
}
public void dump(BufferedImage src) throws ImageReadException, IOException {
dump("", src);
}
public void dump(String prefix, BufferedImage src) throws ImageReadException, IOException {
System.out.println(prefix + ": " + "dump");
dumpColorSpace(prefix, src.getColorModel().getColorSpace());
dumpBIProps(prefix, src);
}
public void dumpBIProps(String prefix, BufferedImage src) {
String[] keys = src.getPropertyNames();
if (keys == null) {
System.out.println(prefix + ": no props");
return;
}
for (int i = 0; i < keys.length; i++) {
String key = keys[i];
System.out.println(prefix + ": " + key + ": " + src.getProperty(key));
}
}
}

View file

@ -0,0 +1,71 @@
package org.apache.sanselan;
public class ImageFormat {
public final String name;
public final String extension;
public final boolean actual;
private ImageFormat(String name, boolean actual) {
this.name = name;
this.extension = name;
this.actual = actual;
}
private ImageFormat(String name) {
this.name = name;
this.extension = name;
this.actual = true;
}
public boolean equals(Object o) {
if (!(o instanceof ImageFormat))
return false;
ImageFormat other = (ImageFormat)o;
return other.name.equals(this.name);
}
public String toString() {
return "{" + this.name + ": " + this.extension + "}";
}
public int hashCode() {
return this.name.hashCode();
}
public static final ImageFormat IMAGE_FORMAT_UNKNOWN = new ImageFormat("UNKNOWN", false);
public static final ImageFormat IMAGE_FORMAT_PNG = new ImageFormat("PNG");
public static final ImageFormat IMAGE_FORMAT_GIF = new ImageFormat("GIF");
public static final ImageFormat IMAGE_FORMAT_ICO = new ImageFormat("ICO");
public static final ImageFormat IMAGE_FORMAT_TIFF = new ImageFormat("TIFF");
public static final ImageFormat IMAGE_FORMAT_JPEG = new ImageFormat("JPEG");
public static final ImageFormat IMAGE_FORMAT_BMP = new ImageFormat("BMP");
public static final ImageFormat IMAGE_FORMAT_PSD = new ImageFormat("PSD");
public static final ImageFormat IMAGE_FORMAT_PBM = new ImageFormat("PBM");
public static final ImageFormat IMAGE_FORMAT_PGM = new ImageFormat("PGM");
public static final ImageFormat IMAGE_FORMAT_PPM = new ImageFormat("PPM");
public static final ImageFormat IMAGE_FORMAT_PNM = new ImageFormat("PNM");
public static final ImageFormat IMAGE_FORMAT_TGA = new ImageFormat("TGA");
public static final ImageFormat IMAGE_FORMAT_JBIG2 = new ImageFormat("JBig2");
public static final ImageFormat[] getAllFormats() {
ImageFormat[] result = {
IMAGE_FORMAT_UNKNOWN, IMAGE_FORMAT_PNG, IMAGE_FORMAT_GIF, IMAGE_FORMAT_TIFF, IMAGE_FORMAT_JPEG, IMAGE_FORMAT_BMP, IMAGE_FORMAT_PSD, IMAGE_FORMAT_PBM, IMAGE_FORMAT_PGM, IMAGE_FORMAT_PPM,
IMAGE_FORMAT_PNM, IMAGE_FORMAT_TGA, IMAGE_FORMAT_JBIG2 };
return result;
}
}

View file

@ -0,0 +1,215 @@
package org.apache.sanselan;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
public class ImageInfo {
private final String formatDetails;
private final int bitsPerPixel;
private final ArrayList comments;
private final ImageFormat format;
private final String formatName;
private final int height;
private final String mimeType;
private final int numberOfImages;
private final int physicalHeightDpi;
private final float physicalHeightInch;
private final int physicalWidthDpi;
private final float physicalWidthInch;
private final int width;
private final boolean isProgressive;
private final boolean isTransparent;
private final boolean usesPalette;
public static final int COLOR_TYPE_BW = 0;
public static final int COLOR_TYPE_GRAYSCALE = 1;
public static final int COLOR_TYPE_RGB = 2;
public static final int COLOR_TYPE_CMYK = 3;
public static final int COLOR_TYPE_OTHER = -1;
public static final int COLOR_TYPE_UNKNOWN = -2;
private final int colorType;
public static final String COMPRESSION_ALGORITHM_UNKNOWN = "Unknown";
public static final String COMPRESSION_ALGORITHM_NONE = "None";
public static final String COMPRESSION_ALGORITHM_LZW = "LZW";
public static final String COMPRESSION_ALGORITHM_PACKBITS = "PackBits";
public static final String COMPRESSION_ALGORITHM_JPEG = "JPEG";
public static final String COMPRESSION_ALGORITHM_RLE = "RLE: Run-Length Encoding";
public static final String COMPRESSION_ALGORITHM_PSD = "Photoshop";
public static final String COMPRESSION_ALGORITHM_PNG_FILTER = "PNG Filter";
public static final String COMPRESSION_ALGORITHM_CCITT_GROUP_3 = "CCITT Group 3 1-Dimensional Modified Huffman run-length encoding.";
public static final String COMPRESSION_ALGORITHM_CCITT_GROUP_4 = "CCITT Group 4";
public static final String COMPRESSION_ALGORITHM_CCITT_1D = "CCITT 1D";
private final String compressionAlgorithm;
public ImageInfo(String formatDetails, int bitsPerPixel, ArrayList comments, ImageFormat format, String formatName, int height, String mimeType, int numberOfImages, int physicalHeightDpi, float physicalHeightInch, int physicalWidthDpi, float physicalWidthInch, int width, boolean isProgressive, boolean isTransparent, boolean usesPalette, int colorType, String compressionAlgorithm) {
this.formatDetails = formatDetails;
this.bitsPerPixel = bitsPerPixel;
this.comments = comments;
this.format = format;
this.formatName = formatName;
this.height = height;
this.mimeType = mimeType;
this.numberOfImages = numberOfImages;
this.physicalHeightDpi = physicalHeightDpi;
this.physicalHeightInch = physicalHeightInch;
this.physicalWidthDpi = physicalWidthDpi;
this.physicalWidthInch = physicalWidthInch;
this.width = width;
this.isProgressive = isProgressive;
this.isTransparent = isTransparent;
this.usesPalette = usesPalette;
this.colorType = colorType;
this.compressionAlgorithm = compressionAlgorithm;
}
public int getBitsPerPixel() {
return this.bitsPerPixel;
}
public ArrayList getComments() {
return new ArrayList(this.comments);
}
public ImageFormat getFormat() {
return this.format;
}
public String getFormatName() {
return this.formatName;
}
public int getHeight() {
return this.height;
}
public String getMimeType() {
return this.mimeType;
}
public int getNumberOfImages() {
return this.numberOfImages;
}
public int getPhysicalHeightDpi() {
return this.physicalHeightDpi;
}
public float getPhysicalHeightInch() {
return this.physicalHeightInch;
}
public int getPhysicalWidthDpi() {
return this.physicalWidthDpi;
}
public float getPhysicalWidthInch() {
return this.physicalWidthInch;
}
public int getWidth() {
return this.width;
}
public boolean getIsProgressive() {
return this.isProgressive;
}
public int getColorType() {
return this.colorType;
}
public String getColorTypeDescription() {
switch (this.colorType) {
case 0:
return "Black and White";
case 1:
return "Grayscale";
case 2:
return "RGB";
case 3:
return "CMYK";
case -1:
return "Other";
case -2:
return "Unknown";
}
return "Unknown";
}
public void dump() {
System.out.print(toString());
}
public String toString() {
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
toString(pw, "");
pw.flush();
return sw.toString();
} catch (Exception e) {
return "Image Data: Error";
}
}
public void toString(PrintWriter pw, String prefix) throws ImageReadException, IOException {
pw.println("Format Details: " + this.formatDetails);
pw.println("Bits Per Pixel: " + this.bitsPerPixel);
pw.println("Comments: " + this.comments.size());
for (int i = 0; i < this.comments.size(); i++) {
String s = (String)this.comments.get(i);
pw.println("\t" + i + ": '" + s + "'");
}
pw.println("Format: " + this.format.name);
pw.println("Format Name: " + this.formatName);
pw.println("Compression Algorithm: " + this.compressionAlgorithm);
pw.println("Height: " + this.height);
pw.println("MimeType: " + this.mimeType);
pw.println("Number Of Images: " + this.numberOfImages);
pw.println("Physical Height Dpi: " + this.physicalHeightDpi);
pw.println("Physical Height Inch: " + this.physicalHeightInch);
pw.println("Physical Width Dpi: " + this.physicalWidthDpi);
pw.println("Physical Width Inch: " + this.physicalWidthInch);
pw.println("Width: " + this.width);
pw.println("Is Progressive: " + this.isProgressive);
pw.println("Is Transparent: " + this.isTransparent);
pw.println("Color Type: " + getColorTypeDescription());
pw.println("Uses Palette: " + this.usesPalette);
pw.flush();
}
}

View file

@ -0,0 +1,250 @@
package org.apache.sanselan;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Map;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.IBufferedImageFactory;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.SimpleBufferedImageFactory;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceArray;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.formats.bmp.BmpImageParser;
import org.apache.sanselan.formats.gif.GifImageParser;
import org.apache.sanselan.formats.ico.IcoImageParser;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
import org.apache.sanselan.formats.png.PngImageParser;
import org.apache.sanselan.formats.pnm.PNMImageParser;
import org.apache.sanselan.formats.psd.PsdImageParser;
import org.apache.sanselan.formats.tiff.TiffImageParser;
import org.apache.sanselan.util.Debug;
public abstract class ImageParser extends BinaryFileParser implements SanselanConstants {
public static final ImageParser[] getAllImageParsers() {
ImageParser[] result = { new JpegImageParser(), new TiffImageParser(), new PngImageParser(), new BmpImageParser(), new GifImageParser(), new PsdImageParser(), new PNMImageParser(), new IcoImageParser() };
return result;
}
public final IImageMetadata getMetadata(ByteSource byteSource) throws ImageReadException, IOException {
return getMetadata(byteSource, null);
}
public abstract IImageMetadata getMetadata(ByteSource paramByteSource, Map paramMap) throws ImageReadException, IOException;
public final IImageMetadata getMetadata(byte[] bytes) throws ImageReadException, IOException {
return getMetadata(bytes);
}
public final IImageMetadata getMetadata(byte[] bytes, Map params) throws ImageReadException, IOException {
return getMetadata(new ByteSourceArray(bytes), params);
}
public final IImageMetadata getMetadata(File file) throws ImageReadException, IOException {
return getMetadata(file, null);
}
public final IImageMetadata getMetadata(File file, Map params) throws ImageReadException, IOException {
if (this.debug)
System.out.println(getName() + ".getMetadata" + ": " + file.getName());
if (!canAcceptExtension(file))
return null;
return getMetadata(new ByteSourceFile(file), params);
}
public abstract ImageInfo getImageInfo(ByteSource paramByteSource, Map paramMap) throws ImageReadException, IOException;
public final ImageInfo getImageInfo(ByteSource byteSource) throws ImageReadException, IOException {
return getImageInfo(byteSource, null);
}
public final ImageInfo getImageInfo(byte[] bytes, Map params) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceArray(bytes), params);
}
public final ImageInfo getImageInfo(File file, Map params) throws ImageReadException, IOException {
if (!canAcceptExtension(file))
return null;
return getImageInfo(new ByteSourceFile(file), params);
}
public FormatCompliance getFormatCompliance(ByteSource byteSource) throws ImageReadException, IOException {
return null;
}
public final FormatCompliance getFormatCompliance(byte[] bytes) throws ImageReadException, IOException {
return getFormatCompliance(new ByteSourceArray(bytes));
}
public final FormatCompliance getFormatCompliance(File file) throws ImageReadException, IOException {
if (!canAcceptExtension(file))
return null;
return getFormatCompliance(new ByteSourceFile(file));
}
public ArrayList getAllBufferedImages(ByteSource byteSource) throws ImageReadException, IOException {
BufferedImage bi = getBufferedImage(byteSource, null);
ArrayList result = new ArrayList();
result.add(bi);
return result;
}
public final ArrayList getAllBufferedImages(byte[] bytes) throws ImageReadException, IOException {
return getAllBufferedImages(new ByteSourceArray(bytes));
}
public final ArrayList getAllBufferedImages(File file) throws ImageReadException, IOException {
if (!canAcceptExtension(file))
return null;
return getAllBufferedImages(new ByteSourceFile(file));
}
public abstract BufferedImage getBufferedImage(ByteSource paramByteSource, Map paramMap) throws ImageReadException, IOException;
public final BufferedImage getBufferedImage(byte[] bytes, Map params) throws ImageReadException, IOException {
return getBufferedImage(new ByteSourceArray(bytes), params);
}
public final BufferedImage getBufferedImage(File file, Map params) throws ImageReadException, IOException {
if (!canAcceptExtension(file))
return null;
return getBufferedImage(new ByteSourceFile(file), params);
}
public void writeImage(BufferedImage src, OutputStream os, Map params) throws ImageWriteException, IOException {
try {
os.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
throw new ImageWriteException("This image format (" + getName() + ") cannot be written.");
}
public final Dimension getImageSize(byte[] bytes) throws ImageReadException, IOException {
return getImageSize(bytes, null);
}
public final Dimension getImageSize(byte[] bytes, Map params) throws ImageReadException, IOException {
return getImageSize(new ByteSourceArray(bytes), params);
}
public final Dimension getImageSize(File file) throws ImageReadException, IOException {
return getImageSize(file, null);
}
public final Dimension getImageSize(File file, Map params) throws ImageReadException, IOException {
if (!canAcceptExtension(file))
return null;
return getImageSize(new ByteSourceFile(file), params);
}
public abstract Dimension getImageSize(ByteSource paramByteSource, Map paramMap) throws ImageReadException, IOException;
public abstract String getXmpXml(ByteSource paramByteSource, Map paramMap) throws ImageReadException, IOException;
public final byte[] getICCProfileBytes(byte[] bytes) throws ImageReadException, IOException {
return getICCProfileBytes(bytes, null);
}
public final byte[] getICCProfileBytes(byte[] bytes, Map params) throws ImageReadException, IOException {
return getICCProfileBytes(new ByteSourceArray(bytes), params);
}
public final byte[] getICCProfileBytes(File file) throws ImageReadException, IOException {
return getICCProfileBytes(file, null);
}
public final byte[] getICCProfileBytes(File file, Map params) throws ImageReadException, IOException {
if (!canAcceptExtension(file))
return null;
if (this.debug)
System.out.println(getName() + ": " + file.getName());
return getICCProfileBytes(new ByteSourceFile(file), params);
}
public abstract byte[] getICCProfileBytes(ByteSource paramByteSource, Map paramMap) throws ImageReadException, IOException;
public final String dumpImageFile(byte[] bytes) throws ImageReadException, IOException {
return dumpImageFile(new ByteSourceArray(bytes));
}
public final String dumpImageFile(File file) throws ImageReadException, IOException {
if (!canAcceptExtension(file))
return null;
if (this.debug)
System.out.println(getName() + ": " + file.getName());
return dumpImageFile(new ByteSourceFile(file));
}
public final String dumpImageFile(ByteSource byteSource) throws ImageReadException, IOException {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
dumpImageFile(pw, byteSource);
pw.flush();
return sw.toString();
}
public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource) throws ImageReadException, IOException {
return false;
}
public abstract boolean embedICCProfile(File paramFile1, File paramFile2, byte[] paramArrayOfbyte);
public abstract String getName();
public abstract String getDefaultExtension();
protected abstract String[] getAcceptedExtensions();
protected abstract ImageFormat[] getAcceptedTypes();
public boolean canAcceptType(ImageFormat type) {
ImageFormat[] types = getAcceptedTypes();
for (int i = 0; i < types.length; i++) {
if (types[i].equals(type))
return true;
}
return false;
}
protected final boolean canAcceptExtension(File file) {
return canAcceptExtension(file.getName());
}
protected final boolean canAcceptExtension(String filename) {
String[] exts = getAcceptedExtensions();
if (exts == null)
return true;
int index = filename.lastIndexOf('.');
if (index >= 0) {
String ext = filename.substring(index);
ext = ext.toLowerCase();
for (int i = 0; i < exts.length; i++) {
if (exts[i].toLowerCase().equals(ext))
return true;
}
}
return false;
}
protected IBufferedImageFactory getBufferedImageFactory(Map params) {
if (params == null)
return new SimpleBufferedImageFactory();
IBufferedImageFactory result = (IBufferedImageFactory)params.get("BUFFERED_IMAGE_FACTORY");
if (null != result)
return result;
return new SimpleBufferedImageFactory();
}
public static final boolean isStrict(Map params) {
if (params == null || !params.containsKey("STRICT"))
return false;
return ((Boolean)params.get("STRICT")).booleanValue();
}
}

View file

@ -0,0 +1,13 @@
package org.apache.sanselan;
public class ImageReadException extends SanselanException {
static final long serialVersionUID = -1L;
public ImageReadException(String s) {
super(s);
}
public ImageReadException(String s, Exception e) {
super(s, e);
}
}

View file

@ -0,0 +1,13 @@
package org.apache.sanselan;
public class ImageWriteException extends SanselanException {
static final long serialVersionUID = -1L;
public ImageWriteException(String s) {
super(s);
}
public ImageWriteException(String s, Exception e) {
super(s, e);
}
}

View file

@ -0,0 +1,431 @@
package org.apache.sanselan;
import java.awt.Dimension;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceArray;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
import org.apache.sanselan.icc.IccProfileInfo;
import org.apache.sanselan.icc.IccProfileParser;
import org.apache.sanselan.util.Debug;
public abstract class Sanselan implements SanselanConstants {
public static boolean hasImageFileExtension(File file) {
if (!file.isFile())
return false;
return hasImageFileExtension(file.getName());
}
public static boolean hasImageFileExtension(String filename) {
filename = filename.toLowerCase();
ImageParser[] imageParsers = ImageParser.getAllImageParsers();
for (int i = 0; i < imageParsers.length; i++) {
ImageParser imageParser = imageParsers[i];
String[] exts = imageParser.getAcceptedExtensions();
for (int j = 0; j < exts.length; j++) {
String ext = exts[j];
if (filename.endsWith(ext.toLowerCase()))
return true;
}
}
return false;
}
public static ImageFormat guessFormat(byte[] bytes) throws ImageReadException, IOException {
return guessFormat(new ByteSourceArray(bytes));
}
public static ImageFormat guessFormat(File file) throws ImageReadException, IOException {
return guessFormat(new ByteSourceFile(file));
}
public static ImageFormat guessFormat(ByteSource byteSource) throws ImageReadException, IOException {
InputStream is = null;
try {
is = byteSource.getInputStream();
int i1 = is.read();
int i2 = is.read();
if (i1 < 0 || i2 < 0)
throw new ImageReadException("Couldn't read magic numbers to guess format.");
int b1 = i1 & 0xFF;
int b2 = i2 & 0xFF;
if (b1 == 71 && b2 == 73)
return ImageFormat.IMAGE_FORMAT_GIF;
if (b1 == 137 && b2 == 80)
return ImageFormat.IMAGE_FORMAT_PNG;
if (b1 == 255 && b2 == 216)
return ImageFormat.IMAGE_FORMAT_JPEG;
if (b1 == 66 && b2 == 77)
return ImageFormat.IMAGE_FORMAT_BMP;
if (b1 == 77 && b2 == 77)
return ImageFormat.IMAGE_FORMAT_TIFF;
if (b1 == 73 && b2 == 73)
return ImageFormat.IMAGE_FORMAT_TIFF;
if (b1 == 56 && b2 == 66)
return ImageFormat.IMAGE_FORMAT_PSD;
if (b1 == 80 && b2 == 49)
return ImageFormat.IMAGE_FORMAT_PBM;
if (b1 == 80 && b2 == 52)
return ImageFormat.IMAGE_FORMAT_PBM;
if (b1 == 80 && b2 == 50)
return ImageFormat.IMAGE_FORMAT_PGM;
if (b1 == 80 && b2 == 53)
return ImageFormat.IMAGE_FORMAT_PGM;
if (b1 == 80 && b2 == 51)
return ImageFormat.IMAGE_FORMAT_PPM;
if (b1 == 80 && b2 == 54)
return ImageFormat.IMAGE_FORMAT_PPM;
if (b1 == 151 && b2 == 74) {
int i3 = is.read();
int i4 = is.read();
if (i3 < 0 || i4 < 0)
throw new ImageReadException("Couldn't read magic numbers to guess format.");
int b3 = i3 & 0xFF;
int b4 = i4 & 0xFF;
if (b3 == 66 && b4 == 50)
return ImageFormat.IMAGE_FORMAT_JBIG2;
}
return ImageFormat.IMAGE_FORMAT_UNKNOWN;
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
Debug.debug((Throwable)e);
}
}
}
public static ICC_Profile getICCProfile(byte[] bytes) throws ImageReadException, IOException {
return getICCProfile(bytes, null);
}
public static ICC_Profile getICCProfile(byte[] bytes, Map params) throws ImageReadException, IOException {
return getICCProfile(new ByteSourceArray(bytes), params);
}
public static ICC_Profile getICCProfile(InputStream is, String filename) throws ImageReadException, IOException {
return getICCProfile(is, filename, null);
}
public static ICC_Profile getICCProfile(InputStream is, String filename, Map params) throws ImageReadException, IOException {
return getICCProfile(new ByteSourceInputStream(is, filename), params);
}
public static ICC_Profile getICCProfile(File file) throws ImageReadException, IOException {
return getICCProfile(file, null);
}
public static ICC_Profile getICCProfile(File file, Map params) throws ImageReadException, IOException {
return getICCProfile(new ByteSourceFile(file), params);
}
protected static ICC_Profile getICCProfile(ByteSource byteSource, Map params) throws ImageReadException, IOException {
byte[] bytes = getICCProfileBytes(byteSource, params);
if (bytes == null)
return null;
IccProfileParser parser = new IccProfileParser();
IccProfileInfo info = parser.getICCProfileInfo(bytes);
if (info.issRGB())
return null;
ICC_Profile icc = ICC_Profile.getInstance(bytes);
return icc;
}
public static byte[] getICCProfileBytes(byte[] bytes) throws ImageReadException, IOException {
return getICCProfileBytes(bytes, null);
}
public static byte[] getICCProfileBytes(byte[] bytes, Map params) throws ImageReadException, IOException {
return getICCProfileBytes(new ByteSourceArray(bytes), params);
}
public static byte[] getICCProfileBytes(File file) throws ImageReadException, IOException {
return getICCProfileBytes(file, null);
}
public static byte[] getICCProfileBytes(File file, Map params) throws ImageReadException, IOException {
return getICCProfileBytes(new ByteSourceFile(file), params);
}
private static byte[] getICCProfileBytes(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
return imageParser.getICCProfileBytes(byteSource, params);
}
public static ImageInfo getImageInfo(String filename, byte[] bytes, Map params) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceArray(filename, bytes), params);
}
public static ImageInfo getImageInfo(String filename, byte[] bytes) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceArray(filename, bytes), null);
}
public static ImageInfo getImageInfo(InputStream is, String filename) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceInputStream(is, filename), null);
}
public static ImageInfo getImageInfo(InputStream is, String filename, Map params) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceInputStream(is, filename), params);
}
public static ImageInfo getImageInfo(byte[] bytes) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceArray(bytes), null);
}
public static ImageInfo getImageInfo(byte[] bytes, Map params) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceArray(bytes), params);
}
public static ImageInfo getImageInfo(File file, Map params) throws ImageReadException, IOException {
return getImageInfo(new ByteSourceFile(file), params);
}
public static ImageInfo getImageInfo(File file) throws ImageReadException, IOException {
return getImageInfo(file, null);
}
private static ImageInfo getImageInfo(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
ImageInfo imageInfo = imageParser.getImageInfo(byteSource, params);
return imageInfo;
}
private static final ImageParser getImageParser(ByteSource byteSource) throws ImageReadException, IOException {
ImageFormat format = guessFormat(byteSource);
if (!format.equals(ImageFormat.IMAGE_FORMAT_UNKNOWN)) {
ImageParser[] imageParsers = ImageParser.getAllImageParsers();
for (int i = 0; i < imageParsers.length; i++) {
ImageParser imageParser = imageParsers[i];
if (imageParser.canAcceptType(format))
return imageParser;
}
}
String filename = byteSource.getFilename();
if (filename != null) {
ImageParser[] imageParsers = ImageParser.getAllImageParsers();
for (int i = 0; i < imageParsers.length; i++) {
ImageParser imageParser = imageParsers[i];
if (imageParser.canAcceptExtension(filename))
return imageParser;
}
}
throw new ImageReadException("Can't parse this format.");
}
public static Dimension getImageSize(InputStream is, String filename) throws ImageReadException, IOException {
return getImageSize(is, filename, null);
}
public static Dimension getImageSize(InputStream is, String filename, Map params) throws ImageReadException, IOException {
return getImageSize(new ByteSourceInputStream(is, filename), params);
}
public static Dimension getImageSize(byte[] bytes) throws ImageReadException, IOException {
return getImageSize(bytes, null);
}
public static Dimension getImageSize(byte[] bytes, Map params) throws ImageReadException, IOException {
return getImageSize(new ByteSourceArray(bytes), params);
}
public static Dimension getImageSize(File file) throws ImageReadException, IOException {
return getImageSize(file, null);
}
public static Dimension getImageSize(File file, Map params) throws ImageReadException, IOException {
return getImageSize(new ByteSourceFile(file), params);
}
public static Dimension getImageSize(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
return imageParser.getImageSize(byteSource, params);
}
public static String getXmpXml(InputStream is, String filename) throws ImageReadException, IOException {
return getXmpXml(is, filename, null);
}
public static String getXmpXml(InputStream is, String filename, Map params) throws ImageReadException, IOException {
return getXmpXml(new ByteSourceInputStream(is, filename), params);
}
public static String getXmpXml(byte[] bytes) throws ImageReadException, IOException {
return getXmpXml(bytes, null);
}
public static String getXmpXml(byte[] bytes, Map params) throws ImageReadException, IOException {
return getXmpXml(new ByteSourceArray(bytes), params);
}
public static String getXmpXml(File file) throws ImageReadException, IOException {
return getXmpXml(file, null);
}
public static String getXmpXml(File file, Map params) throws ImageReadException, IOException {
return getXmpXml(new ByteSourceFile(file), params);
}
public static String getXmpXml(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
return imageParser.getXmpXml(byteSource, params);
}
public static IImageMetadata getMetadata(byte[] bytes) throws ImageReadException, IOException {
return getMetadata(bytes, null);
}
public static IImageMetadata getMetadata(byte[] bytes, Map params) throws ImageReadException, IOException {
return getMetadata(new ByteSourceArray(bytes), params);
}
public static IImageMetadata getMetadata(InputStream is, String filename) throws ImageReadException, IOException {
return getMetadata(is, filename, null);
}
public static IImageMetadata getMetadata(InputStream is, String filename, Map params) throws ImageReadException, IOException {
return getMetadata(new ByteSourceInputStream(is, filename), params);
}
public static IImageMetadata getMetadata(File file) throws ImageReadException, IOException {
return getMetadata(file, null);
}
public static IImageMetadata getMetadata(File file, Map params) throws ImageReadException, IOException {
return getMetadata(new ByteSourceFile(file), params);
}
private static IImageMetadata getMetadata(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
return imageParser.getMetadata(byteSource, params);
}
public static String dumpImageFile(byte[] bytes) throws ImageReadException, IOException {
return dumpImageFile(new ByteSourceArray(bytes));
}
public static String dumpImageFile(File file) throws ImageReadException, IOException {
return dumpImageFile(new ByteSourceFile(file));
}
private static String dumpImageFile(ByteSource byteSource) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
return imageParser.dumpImageFile(byteSource);
}
public static FormatCompliance getFormatCompliance(byte[] bytes) throws ImageReadException, IOException {
return getFormatCompliance(new ByteSourceArray(bytes));
}
public static FormatCompliance getFormatCompliance(File file) throws ImageReadException, IOException {
return getFormatCompliance(new ByteSourceFile(file));
}
private static FormatCompliance getFormatCompliance(ByteSource byteSource) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
return imageParser.getFormatCompliance(byteSource);
}
public static ArrayList getAllBufferedImages(InputStream is, String filename) throws ImageReadException, IOException {
return getAllBufferedImages(new ByteSourceInputStream(is, filename));
}
public static ArrayList getAllBufferedImages(byte[] bytes) throws ImageReadException, IOException {
return getAllBufferedImages(new ByteSourceArray(bytes));
}
public static ArrayList getAllBufferedImages(File file) throws ImageReadException, IOException {
return getAllBufferedImages(new ByteSourceFile(file));
}
private static ArrayList getAllBufferedImages(ByteSource byteSource) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
return imageParser.getAllBufferedImages(byteSource);
}
public static BufferedImage getBufferedImage(InputStream is) throws ImageReadException, IOException {
return getBufferedImage(is, null);
}
public static BufferedImage getBufferedImage(InputStream is, Map params) throws ImageReadException, IOException {
String filename = null;
if (params != null && params.containsKey("FILENAME"))
filename = (String)params.get("FILENAME");
return getBufferedImage(new ByteSourceInputStream(is, filename), params);
}
public static BufferedImage getBufferedImage(byte[] bytes) throws ImageReadException, IOException {
return getBufferedImage(new ByteSourceArray(bytes), null);
}
public static BufferedImage getBufferedImage(byte[] bytes, Map params) throws ImageReadException, IOException {
return getBufferedImage(new ByteSourceArray(bytes), params);
}
public static BufferedImage getBufferedImage(File file) throws ImageReadException, IOException {
return getBufferedImage(new ByteSourceFile(file), null);
}
public static BufferedImage getBufferedImage(File file, Map params) throws ImageReadException, IOException {
return getBufferedImage(new ByteSourceFile(file), params);
}
private static BufferedImage getBufferedImage(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ImageParser imageParser = getImageParser(byteSource);
if (null == params)
params = new HashMap();
return imageParser.getBufferedImage(byteSource, params);
}
public static void writeImage(BufferedImage src, File file, ImageFormat format, Map params) throws ImageWriteException, IOException {
OutputStream os = null;
try {
os = new FileOutputStream(file);
os = new BufferedOutputStream(os);
writeImage(src, os, format, params);
} finally {
try {
if (os != null)
os.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
public static byte[] writeImageToBytes(BufferedImage src, ImageFormat format, Map params) throws ImageWriteException, IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
writeImage(src, os, format, params);
return os.toByteArray();
}
public static void writeImage(BufferedImage src, OutputStream os, ImageFormat format, Map params) throws ImageWriteException, IOException {
ImageParser[] imageParsers = ImageParser.getAllImageParsers();
if (params == null)
params = new HashMap();
params.put("FORMAT", format);
for (int i = 0; i < imageParsers.length; ) {
ImageParser imageParser = imageParsers[i];
if (!imageParser.canAcceptType(format)) {
i++;
continue;
}
imageParser.writeImage(src, os, params);
return;
}
throw new ImageWriteException("Unknown Format: " + format);
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan;
public interface SanselanConstants {
public static final String PARAM_KEY_VERBOSE = "VERBOSE";
public static final String PARAM_KEY_FILENAME = "FILENAME";
public static final String PARAM_KEY_FORMAT = "FORMAT";
public static final String PARAM_KEY_COMPRESSION = "COMPRESSION";
public static final String BUFFERED_IMAGE_FACTORY = "BUFFERED_IMAGE_FACTORY";
public static final String PARAM_KEY_READ_THUMBNAILS = "READ_THUMBNAILS";
public static final String PARAM_KEY_STRICT = "STRICT";
public static final String PARAM_KEY_XMP_XML = "XMP_XML";
}

View file

@ -0,0 +1,13 @@
package org.apache.sanselan;
public class SanselanException extends Exception {
static final long serialVersionUID = -1L;
public SanselanException(String s) {
super(s);
}
public SanselanException(String s, Exception e) {
super(s, e);
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorCIELCH {
public final double L;
public final double C;
public final double H;
public ColorCIELCH(double l, double C, double H) {
this.L = l;
this.C = C;
this.H = H;
}
public String toString() {
return "{L: " + this.L + ", C: " + this.C + ", H: " + this.H + "}";
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorCIELab {
public final double L;
public final double a;
public final double b;
public ColorCIELab(double l, double a, double b) {
this.L = l;
this.a = a;
this.b = b;
}
public final String toString() {
return "{L: " + this.L + ", a: " + this.a + ", b: " + this.b + "}";
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorCIELuv {
public final double L;
public final double u;
public final double v;
public ColorCIELuv(double l, double u, double v) {
this.L = l;
this.u = u;
this.v = v;
}
public String toString() {
return "{L: " + this.L + ", u: " + this.u + ", v: " + this.v + "}";
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorCMY {
public final double C;
public final double M;
public final double Y;
public ColorCMY(double C, double M, double Y) {
this.C = C;
this.M = M;
this.Y = Y;
}
public final String toString() {
return "{C: " + this.C + ", M: " + this.M + ", Y: " + this.Y + "}";
}
}

View file

@ -0,0 +1,22 @@
package org.apache.sanselan.color;
public final class ColorCMYK {
public final double C;
public final double M;
public final double Y;
public final double K;
public ColorCMYK(double C, double M, double Y, double K) {
this.C = C;
this.M = M;
this.Y = Y;
this.K = K;
}
public final String toString() {
return "{C: " + this.C + ", M: " + this.M + ", Y: " + this.Y + ", K: " + this.K + "}";
}
}

View file

@ -0,0 +1,640 @@
package org.apache.sanselan.color;
import org.apache.sanselan.util.Debug;
public abstract class ColorConversions {
private static final double ref_X = 95.047D;
private static final double ref_Y = 100.0D;
private static final double ref_Z = 108.883D;
public static final void main(String[] args) {
try {
for (int C = 0; C <= 256; C += 64) {
for (int M = 0; M <= 256; M += 64) {
for (int Y = 0; Y <= 256; Y += 64) {
for (int K = 0; K <= 256; K += 64) {
int rgb1 = convertCMYKtoRGB(Math.min(255, C), Math.min(255, M), Math.min(255, Y), Math.min(255, K));
int rgb2 = convertCMYKtoRGB_old(Math.min(255, C), Math.min(255, M), Math.min(255, Y), Math.min(255, K));
if (rgb1 != rgb2) {
Debug.debug();
Debug.debug("C", C);
Debug.debug("M", M);
Debug.debug("Y", Y);
Debug.debug("K", K);
Debug.debug("rgb1", rgb1 + " (" + Integer.toHexString(rgb1) + ")");
Debug.debug("rgb2", rgb2 + " (" + Integer.toHexString(rgb2) + ")");
}
}
}
}
}
int[] sample_rgbs = { -1, -16777216, -65536, -16711936, -16776961, -65281, -983296, -16711681, 0, -8421505 };
for (int i = 0; i < sample_rgbs.length; i++) {
int rgb = sample_rgbs[i];
ColorXYZ xyz = convertRGBtoXYZ(rgb);
int xyz_rgb = convertXYZtoRGB(xyz);
Debug.debug();
Debug.debug("rgb", rgb + " (" + Integer.toHexString(rgb) + ")");
Debug.debug("xyz", xyz);
Debug.debug("xyz_rgb", xyz_rgb + " (" + Integer.toHexString(xyz_rgb) + ")");
if ((0xFFFFFF & xyz_rgb) != (0xFFFFFF & rgb))
Debug.debug("!!!!!!!!!!!!!!!!!!!!!!!");
ColorCIELab cielab = convertXYZtoCIELab(xyz);
ColorXYZ cielab_xyz = convertCIELabtoXYZ(cielab);
int cielab_xyz_rgb = convertXYZtoRGB(cielab_xyz);
Debug.debug("cielab", cielab);
Debug.debug("cielab_xyz", cielab_xyz);
Debug.debug("cielab_xyz_rgb", cielab_xyz_rgb + " (" + Integer.toHexString(cielab_xyz_rgb) + ")");
if ((0xFFFFFF & cielab_xyz_rgb) != (0xFFFFFF & rgb))
Debug.debug("!!!!!!!!!!!!!!!!!!!!!!!");
ColorHunterLab hunterlab = convertXYZtoHunterLab(xyz);
ColorXYZ hunterlab_xyz = convertHunterLabtoXYZ(hunterlab);
int hunterlab_xyz_rgb = convertXYZtoRGB(hunterlab_xyz);
Debug.debug("hunterlab", hunterlab);
Debug.debug("hunterlab_xyz", hunterlab_xyz);
Debug.debug("hunterlab_xyz_rgb", hunterlab_xyz_rgb + " (" + Integer.toHexString(hunterlab_xyz_rgb) + ")");
if ((0xFFFFFF & hunterlab_xyz_rgb) != (0xFFFFFF & rgb))
Debug.debug("!!!!!!!!!!!!!!!!!!!!!!!");
ColorCMY cmy = convertRGBtoCMY(rgb);
ColorCMYK cmyk = convertCMYtoCMYK(cmy);
ColorCMY cmyk_cmy = convertCMYKtoCMY(cmyk);
int cmyk_cmy_rgb = convertCMYtoRGB(cmyk_cmy);
Debug.debug("cmy", cmy);
Debug.debug("cmyk", cmyk);
Debug.debug("cmyk_cmy", cmyk_cmy);
Debug.debug("cmyk_cmy_rgb", cmyk_cmy_rgb + " (" + Integer.toHexString(cmyk_cmy_rgb) + ")");
if ((0xFFFFFF & cmyk_cmy_rgb) != (0xFFFFFF & rgb))
Debug.debug("!!!!!!!!!!!!!!!!!!!!!!!");
ColorHSL hsl = convertRGBtoHSL(rgb);
int hsl_rgb = convertHSLtoRGB(hsl);
Debug.debug("hsl", hsl);
Debug.debug("hsl_rgb", hsl_rgb + " (" + Integer.toHexString(hsl_rgb) + ")");
if ((0xFFFFFF & hsl_rgb) != (0xFFFFFF & rgb))
Debug.debug("!!!!!!!!!!!!!!!!!!!!!!!");
ColorHSV hsv = convertRGBtoHSV(rgb);
int hsv_rgb = convertHSVtoRGB(hsv);
Debug.debug("hsv", hsv);
Debug.debug("hsv_rgb", hsv_rgb + " (" + Integer.toHexString(hsv_rgb) + ")");
if ((0xFFFFFF & hsv_rgb) != (0xFFFFFF & rgb))
Debug.debug("!!!!!!!!!!!!!!!!!!!!!!!");
ColorCIELCH cielch = convertCIELabtoCIELCH(cielab);
ColorCIELab cielch_cielab = convertCIELCHtoCIELab(cielch);
Debug.debug("cielch", cielch);
Debug.debug("cielch_cielab", cielch_cielab);
ColorCIELuv cieluv = convertXYZtoCIELuv(xyz);
ColorXYZ cieluv_xyz = convertCIELuvtoXYZ(cieluv);
Debug.debug("cieluv", cieluv);
Debug.debug("cieluv_xyz", cieluv_xyz);
}
} catch (Throwable e) {
Debug.debug(e);
}
}
public static final ColorCIELab convertXYZtoCIELab(ColorXYZ xyz) {
return convertXYZtoCIELab(xyz.X, xyz.Y, xyz.Z);
}
public static final ColorCIELab convertXYZtoCIELab(double X, double Y, double Z) {
double var_X = X / 95.047D;
double var_Y = Y / 100.0D;
double var_Z = Z / 108.883D;
if (var_X > 0.008856D) {
var_X = Math.pow(var_X, 0.3333333333333333D);
} else {
var_X = 7.787D * var_X + 0.13793103448275862D;
}
if (var_Y > 0.008856D) {
var_Y = Math.pow(var_Y, 0.3333333333333333D);
} else {
var_Y = 7.787D * var_Y + 0.13793103448275862D;
}
if (var_Z > 0.008856D) {
var_Z = Math.pow(var_Z, 0.3333333333333333D);
} else {
var_Z = 7.787D * var_Z + 0.13793103448275862D;
}
double L = 116.0D * var_Y - 16.0D;
double a = 500.0D * (var_X - var_Y);
double b = 200.0D * (var_Y - var_Z);
return new ColorCIELab(L, a, b);
}
public static final ColorXYZ convertCIELabtoXYZ(ColorCIELab cielab) {
return convertCIELabtoXYZ(cielab.L, cielab.a, cielab.b);
}
public static final ColorXYZ convertCIELabtoXYZ(double L, double a, double b) {
double var_Y = (L + 16.0D) / 116.0D;
double var_X = a / 500.0D + var_Y;
double var_Z = var_Y - b / 200.0D;
if (Math.pow(var_Y, 3.0D) > 0.008856D) {
var_Y = Math.pow(var_Y, 3.0D);
} else {
var_Y = (var_Y - 0.13793103448275862D) / 7.787D;
}
if (Math.pow(var_X, 3.0D) > 0.008856D) {
var_X = Math.pow(var_X, 3.0D);
} else {
var_X = (var_X - 0.13793103448275862D) / 7.787D;
}
if (Math.pow(var_Z, 3.0D) > 0.008856D) {
var_Z = Math.pow(var_Z, 3.0D);
} else {
var_Z = (var_Z - 0.13793103448275862D) / 7.787D;
}
double X = 95.047D * var_X;
double Y = 100.0D * var_Y;
double Z = 108.883D * var_Z;
return new ColorXYZ(X, Y, Z);
}
public static final ColorHunterLab convertXYZtoHunterLab(ColorXYZ xyz) {
return convertXYZtoHunterLab(xyz.X, xyz.Y, xyz.Z);
}
public static final ColorHunterLab convertXYZtoHunterLab(double X, double Y, double Z) {
double L = 10.0D * Math.sqrt(Y);
double a = 17.5D * (1.02D * X - Y) / Math.sqrt(Y);
double b = 7.0D * (Y - 0.847D * Z) / Math.sqrt(Y);
return new ColorHunterLab(L, a, b);
}
public static final ColorXYZ convertHunterLabtoXYZ(ColorHunterLab cielab) {
return convertHunterLabtoXYZ(cielab.L, cielab.a, cielab.b);
}
public static final ColorXYZ convertHunterLabtoXYZ(double L, double a, double b) {
double var_Y = L / 10.0D;
double var_X = a / 17.5D * L / 10.0D;
double var_Z = b / 7.0D * L / 10.0D;
double Y = Math.pow(var_Y, 2.0D);
double X = (var_X + Y) / 1.02D;
double Z = -(var_Z - Y) / 0.847D;
return new ColorXYZ(X, Y, Z);
}
public static final int convertXYZtoRGB(ColorXYZ xyz) {
return convertXYZtoRGB(xyz.X, xyz.Y, xyz.Z);
}
public static final int convertXYZtoRGB(double X, double Y, double Z) {
double var_X = X / 100.0D;
double var_Y = Y / 100.0D;
double var_Z = Z / 100.0D;
double var_R = var_X * 3.2406D + var_Y * -1.5372D + var_Z * -0.4986D;
double var_G = var_X * -0.9689D + var_Y * 1.8758D + var_Z * 0.0415D;
double var_B = var_X * 0.0557D + var_Y * -0.204D + var_Z * 1.057D;
if (var_R > 0.0031308D) {
var_R = 1.055D * Math.pow(var_R, 0.4166666666666667D) - 0.055D;
} else {
var_R = 12.92D * var_R;
}
if (var_G > 0.0031308D) {
var_G = 1.055D * Math.pow(var_G, 0.4166666666666667D) - 0.055D;
} else {
var_G = 12.92D * var_G;
}
if (var_B > 0.0031308D) {
var_B = 1.055D * Math.pow(var_B, 0.4166666666666667D) - 0.055D;
} else {
var_B = 12.92D * var_B;
}
double R = var_R * 255.0D;
double G = var_G * 255.0D;
double B = var_B * 255.0D;
return convertRGBtoRGB(R, G, B);
}
public static final ColorXYZ convertRGBtoXYZ(int rgb) {
int r = 0xFF & rgb >> 16;
int g = 0xFF & rgb >> 8;
int b = 0xFF & rgb >> 0;
double var_R = (double)r / 255.0D;
double var_G = (double)g / 255.0D;
double var_B = (double)b / 255.0D;
if (var_R > 0.04045D) {
var_R = Math.pow((var_R + 0.055D) / 1.055D, 2.4D);
} else {
var_R /= 12.92D;
}
if (var_G > 0.04045D) {
var_G = Math.pow((var_G + 0.055D) / 1.055D, 2.4D);
} else {
var_G /= 12.92D;
}
if (var_B > 0.04045D) {
var_B = Math.pow((var_B + 0.055D) / 1.055D, 2.4D);
} else {
var_B /= 12.92D;
}
var_R *= 100.0D;
var_G *= 100.0D;
var_B *= 100.0D;
double X = var_R * 0.4124D + var_G * 0.3576D + var_B * 0.1805D;
double Y = var_R * 0.2126D + var_G * 0.7152D + var_B * 0.0722D;
double Z = var_R * 0.0193D + var_G * 0.1192D + var_B * 0.9505D;
return new ColorXYZ(X, Y, Z);
}
public static final ColorCMY convertRGBtoCMY(int rgb) {
int R = 0xFF & rgb >> 16;
int G = 0xFF & rgb >> 8;
int B = 0xFF & rgb >> 0;
double C = 1.0D - (double)R / 255.0D;
double M = 1.0D - (double)G / 255.0D;
double Y = 1.0D - (double)B / 255.0D;
return new ColorCMY(C, M, Y);
}
public static final int convertCMYtoRGB(ColorCMY cmy) {
double R = (1.0D - cmy.C) * 255.0D;
double G = (1.0D - cmy.M) * 255.0D;
double B = (1.0D - cmy.Y) * 255.0D;
return convertRGBtoRGB(R, G, B);
}
public static final ColorCMYK convertCMYtoCMYK(ColorCMY cmy) {
double C = cmy.C;
double M = cmy.M;
double Y = cmy.Y;
double var_K = 1.0D;
if (C < var_K)
var_K = C;
if (M < var_K)
var_K = M;
if (Y < var_K)
var_K = Y;
if (var_K == 1.0D) {
C = 0.0D;
M = 0.0D;
Y = 0.0D;
} else {
C = (C - var_K) / (1.0D - var_K);
M = (M - var_K) / (1.0D - var_K);
Y = (Y - var_K) / (1.0D - var_K);
}
return new ColorCMYK(C, M, Y, var_K);
}
public static final ColorCMY convertCMYKtoCMY(ColorCMYK cmyk) {
return convertCMYKtoCMY(cmyk.C, cmyk.M, cmyk.Y, cmyk.K);
}
public static final ColorCMY convertCMYKtoCMY(double C, double M, double Y, double K) {
C = C * (1.0D - K) + K;
M = M * (1.0D - K) + K;
Y = Y * (1.0D - K) + K;
return new ColorCMY(C, M, Y);
}
public static final int convertCMYKtoRGB(int c, int m, int y, int k) {
double C = (double)c / 255.0D;
double M = (double)m / 255.0D;
double Y = (double)y / 255.0D;
double K = (double)k / 255.0D;
return convertCMYtoRGB(convertCMYKtoCMY(C, M, Y, K));
}
public static final ColorHSL convertRGBtoHSL(int rgb) {
double H, S;
int R = 0xFF & rgb >> 16;
int G = 0xFF & rgb >> 8;
int B = 0xFF & rgb >> 0;
double var_R = (double)R / 255.0D;
double var_G = (double)G / 255.0D;
double var_B = (double)B / 255.0D;
double var_Min = Math.min(var_R, Math.min(var_G, var_B));
double var_Max = Math.max(var_R, Math.max(var_G, var_B));
double del_Max = var_Max - var_Min;
double L = (var_Max + var_Min) / 2.0D;
if (del_Max == 0.0D) {
H = 0.0D;
S = 0.0D;
} else {
if (L < 0.5D) {
S = del_Max / (var_Max + var_Min);
} else {
S = del_Max / (2.0D - var_Max - var_Min);
}
double del_R = ((var_Max - var_R) / 6.0D + del_Max / 2.0D) / del_Max;
double del_G = ((var_Max - var_G) / 6.0D + del_Max / 2.0D) / del_Max;
double del_B = ((var_Max - var_B) / 6.0D + del_Max / 2.0D) / del_Max;
if (var_R == var_Max) {
H = del_B - del_G;
} else if (var_G == var_Max) {
H = 0.3333333333333333D + del_R - del_B;
} else if (var_B == var_Max) {
H = 0.6666666666666666D + del_G - del_R;
} else {
Debug.debug("uh oh");
H = 0.0D;
}
if (H < 0.0D)
H++;
if (H > 1.0D)
H--;
}
return new ColorHSL(H, S, L);
}
public static int convertHSLtoRGB(ColorHSL hsl) {
return convertHSLtoRGB(hsl.H, hsl.S, hsl.L);
}
public static int convertHSLtoRGB(double H, double S, double L) {
double R, G, B;
if (S == 0.0D) {
R = L * 255.0D;
G = L * 255.0D;
B = L * 255.0D;
} else {
double var_2;
if (L < 0.5D) {
var_2 = L * (1.0D + S);
} else {
var_2 = L + S - S * L;
}
double var_1 = 2.0D * L - var_2;
R = 255.0D * convertHuetoRGB(var_1, var_2, H + 0.3333333333333333D);
G = 255.0D * convertHuetoRGB(var_1, var_2, H);
B = 255.0D * convertHuetoRGB(var_1, var_2, H - 0.3333333333333333D);
}
return convertRGBtoRGB(R, G, B);
}
private static double convertHuetoRGB(double v1, double v2, double vH) {
if (vH < 0.0D)
vH++;
if (vH > 1.0D)
vH--;
if (6.0D * vH < 1.0D)
return v1 + (v2 - v1) * 6.0D * vH;
if (2.0D * vH < 1.0D)
return v2;
if (3.0D * vH < 2.0D)
return v1 + (v2 - v1) * (0.6666666666666666D - vH) * 6.0D;
return v1;
}
public static final ColorHSV convertRGBtoHSV(int rgb) {
double H, S;
int R = 0xFF & rgb >> 16;
int G = 0xFF & rgb >> 8;
int B = 0xFF & rgb >> 0;
double var_R = (double)R / 255.0D;
double var_G = (double)G / 255.0D;
double var_B = (double)B / 255.0D;
double var_Min = Math.min(var_R, Math.min(var_G, var_B));
double var_Max = Math.max(var_R, Math.max(var_G, var_B));
double del_Max = var_Max - var_Min;
double V = var_Max;
if (del_Max == 0.0D) {
H = 0.0D;
S = 0.0D;
} else {
S = del_Max / var_Max;
double del_R = ((var_Max - var_R) / 6.0D + del_Max / 2.0D) / del_Max;
double del_G = ((var_Max - var_G) / 6.0D + del_Max / 2.0D) / del_Max;
double del_B = ((var_Max - var_B) / 6.0D + del_Max / 2.0D) / del_Max;
if (var_R == var_Max) {
H = del_B - del_G;
} else if (var_G == var_Max) {
H = 0.3333333333333333D + del_R - del_B;
} else if (var_B == var_Max) {
H = 0.6666666666666666D + del_G - del_R;
} else {
Debug.debug("uh oh");
H = 0.0D;
}
if (H < 0.0D)
H++;
if (H > 1.0D)
H--;
}
return new ColorHSV(H, S, V);
}
public static int convertHSVtoRGB(ColorHSV HSV) {
return convertHSVtoRGB(HSV.H, HSV.S, HSV.V);
}
public static int convertHSVtoRGB(double H, double S, double V) {
double R, G, B;
if (S == 0.0D) {
R = V * 255.0D;
G = V * 255.0D;
B = V * 255.0D;
} else {
double var_r, var_g, var_b;
double var_h = H * 6.0D;
if (var_h == 6.0D)
var_h = 0.0D;
double var_i = Math.floor(var_h);
double var_1 = V * (1.0D - S);
double var_2 = V * (1.0D - S * (var_h - var_i));
double var_3 = V * (1.0D - S * (1.0D - (var_h - var_i)));
if (var_i == 0.0D) {
var_r = V;
var_g = var_3;
var_b = var_1;
} else if (var_i == 1.0D) {
var_r = var_2;
var_g = V;
var_b = var_1;
} else if (var_i == 2.0D) {
var_r = var_1;
var_g = V;
var_b = var_3;
} else if (var_i == 3.0D) {
var_r = var_1;
var_g = var_2;
var_b = V;
} else if (var_i == 4.0D) {
var_r = var_3;
var_g = var_1;
var_b = V;
} else {
var_r = V;
var_g = var_1;
var_b = var_2;
}
R = var_r * 255.0D;
G = var_g * 255.0D;
B = var_b * 255.0D;
}
return convertRGBtoRGB(R, G, B);
}
public static final int convertCMYKtoRGB_old(int sc, int sm, int sy, int sk) {
int red = 255 - (sc + sk);
int green = 255 - (sm + sk);
int blue = 255 - (sy + sk);
return convertRGBtoRGB(red, green, blue);
}
private static double cube(double f) {
return f * f * f;
}
private static double square(double f) {
return f * f;
}
public static final int convertCIELabtoARGBTest(int cieL, int cieA, int cieB) {
double var_Y = ((double)cieL * 100.0D / 255.0D + 16.0D) / 116.0D;
double var_X = (double)cieA / 500.0D + var_Y;
double var_Z = var_Y - (double)cieB / 200.0D;
double var_x_cube = cube(var_X);
double var_y_cube = cube(var_Y);
double var_z_cube = cube(var_Z);
if (var_y_cube > 0.008856D) {
var_Y = var_y_cube;
} else {
var_Y = (var_Y - 0.13793103448275862D) / 7.787D;
}
if (var_x_cube > 0.008856D) {
var_X = var_x_cube;
} else {
var_X = (var_X - 0.13793103448275862D) / 7.787D;
}
if (var_z_cube > 0.008856D) {
var_Z = var_z_cube;
} else {
var_Z = (var_Z - 0.13793103448275862D) / 7.787D;
}
double X = 95.047D * var_X;
double Y = 100.0D * var_Y;
double Z = 108.883D * var_Z;
double d1 = X / 100.0D;
double d2 = Y / 100.0D;
double d3 = Z / 100.0D;
double var_R = d1 * 3.2406D + d2 * -1.5372D + d3 * -0.4986D;
double var_G = d1 * -0.9689D + d2 * 1.8758D + d3 * 0.0415D;
double var_B = d1 * 0.0557D + d2 * -0.204D + d3 * 1.057D;
if (var_R > 0.0031308D) {
var_R = 1.055D * Math.pow(var_R, 0.4166666666666667D) - 0.055D;
} else {
var_R = 12.92D * var_R;
}
if (var_G > 0.0031308D) {
var_G = 1.055D * Math.pow(var_G, 0.4166666666666667D) - 0.055D;
} else {
var_G = 12.92D * var_G;
}
if (var_B > 0.0031308D) {
var_B = 1.055D * Math.pow(var_B, 0.4166666666666667D) - 0.055D;
} else {
var_B = 12.92D * var_B;
}
double R = var_R * 255.0D;
double G = var_G * 255.0D;
double B = var_B * 255.0D;
return convertRGBtoRGB(R, G, B);
}
private static final int convertRGBtoRGB(double R, double G, double B) {
int red = (int)Math.round(R);
int green = (int)Math.round(G);
int blue = (int)Math.round(B);
red = Math.min(255, Math.max(0, red));
green = Math.min(255, Math.max(0, green));
blue = Math.min(255, Math.max(0, blue));
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
return rgb;
}
private static final int convertRGBtoRGB(int red, int green, int blue) {
red = Math.min(255, Math.max(0, red));
green = Math.min(255, Math.max(0, green));
blue = Math.min(255, Math.max(0, blue));
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
return rgb;
}
public static ColorCIELCH convertCIELabtoCIELCH(ColorCIELab cielab) {
return convertCIELabtoCIELCH(cielab.L, cielab.a, cielab.b);
}
public static ColorCIELCH convertCIELabtoCIELCH(double L, double a, double b) {
double var_H = Math.atan2(b, a);
if (var_H > 0.0D) {
var_H = var_H / Math.PI * 180.0D;
} else {
var_H = 360.0D - radian_2_degree(Math.abs(var_H));
}
double C = Math.sqrt(square(a) + square(b));
double H = var_H;
return new ColorCIELCH(L, C, H);
}
public static ColorCIELab convertCIELCHtoCIELab(ColorCIELCH cielch) {
return convertCIELCHtoCIELab(cielch.L, cielch.C, cielch.H);
}
public static ColorCIELab convertCIELCHtoCIELab(double L, double C, double H) {
double a = Math.cos(degree_2_radian(H)) * C;
double b = Math.sin(degree_2_radian(H)) * C;
return new ColorCIELab(L, a, b);
}
public static double degree_2_radian(double degree) {
return degree * Math.PI / 180.0D;
}
public static double radian_2_degree(double radian) {
return radian * 180.0D / Math.PI;
}
public static ColorCIELuv convertXYZtoCIELuv(ColorXYZ xyz) {
return convertXYZtoCIELuv(xyz.X, xyz.Y, xyz.Z);
}
public static ColorCIELuv convertXYZtoCIELuv(double X, double Y, double Z) {
double var_U = 4.0D * X / (X + 15.0D * Y + 3.0D * Z);
double var_V = 9.0D * Y / (X + 15.0D * Y + 3.0D * Z);
double var_Y = Y / 100.0D;
if (var_Y > 0.008856D) {
var_Y = Math.pow(var_Y, 0.3333333333333333D);
} else {
var_Y = 7.787D * var_Y + 0.13793103448275862D;
}
double ref_X = 95.047D;
double ref_Y = 100.0D;
double ref_Z = 108.883D;
double ref_U = 4.0D * ref_X / (ref_X + 15.0D * ref_Y + 3.0D * ref_Z);
double ref_V = 9.0D * ref_Y / (ref_X + 15.0D * ref_Y + 3.0D * ref_Z);
double L = 116.0D * var_Y - 16.0D;
double u = 13.0D * L * (var_U - ref_U);
double v = 13.0D * L * (var_V - ref_V);
return new ColorCIELuv(L, u, v);
}
public static ColorXYZ convertCIELuvtoXYZ(ColorCIELuv cielch) {
return convertCIELuvtoXYZ(cielch.L, cielch.u, cielch.v);
}
public static ColorXYZ convertCIELuvtoXYZ(double L, double u, double v) {
double var_Y = (L + 16.0D) / 116.0D;
if (Math.pow(var_Y, 3.0D) > 0.008856D) {
var_Y = Math.pow(var_Y, 3.0D);
} else {
var_Y = (var_Y - 0.0D) / 7.787D;
}
double ref_X = 95.047D;
double ref_Y = 100.0D;
double ref_Z = 108.883D;
double ref_U = 4.0D * ref_X / (ref_X + 15.0D * ref_Y + 3.0D * ref_Z);
double ref_V = 9.0D * ref_Y / (ref_X + 15.0D * ref_Y + 3.0D * ref_Z);
double var_U = u / (13.0D * L) + ref_U;
double var_V = v / (13.0D * L) + ref_V;
double Y = var_Y * 100.0D;
double X = -(9.0D * Y * var_U) / ((var_U - 4.0D) * var_V - var_U * var_V);
double Z = (9.0D * Y - 15.0D * var_V * Y - var_V * X) / (3.0D * var_V);
return new ColorXYZ(X, Y, Z);
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorHSL {
public final double H;
public final double S;
public final double L;
public ColorHSL(double h, double s, double v) {
this.H = h;
this.S = s;
this.L = v;
}
public final String toString() {
return "{H: " + this.H + ", S: " + this.S + ", L: " + this.L + "}";
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorHSV {
public final double H;
public final double S;
public final double V;
public ColorHSV(double h, double s, double v) {
this.H = h;
this.S = s;
this.V = v;
}
public final String toString() {
return "{H: " + this.H + ", S: " + this.S + ", V: " + this.V + "}";
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorHunterLab {
public final double L;
public final double a;
public final double b;
public ColorHunterLab(double l, double a, double b) {
this.L = l;
this.a = a;
this.b = b;
}
public final String toString() {
return "{L: " + this.L + ", a: " + this.a + ", b: " + this.b + "}";
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.color;
public final class ColorXYZ {
public final double X;
public final double Y;
public final double Z;
public ColorXYZ(double x, double y, double z) {
this.X = x;
this.Y = y;
this.Z = z;
}
public final String toString() {
return "{X: " + this.X + ", Y: " + this.Y + ", Z: " + this.Z + "}";
}
}

View file

@ -0,0 +1,21 @@
package org.apache.sanselan.common;
public interface BinaryConstants {
public static final int BYTE_ORDER_INTEL = 73;
public static final int BYTE_ORDER_LEAST_SIGNIFICANT_BYTE = 73;
public static final int BYTE_ORDER_LSB = 73;
public static final int BYTE_ORDER_LITTLE_ENDIAN = 73;
public static final int BYTE_ORDER_MOTOROLA = 77;
public static final int BYTE_ORDER_MOST_SIGNIFICANT_BYTE = 77;
public static final int BYTE_ORDER_MSB = 77;
public static final int BYTE_ORDER_NETWORK = 77;
public static final int BYTE_ORDER_BIG_ENDIAN = 77;
}

View file

@ -0,0 +1,665 @@
package org.apache.sanselan.common;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
public class BinaryFileFunctions implements BinaryConstants {
protected boolean debug = false;
public final void setDebug(boolean b) {
this.debug = b;
}
public final boolean getDebug() {
return this.debug;
}
protected final void readRandomBytes(InputStream is) throws ImageReadException, IOException {
for (int counter = 0; counter < 100; counter++)
readByte("" + counter, is, "Random Data");
}
public final void debugNumber(String msg, int data) {
debugNumber(msg, data, 1);
}
public final void debugNumber(String msg, int data, int bytes) {
PrintWriter pw = new PrintWriter(System.out);
debugNumber(pw, msg, data, bytes);
pw.flush();
}
public final void debugNumber(PrintWriter pw, String msg, int data) {
debugNumber(pw, msg, data, 1);
}
public final void debugNumber(PrintWriter pw, String msg, int data, int bytes) {
pw.print(msg + ": " + data + " (");
int byteData = data;
for (int i = 0; i < bytes; i++) {
if (i > 0)
pw.print(",");
int singleByte = 0xFF & byteData;
pw.print((char)singleByte + " [" + singleByte + "]");
byteData >>= 8;
}
pw.println(") [0x" + Integer.toHexString(data) + ", " + Integer.toBinaryString(data) + "]");
pw.flush();
}
public final boolean startsWith(byte[] haystack, byte[] needle) {
if (needle == null)
return false;
if (haystack == null)
return false;
if (needle.length > haystack.length)
return false;
for (int i = 0; i < needle.length; i++) {
if (needle[i] != haystack[i])
return false;
}
return true;
}
public final byte[] readBytes(InputStream is, int count) throws ImageReadException, IOException {
byte[] result = new byte[count];
for (int i = 0; i < count; i++) {
int data = is.read();
result[i] = (byte)data;
}
return result;
}
public final void readAndVerifyBytes(InputStream is, byte[] expected, String exception) throws ImageReadException, IOException {
for (int i = 0; i < expected.length; i++) {
int data = is.read();
byte b = (byte)(0xFF & data);
if (data < 0)
throw new ImageReadException("Unexpected EOF.");
if (b != expected[i])
throw new ImageReadException(exception);
}
}
protected final void readAndVerifyBytes(String name, InputStream is, byte[] expected, String exception) throws ImageReadException, IOException {
byte[] bytes = readByteArray(name, expected.length, is, exception);
for (int i = 0; i < expected.length; i++) {
if (bytes[i] != expected[i])
throw new ImageReadException(exception);
}
}
public final void skipBytes(InputStream is, int length, String exception) throws IOException {
long total = 0L;
while ((long)length != total) {
long skipped = is.skip((long)length - total);
if (skipped < 1L)
throw new IOException(exception + " (" + skipped + ")");
total += skipped;
}
}
protected final void scanForByte(InputStream is, byte value) throws IOException {
int count = 0;
for (int i = 0; count < 3; i++) {
int b = is.read();
if (b < 0)
return;
if ((0xFF & b) == value) {
System.out.println("\t" + i + ": match.");
count++;
}
}
}
public final byte readByte(String name, InputStream is, String exception) throws ImageReadException, IOException {
int result = is.read();
if (result < 0) {
System.out.println(name + ": " + result);
throw new IOException(exception);
}
if (this.debug)
debugNumber(name, result);
return (byte)(0xFF & result);
}
protected final RationalNumber[] convertByteArrayToRationalArray(String name, byte[] bytes, int start, int length, int byteOrder) {
int expectedLength = start + length * 8;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
RationalNumber[] result = new RationalNumber[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToRational(name, bytes, start + i * 8, byteOrder);
return result;
}
protected final RationalNumber convertByteArrayToRational(String name, byte[] bytes, int byteOrder) {
return convertByteArrayToRational(name, bytes, 0, byteOrder);
}
protected final RationalNumber convertByteArrayToRational(String name, byte[] bytes, int start, int byteOrder) {
int numerator = convertByteArrayToInt(name, bytes, start + 0, byteOrder);
int divisor = convertByteArrayToInt(name, bytes, start + 4, byteOrder);
return new RationalNumber(numerator, divisor);
}
protected final int convertByteArrayToInt(String name, byte[] bytes, int byteOrder) {
return convertByteArrayToInt(name, bytes, 0, byteOrder);
}
protected final int convertByteArrayToInt(String name, byte[] bytes, int start, int byteOrder) {
int result;
byte byte0 = bytes[start + 0];
byte byte1 = bytes[start + 1];
byte byte2 = bytes[start + 2];
byte byte3 = bytes[start + 3];
if (byteOrder == 77) {
result = (0xFF & byte0) << 24 | (0xFF & byte1) << 16 | (0xFF & byte2) << 8 | (0xFF & byte3) << 0;
} else {
result = (0xFF & byte3) << 24 | (0xFF & byte2) << 16 | (0xFF & byte1) << 8 | (0xFF & byte0) << 0;
}
if (this.debug)
debugNumber(name, result, 4);
return result;
}
protected final int[] convertByteArrayToIntArray(String name, byte[] bytes, int start, int length, int byteOrder) {
int expectedLength = start + length * 4;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
int[] result = new int[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToInt(name, bytes, start + i * 4, byteOrder);
return result;
}
protected final void writeIntInToByteArray(int value, byte[] bytes, int start, int byteOrder) {
if (byteOrder == 77) {
bytes[start + 0] = (byte)(value >> 24);
bytes[start + 1] = (byte)(value >> 16);
bytes[start + 2] = (byte)(value >> 8);
bytes[start + 3] = (byte)(value >> 0);
} else {
bytes[start + 3] = (byte)(value >> 24);
bytes[start + 2] = (byte)(value >> 16);
bytes[start + 1] = (byte)(value >> 8);
bytes[start + 0] = (byte)(value >> 0);
}
}
protected static final byte[] int2ToByteArray(int value, int byteOrder) {
if (byteOrder == 77)
return new byte[] { (byte)(value >> 8), (byte)(value >> 0) };
return new byte[] { (byte)(value >> 0), (byte)(value >> 8) };
}
protected final byte[] convertIntArrayToByteArray(int[] values, int byteOrder) {
byte[] result = new byte[values.length * 4];
for (int i = 0; i < values.length; i++)
writeIntInToByteArray(values[i], result, i * 4, byteOrder);
return result;
}
protected final byte[] convertShortArrayToByteArray(int[] values, int byteOrder) {
byte[] result = new byte[values.length * 2];
for (int i = 0; i < values.length; i++) {
int value = values[i];
if (byteOrder == 77) {
result[i * 2 + 0] = (byte)(value >> 8);
result[i * 2 + 1] = (byte)(value >> 0);
} else {
result[i * 2 + 1] = (byte)(value >> 8);
result[i * 2 + 0] = (byte)(value >> 0);
}
}
return result;
}
protected final byte[] convertShortToByteArray(int value, int byteOrder) {
byte[] result = new byte[2];
if (byteOrder == 77) {
result[0] = (byte)(value >> 8);
result[1] = (byte)(value >> 0);
} else {
result[1] = (byte)(value >> 8);
result[0] = (byte)(value >> 0);
}
return result;
}
protected final byte[] convertIntArrayToRationalArray(int[] numerators, int[] denominators, int byteOrder) throws ImageWriteException {
if (numerators.length != denominators.length)
throw new ImageWriteException("numerators.length (" + numerators.length + " != denominators.length (" + denominators.length + ")");
byte[] result = new byte[numerators.length * 8];
for (int i = 0; i < numerators.length; i++) {
writeIntInToByteArray(numerators[i], result, i * 8, byteOrder);
writeIntInToByteArray(denominators[i], result, i * 8 + 4, byteOrder);
}
return result;
}
protected final byte[] convertRationalArrayToByteArray(RationalNumber[] numbers, int byteOrder) throws ImageWriteException {
byte[] result = new byte[numbers.length * 8];
for (int i = 0; i < numbers.length; i++) {
writeIntInToByteArray((numbers[i]).numerator, result, i * 8, byteOrder);
writeIntInToByteArray((numbers[i]).divisor, result, i * 8 + 4, byteOrder);
}
return result;
}
protected final byte[] convertRationalToByteArray(RationalNumber number, int byteOrder) throws ImageWriteException {
byte[] result = new byte[8];
writeIntInToByteArray(number.numerator, result, 0, byteOrder);
writeIntInToByteArray(number.divisor, result, 4, byteOrder);
return result;
}
protected final int convertByteArrayToShort(String name, byte[] bytes, int byteOrder) throws ImageReadException {
return convertByteArrayToShort(name, 0, bytes, byteOrder);
}
protected final int convertByteArrayToShort(String name, int index, byte[] bytes, int byteOrder) throws ImageReadException {
int result;
if (index + 1 >= bytes.length)
throw new ImageReadException("Index out of bounds. Array size: " + bytes.length + ", index: " + index);
int byte0 = 0xFF & bytes[index + 0];
int byte1 = 0xFF & bytes[index + 1];
if (byteOrder == 77) {
result = byte0 << 8 | byte1;
} else {
result = byte1 << 8 | byte0;
}
if (this.debug)
debugNumber(name, result, 2);
return result;
}
protected final int[] convertByteArrayToShortArray(String name, byte[] bytes, int start, int length, int byteOrder) throws ImageReadException {
int expectedLength = start + length * 2;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
int[] result = new int[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToShort(name, start + i * 2, bytes, byteOrder);
return result;
}
public final byte[] readByteArray(String name, int length, InputStream is) throws IOException {
String exception = name + " could not be read.";
return readByteArray(name, length, is, exception);
}
public final byte[] readByteArray(String name, int length, InputStream is, String exception) throws IOException {
byte[] result = new byte[length];
int read = 0;
while (read < length) {
int count = is.read(result, read, length - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
if (this.debug)
for (int i = 0; i < length && i < 50; i++)
debugNumber(name + " (" + i + ")", 0xFF & result[i]);
return result;
}
public final void debugByteArray(String name, byte[] bytes) {
System.out.println(name + ": " + bytes.length);
for (int i = 0; i < bytes.length && i < 50; i++)
debugNumber("\t (" + i + ")", 0xFF & bytes[i]);
}
protected final void debugNumberArray(String name, int[] numbers, int length) {
System.out.println(name + ": " + numbers.length);
for (int i = 0; i < numbers.length && i < 50; i++)
debugNumber(name + " (" + i + ")", numbers[i], length);
}
public final byte[] readBytearray(String name, byte[] bytes, int start, int count) throws ImageReadException {
if (bytes.length < start + count)
throw new ImageReadException("Invalid read. bytes.length: " + bytes.length + ", start: " + start + ", count: " + count);
byte[] result = new byte[count];
System.arraycopy(bytes, start, result, 0, count);
if (this.debug)
debugByteArray(name, result);
return result;
}
protected final byte[] getByteArrayTail(String name, byte[] bytes, int count) throws ImageReadException {
return readBytearray(name, bytes, count, bytes.length - count);
}
protected final byte[] getBytearrayHead(String name, byte[] bytes, int count) throws ImageReadException {
return readBytearray(name, bytes, 0, bytes.length - count);
}
public static final byte[] slice(byte[] bytes, int start, int count) {
if (bytes.length < start + count)
return null;
byte[] result = new byte[count];
System.arraycopy(bytes, start, result, 0, count);
return result;
}
public static final byte[] tail(byte[] bytes, int count) {
if (count > bytes.length)
count = bytes.length;
return slice(bytes, bytes.length - count, count);
}
public static final byte[] head(byte[] bytes, int count) {
if (count > bytes.length)
count = bytes.length;
return slice(bytes, 0, count);
}
public final boolean compareByteArrays(byte[] a, byte[] b) {
if (a.length != b.length)
return false;
return compareByteArrays(a, 0, b, 0, a.length);
}
public final boolean compareByteArrays(byte[] a, int aStart, byte[] b, int bStart, int length) {
if (a.length < aStart + length)
return false;
if (b.length < bStart + length)
return false;
for (int i = 0; i < length; i++) {
if (a[aStart + i] != b[bStart + i])
return false;
}
return true;
}
public static final boolean compareBytes(byte[] a, byte[] b) {
if (a.length != b.length)
return false;
return compareBytes(a, 0, b, 0, a.length);
}
public static final boolean compareBytes(byte[] a, int aStart, byte[] b, int bStart, int length) {
if (a.length < aStart + length)
return false;
if (b.length < bStart + length)
return false;
for (int i = 0; i < length; i++) {
if (a[aStart + i] != b[bStart + i])
return false;
}
return true;
}
protected final int read4Bytes(String name, InputStream is, String exception, int byteOrder) throws ImageReadException, IOException {
int size = 4;
byte[] bytes = new byte[size];
int read = 0;
while (read < size) {
int count = is.read(bytes, read, size - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
return convertByteArrayToInt(name, bytes, byteOrder);
}
protected final int read3Bytes(String name, InputStream is, String exception, int byteOrder) throws ImageReadException, IOException {
int result;
byte byte0 = (byte)is.read();
byte byte1 = (byte)is.read();
byte byte2 = (byte)is.read();
if (byteOrder == 77) {
result = (0xFF & byte0) << 16 | (0xFF & byte1) << 8 | (0xFF & byte2) << 0;
} else {
result = (0xFF & byte2) << 16 | (0xFF & byte1) << 8 | (0xFF & byte0) << 0;
}
if (this.debug)
debugNumber(name, result, 3);
return result;
}
protected final int read2Bytes(String name, InputStream is, String exception, int byteOrder) throws ImageReadException, IOException {
int size = 2;
byte[] bytes = new byte[size];
int read = 0;
while (read < size) {
int count = is.read(bytes, read, size - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
return convertByteArrayToShort(name, bytes, byteOrder);
}
protected final void printCharQuad(String msg, int i) {
System.out.println(msg + ": '" + (char)(0xFF & i >> 24) + (char)(0xFF & i >> 16) + (char)(0xFF & i >> 8) + (char)(0xFF & i >> 0) + "'");
}
protected final void printCharQuad(PrintWriter pw, String msg, int i) {
pw.println(msg + ": '" + (char)(0xFF & i >> 24) + (char)(0xFF & i >> 16) + (char)(0xFF & i >> 8) + (char)(0xFF & i >> 0) + "'");
}
protected final void printByteBits(String msg, byte i) {
System.out.println(msg + ": '" + Integer.toBinaryString(0xFF & i));
}
public static final int CharsToQuad(char c1, char c2, char c3, char c4) {
return (0xFF & c1) << 24 | (0xFF & c2) << 16 | (0xFF & c3) << 8 | (0xFF & c4) << 0;
}
public final int findNull(byte[] src) {
return findNull(src, 0);
}
public final int findNull(byte[] src, int start) {
for (int i = start; i < src.length; i++) {
if (src[i] == 0)
return i;
}
return -1;
}
protected final byte[] getRAFBytes(RandomAccessFile raf, long pos, int length, String exception) throws IOException {
if (this.debug) {
System.out.println("getRAFBytes pos: " + pos);
System.out.println("getRAFBytes length: " + length);
}
byte[] result = new byte[length];
raf.seek(pos);
int read = 0;
while (read < length) {
int count = raf.read(result, read, length - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
return result;
}
protected final float convertByteArrayToFloat(String name, byte[] bytes, int byteOrder) {
return convertByteArrayToFloat(name, bytes, 0, byteOrder);
}
protected final float convertByteArrayToFloat(String name, byte[] bytes, int start, int byteOrder) {
int bits;
byte byte0 = bytes[start + 0];
byte byte1 = bytes[start + 1];
byte byte2 = bytes[start + 2];
byte byte3 = bytes[start + 3];
if (byteOrder == 77) {
bits = (0xFF & byte0) << 24 | (0xFF & byte1) << 16 | (0xFF & byte2) << 8 | (0xFF & byte3) << 0;
} else {
bits = (0xFF & byte3) << 24 | (0xFF & byte2) << 16 | (0xFF & byte1) << 8 | (0xFF & byte0) << 0;
}
float result = Float.intBitsToFloat(bits);
return result;
}
protected final float[] convertByteArrayToFloatArray(String name, byte[] bytes, int start, int length, int byteOrder) {
int expectedLength = start + length * 4;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
float[] result = new float[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToFloat(name, bytes, start + i * 4, byteOrder);
return result;
}
protected final byte[] convertFloatToByteArray(float value, int byteOrder) {
byte[] result = new byte[4];
int bits = Float.floatToRawIntBits(value);
if (byteOrder == 77) {
result[0] = (byte)(0xFF & bits >> 0);
result[1] = (byte)(0xFF & bits >> 8);
result[2] = (byte)(0xFF & bits >> 16);
result[3] = (byte)(0xFF & bits >> 24);
} else {
result[3] = (byte)(0xFF & bits >> 0);
result[2] = (byte)(0xFF & bits >> 8);
result[1] = (byte)(0xFF & bits >> 16);
result[0] = (byte)(0xFF & bits >> 24);
}
return result;
}
protected final byte[] convertFloatArrayToByteArray(float[] values, int byteOrder) {
byte[] result = new byte[values.length * 4];
for (int i = 0; i < values.length; i++) {
float value = values[i];
int bits = Float.floatToRawIntBits(value);
int start = i * 4;
if (byteOrder == 77) {
result[start + 0] = (byte)(0xFF & bits >> 0);
result[start + 1] = (byte)(0xFF & bits >> 8);
result[start + 2] = (byte)(0xFF & bits >> 16);
result[start + 3] = (byte)(0xFF & bits >> 24);
} else {
result[start + 3] = (byte)(0xFF & bits >> 0);
result[start + 2] = (byte)(0xFF & bits >> 8);
result[start + 1] = (byte)(0xFF & bits >> 16);
result[start + 0] = (byte)(0xFF & bits >> 24);
}
}
return result;
}
protected final byte[] convertDoubleToByteArray(double value, int byteOrder) {
byte[] result = new byte[8];
long bits = Double.doubleToRawLongBits(value);
if (byteOrder == 77) {
result[0] = (byte)(int)(0xFFL & bits >> 0L);
result[1] = (byte)(int)(0xFFL & bits >> 8L);
result[2] = (byte)(int)(0xFFL & bits >> 16L);
result[3] = (byte)(int)(0xFFL & bits >> 24L);
result[4] = (byte)(int)(0xFFL & bits >> 32L);
result[5] = (byte)(int)(0xFFL & bits >> 40L);
result[6] = (byte)(int)(0xFFL & bits >> 48L);
result[7] = (byte)(int)(0xFFL & bits >> 56L);
} else {
result[7] = (byte)(int)(0xFFL & bits >> 0L);
result[6] = (byte)(int)(0xFFL & bits >> 8L);
result[5] = (byte)(int)(0xFFL & bits >> 16L);
result[4] = (byte)(int)(0xFFL & bits >> 24L);
result[3] = (byte)(int)(0xFFL & bits >> 32L);
result[2] = (byte)(int)(0xFFL & bits >> 40L);
result[1] = (byte)(int)(0xFFL & bits >> 48L);
result[0] = (byte)(int)(0xFFL & bits >> 56L);
}
return result;
}
protected final byte[] convertDoubleArrayToByteArray(double[] values, int byteOrder) {
byte[] result = new byte[values.length * 8];
for (int i = 0; i < values.length; i++) {
double value = values[i];
long bits = Double.doubleToRawLongBits(value);
int start = i * 8;
if (byteOrder == 77) {
result[start + 0] = (byte)(int)(0xFFL & bits >> 0L);
result[start + 1] = (byte)(int)(0xFFL & bits >> 8L);
result[start + 2] = (byte)(int)(0xFFL & bits >> 16L);
result[start + 3] = (byte)(int)(0xFFL & bits >> 24L);
result[start + 4] = (byte)(int)(0xFFL & bits >> 32L);
result[start + 5] = (byte)(int)(0xFFL & bits >> 40L);
result[start + 6] = (byte)(int)(0xFFL & bits >> 48L);
result[start + 7] = (byte)(int)(0xFFL & bits >> 56L);
} else {
result[start + 7] = (byte)(int)(0xFFL & bits >> 0L);
result[start + 6] = (byte)(int)(0xFFL & bits >> 8L);
result[start + 5] = (byte)(int)(0xFFL & bits >> 16L);
result[start + 4] = (byte)(int)(0xFFL & bits >> 24L);
result[start + 3] = (byte)(int)(0xFFL & bits >> 32L);
result[start + 2] = (byte)(int)(0xFFL & bits >> 40L);
result[start + 1] = (byte)(int)(0xFFL & bits >> 48L);
result[start + 0] = (byte)(int)(0xFFL & bits >> 56L);
}
}
return result;
}
protected final double convertByteArrayToDouble(String name, byte[] bytes, int byteOrder) {
return convertByteArrayToDouble(name, bytes, 0, byteOrder);
}
protected final double convertByteArrayToDouble(String name, byte[] bytes, int start, int byteOrder) {
long bits;
byte byte0 = bytes[start + 0];
byte byte1 = bytes[start + 1];
byte byte2 = bytes[start + 2];
byte byte3 = bytes[start + 3];
byte byte4 = bytes[start + 4];
byte byte5 = bytes[start + 5];
byte byte6 = bytes[start + 6];
byte byte7 = bytes[start + 7];
if (byteOrder == 77) {
bits = (long)((0xFF & byte0) << 56 | (0xFF & byte1) << 48 | (0xFF & byte2) << 40 | (0xFF & byte3) << 32 | (0xFF & byte4) << 24 | (0xFF & byte5) << 16 | (0xFF & byte6) << 8 | (0xFF & byte7) << 0);
} else {
bits = (long)((0xFF & byte7) << 56 | (0xFF & byte6) << 48 | (0xFF & byte5) << 40 | (0xFF & byte4) << 32 | (0xFF & byte3) << 24 | (0xFF & byte2) << 16 | (0xFF & byte1) << 8 | (0xFF & byte0) << 0);
}
double result = Double.longBitsToDouble(bits);
return result;
}
protected final double[] convertByteArrayToDoubleArray(String name, byte[] bytes, int start, int length, int byteOrder) {
int expectedLength = start + length * 8;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
double[] result = new double[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToDouble(name, bytes, start + i * 8, byteOrder);
return result;
}
protected void skipBytes(InputStream is, int length) throws IOException {
skipBytes(is, length, "Couldn't skip bytes");
}
public final void copyStreamToStream(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = is.read(buffer)) > 0)
os.write(buffer, 0, read);
}
public final byte[] getStreamBytes(InputStream is) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
copyStreamToStream(is, os);
return os.toByteArray();
}
}

View file

@ -0,0 +1,77 @@
package org.apache.sanselan.common;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
public class BinaryFileParser extends BinaryFileFunctions {
public BinaryFileParser(int byteOrder) {
this.byteOrder = byteOrder;
}
private int byteOrder = 77;
public BinaryFileParser() {}
protected void setByteOrder(int a, int b) throws ImageReadException, IOException {
if (a != b)
throw new ImageReadException("Byte Order bytes don't match (" + a + ", " + b + ").");
if (a == 77) {
this.byteOrder = a;
} else if (a == 73) {
this.byteOrder = a;
} else {
throw new ImageReadException("Unknown Byte Order hint: " + a);
}
}
protected void setByteOrder(int byteOrder) {
this.byteOrder = byteOrder;
}
protected int getByteOrder() {
return this.byteOrder;
}
protected final int convertByteArrayToInt(String name, int start, byte[] bytes) {
return convertByteArrayToInt(name, bytes, start, this.byteOrder);
}
protected final int convertByteArrayToInt(String name, byte[] bytes) {
return convertByteArrayToInt(name, bytes, this.byteOrder);
}
public final int convertByteArrayToShort(String name, byte[] bytes) throws ImageReadException {
return convertByteArrayToShort(name, bytes, this.byteOrder);
}
public final int convertByteArrayToShort(String name, int start, byte[] bytes) throws ImageReadException {
return convertByteArrayToShort(name, start, bytes, this.byteOrder);
}
public final int read4Bytes(String name, InputStream is, String exception) throws ImageReadException, IOException {
return read4Bytes(name, is, exception, this.byteOrder);
}
public final int read3Bytes(String name, InputStream is, String exception) throws ImageReadException, IOException {
return read3Bytes(name, is, exception, this.byteOrder);
}
public final int read2Bytes(String name, InputStream is, String exception) throws ImageReadException, IOException {
return read2Bytes(name, is, exception, this.byteOrder);
}
public static boolean byteArrayHasPrefix(byte[] bytes, byte[] prefix) {
if (bytes == null || bytes.length < prefix.length)
return false;
for (int i = 0; i < prefix.length; i++) {
if (bytes[i] != prefix[i])
return false;
}
return true;
}
protected final byte[] int2ToByteArray(int value) {
return int2ToByteArray(value, this.byteOrder);
}
}

View file

@ -0,0 +1,447 @@
package org.apache.sanselan.common;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import org.apache.sanselan.ImageReadException;
public class BinaryInputStream extends InputStream implements BinaryConstants {
protected boolean debug = false;
private final InputStream is;
public final void setDebug(boolean b) {
this.debug = b;
}
public final boolean getDebug() {
return this.debug;
}
public BinaryInputStream(byte[] bytes, int byteOrder) {
this.byteOrder = byteOrder;
this.is = new ByteArrayInputStream(bytes);
}
public BinaryInputStream(InputStream is, int byteOrder) {
this.byteOrder = byteOrder;
this.is = is;
}
public BinaryInputStream(InputStream is) {
this.is = is;
}
private int byteOrder = 77;
protected void setByteOrder(int a, int b) throws ImageReadException, IOException {
if (a != b)
throw new ImageReadException("Byte Order bytes don't match (" + a + ", " + b + ").");
if (a == 77) {
this.byteOrder = a;
} else if (a == 73) {
this.byteOrder = a;
} else {
throw new ImageReadException("Unknown Byte Order hint: " + a);
}
}
protected void setByteOrder(int byteOrder) {
this.byteOrder = byteOrder;
}
protected int getByteOrder() {
return this.byteOrder;
}
public int read() throws IOException {
return this.is.read();
}
protected final int convertByteArrayToInt(String name, byte[] bytes) {
return convertByteArrayToInt(name, bytes, this.byteOrder);
}
public final int convertByteArrayToShort(String name, byte[] bytes) {
return convertByteArrayToShort(name, bytes, this.byteOrder);
}
public final int convertByteArrayToShort(String name, int start, byte[] bytes) {
return convertByteArrayToShort(name, start, bytes, this.byteOrder);
}
public final int read4Bytes(String name, String exception) throws ImageReadException, IOException {
return read4Bytes(name, exception, this.byteOrder);
}
public final int read3Bytes(String name, String exception) throws ImageReadException, IOException {
return read3Bytes(name, exception, this.byteOrder);
}
public final int read2Bytes(String name, String exception) throws ImageReadException, IOException {
return read2Bytes(name, exception, this.byteOrder);
}
protected final void readRandomBytes() throws ImageReadException, IOException {
for (int counter = 0; counter < 100; counter++)
readByte("" + counter, "Random Data");
}
public final void debugNumber(String msg, int data) {
debugNumber(msg, data, 1);
}
public final void debugNumber(String msg, int data, int bytes) {
System.out.print(msg + ": " + data + " (");
int byteData = data;
for (int i = 0; i < bytes; i++) {
if (i > 0)
System.out.print(",");
int singleByte = 0xFF & byteData;
System.out.print((char)singleByte + " [" + singleByte + "]");
byteData >>= 8;
}
System.out.println(") [0x" + Integer.toHexString(data) + ", " + Integer.toBinaryString(data) + "]");
}
public final void readAndVerifyBytes(byte[] expected, String exception) throws ImageReadException, IOException {
for (int i = 0; i < expected.length; i++) {
int data = this.is.read();
byte b = (byte)(0xFF & data);
if (data < 0 || b != expected[i]) {
System.out.println("i: " + i);
debugByteArray("expected", expected);
debugNumber("data[" + i + "]", b);
throw new ImageReadException(exception);
}
}
}
protected final void readAndVerifyBytes(String name, byte[] expected, String exception) throws ImageReadException, IOException {
byte[] bytes = readByteArray(name, expected.length, exception);
for (int i = 0; i < expected.length; i++) {
if (bytes[i] != expected[i]) {
System.out.println("i: " + i);
debugNumber("bytes[" + i + "]", bytes[i]);
debugNumber("expected[" + i + "]", expected[i]);
throw new ImageReadException(exception);
}
}
}
public final void skipBytes(int length, String exception) throws IOException {
long total = 0L;
while ((long)length != total) {
long skipped = this.is.skip((long)length - total);
if (skipped < 1L)
throw new IOException(exception + " (" + skipped + ")");
total += skipped;
}
}
protected final void scanForByte(byte value) throws IOException {
int count = 0;
for (int i = 0; count < 3; i++) {
int b = this.is.read();
if (b < 0)
return;
if ((0xFF & b) == value) {
System.out.println("\t" + i + ": match.");
count++;
}
}
}
public final byte readByte(String name, String exception) throws IOException {
int result = this.is.read();
if (result < 0) {
System.out.println(name + ": " + result);
throw new IOException(exception);
}
if (this.debug)
debugNumber(name, result);
return (byte)(0xFF & result);
}
protected final RationalNumber[] convertByteArrayToRationalArray(String name, byte[] bytes, int start, int length, int byteOrder) {
int expectedLength = start + length * 8;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
RationalNumber[] result = new RationalNumber[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToRational(name, bytes, start + i * 8, byteOrder);
return result;
}
protected final RationalNumber convertByteArrayToRational(String name, byte[] bytes, int byteOrder) {
return convertByteArrayToRational(name, bytes, 0, byteOrder);
}
protected final RationalNumber convertByteArrayToRational(String name, byte[] bytes, int start, int byteOrder) {
int numerator = convertByteArrayToInt(name, bytes, start + 0, 4, byteOrder);
int divisor = convertByteArrayToInt(name, bytes, start + 4, 4, byteOrder);
return new RationalNumber(numerator, divisor);
}
protected final int convertByteArrayToInt(String name, byte[] bytes, int byteOrder) {
return convertByteArrayToInt(name, bytes, 0, 4, byteOrder);
}
protected final int convertByteArrayToInt(String name, byte[] bytes, int start, int length, int byteOrder) {
int result;
byte byte0 = bytes[start + 0];
byte byte1 = bytes[start + 1];
byte byte2 = bytes[start + 2];
byte byte3 = 0;
if (length == 4)
byte3 = bytes[start + 3];
if (byteOrder == 77) {
result = ((0xFF & byte0) << 24) + ((0xFF & byte1) << 16) + ((0xFF & byte2) << 8) + ((0xFF & byte3) << 0);
} else {
result = ((0xFF & byte3) << 24) + ((0xFF & byte2) << 16) + ((0xFF & byte1) << 8) + ((0xFF & byte0) << 0);
}
if (this.debug)
debugNumber(name, result, 4);
return result;
}
protected final int[] convertByteArrayToIntArray(String name, byte[] bytes, int start, int length, int byteOrder) {
int expectedLength = start + length * 4;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
int[] result = new int[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToInt(name, bytes, start + i * 4, 4, byteOrder);
return result;
}
protected final int convertByteArrayToShort(String name, byte[] bytes, int byteOrder) {
return convertByteArrayToShort(name, 0, bytes, byteOrder);
}
protected final int convertByteArrayToShort(String name, int start, byte[] bytes, int byteOrder) {
int result;
byte byte0 = bytes[start + 0];
byte byte1 = bytes[start + 1];
if (byteOrder == 77) {
result = ((0xFF & byte0) << 8) + ((0xFF & byte1) << 0);
} else {
result = ((0xFF & byte1) << 8) + ((0xFF & byte0) << 0);
}
if (this.debug)
debugNumber(name, result, 2);
return result;
}
protected final int[] convertByteArrayToShortArray(String name, byte[] bytes, int start, int length, int byteOrder) {
int expectedLength = start + length * 2;
if (bytes.length < expectedLength) {
System.out.println(name + ": expected length: " + expectedLength + ", actual length: " + bytes.length);
return null;
}
int[] result = new int[length];
for (int i = 0; i < length; i++)
result[i] = convertByteArrayToShort(name, start + i * 2, bytes, byteOrder);
return result;
}
public final byte[] readByteArray(String name, int length, String exception) throws ImageReadException, IOException {
byte[] result = new byte[length];
int read = 0;
while (read < length) {
int count = this.is.read(result, read, length - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
if (this.debug)
for (int i = 0; i < length && i < 150; i++)
debugNumber(name + " (" + i + ")", 0xFF & result[i]);
return result;
}
protected final void debugByteArray(String name, byte[] bytes) {
System.out.println(name + ": " + bytes.length);
for (int i = 0; i < bytes.length && i < 50; i++)
debugNumber(name + " (" + i + ")", bytes[i]);
}
protected final void debugNumberArray(String name, int[] numbers, int length) {
System.out.println(name + ": " + numbers.length);
for (int i = 0; i < numbers.length && i < 50; i++)
debugNumber(name + " (" + i + ")", numbers[i], length);
}
public final byte[] readBytearray(String name, byte[] bytes, int start, int count) {
if (bytes.length < start + count)
return null;
byte[] result = new byte[count];
System.arraycopy(bytes, start, result, 0, count);
if (this.debug)
debugByteArray(name, result);
return result;
}
public final byte[] readByteArray(int length, String error) throws ImageReadException, IOException {
boolean verbose = false;
boolean strict = true;
return readByteArray(length, error, verbose, strict);
}
public final byte[] readByteArray(int length, String error, boolean verbose, boolean strict) throws ImageReadException, IOException {
byte[] bytes = new byte[length];
int total = 0;
int read;
while ((read = read(bytes, total, length - total)) > 0)
total += read;
if (total < length) {
if (strict)
throw new ImageReadException(error);
if (verbose)
System.out.println(error);
return null;
}
return bytes;
}
protected final byte[] getBytearrayTail(String name, byte[] bytes, int count) {
return readBytearray(name, bytes, count, bytes.length - count);
}
protected final byte[] getBytearrayHead(String name, byte[] bytes, int count) {
return readBytearray(name, bytes, 0, bytes.length - count);
}
public final boolean compareByteArrays(byte[] a, int aStart, byte[] b, int bStart, int length) {
if (a.length < aStart + length)
return false;
if (b.length < bStart + length)
return false;
for (int i = 0; i < length; i++) {
if (a[aStart + i] != b[bStart + i]) {
debugNumber("a[" + (aStart + i) + "]", a[aStart + i]);
debugNumber("b[" + (bStart + i) + "]", b[bStart + i]);
return false;
}
}
return true;
}
protected final int read4Bytes(String name, String exception, int byteOrder) throws ImageReadException, IOException {
int size = 4;
byte[] bytes = new byte[size];
int read = 0;
while (read < size) {
int count = this.is.read(bytes, read, size - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
return convertByteArrayToInt(name, bytes, byteOrder);
}
protected final int read3Bytes(String name, String exception, int byteOrder) throws ImageReadException, IOException {
int size = 3;
byte[] bytes = new byte[size];
int read = 0;
while (read < size) {
int count = this.is.read(bytes, read, size - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
return convertByteArrayToInt(name, bytes, 0, 3, byteOrder);
}
protected final int read2Bytes(String name, String exception, int byteOrder) throws ImageReadException, IOException {
int size = 2;
byte[] bytes = new byte[size];
int read = 0;
while (read < size) {
int count = this.is.read(bytes, read, size - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
return convertByteArrayToShort(name, bytes, byteOrder);
}
public final int read1ByteInteger(String exception) throws ImageReadException, IOException {
int byte0 = this.is.read();
if (byte0 < 0)
throw new ImageReadException(exception);
return 0xFF & byte0;
}
public final int read2ByteInteger(String exception) throws ImageReadException, IOException {
int byte0 = this.is.read();
int byte1 = this.is.read();
if (byte0 < 0 || byte1 < 0)
throw new ImageReadException(exception);
if (this.byteOrder == 77)
return ((0xFF & byte0) << 8) + ((0xFF & byte1) << 0);
return ((0xFF & byte1) << 8) + ((0xFF & byte0) << 0);
}
public final int read4ByteInteger(String exception) throws ImageReadException, IOException {
int byte0 = this.is.read();
int byte1 = this.is.read();
int byte2 = this.is.read();
int byte3 = this.is.read();
if (byte0 < 0 || byte1 < 0 || byte2 < 0 || byte3 < 0)
throw new ImageReadException(exception);
if (this.byteOrder == 77)
return ((0xFF & byte0) << 24) + ((0xFF & byte1) << 16) + ((0xFF & byte2) << 8) + ((0xFF & byte3) << 0);
return ((0xFF & byte3) << 24) + ((0xFF & byte2) << 16) + ((0xFF & byte1) << 8) + ((0xFF & byte0) << 0);
}
protected final void printCharQuad(String msg, int i) {
System.out.println(msg + ": '" + (char)(0xFF & i >> 24) + (char)(0xFF & i >> 16) + (char)(0xFF & i >> 8) + (char)(0xFF & i >> 0) + "'");
}
protected final void printByteBits(String msg, byte i) {
System.out.println(msg + ": '" + Integer.toBinaryString(0xFF & i));
}
protected static final int CharsToQuad(char c1, char c2, char c3, char c4) {
return (0xFF & c1) << 24 | (0xFF & c2) << 16 | (0xFF & c3) << 8 | (0xFF & c4) << 0;
}
public final int findNull(byte[] src) {
return findNull(src, 0);
}
public final int findNull(byte[] src, int start) {
for (int i = start; i < src.length; i++) {
if (src[i] == 0)
return i;
}
return -1;
}
protected final byte[] getRAFBytes(RandomAccessFile raf, long pos, int length, String exception) throws IOException {
byte[] result = new byte[length];
if (this.debug) {
System.out.println("getRAFBytes pos: " + pos);
System.out.println("getRAFBytes length: " + length);
}
raf.seek(pos);
int read = 0;
while (read < length) {
int count = raf.read(result, read, length - read);
if (count < 1)
throw new IOException(exception);
read += count;
}
return result;
}
protected void skipBytes(int length) throws IOException {
skipBytes(length, "Couldn't skip bytes");
}
}

View file

@ -0,0 +1,122 @@
package org.apache.sanselan.common;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.sanselan.ImageWriteException;
public class BinaryOutputStream extends OutputStream implements BinaryConstants {
protected boolean debug = false;
private int count = 0;
private final OutputStream os;
public final void setDebug(boolean b) {
this.debug = b;
}
public final boolean getDebug() {
return this.debug;
}
public BinaryOutputStream(OutputStream os, int byteOrder) {
this.byteOrder = byteOrder;
this.os = os;
}
public BinaryOutputStream(OutputStream os) {
this.os = os;
}
private int byteOrder = 77;
protected void setByteOrder(int a, int b) throws ImageWriteException, IOException {
if (a != b)
throw new ImageWriteException("Byte Order bytes don't match (" + a + ", " + b + ").");
if (a == 77) {
this.byteOrder = a;
} else if (a == 73) {
this.byteOrder = a;
} else {
throw new ImageWriteException("Unknown Byte Order hint: " + a);
}
}
protected void setByteOrder(int byteOrder) {
this.byteOrder = byteOrder;
}
public int getByteOrder() {
return this.byteOrder;
}
public void write(int i) throws IOException {
this.os.write(i);
this.count++;
}
public int getByteCount() {
return this.count;
}
public final void write4Bytes(int value) throws ImageWriteException, IOException {
writeNBytes(value, 4);
}
public final void write3Bytes(int value) throws ImageWriteException, IOException {
writeNBytes(value, 3);
}
public final void write2Bytes(int value) throws ImageWriteException, IOException {
writeNBytes(value, 2);
}
public final void write4ByteInteger(int value) throws ImageWriteException, IOException {
if (this.byteOrder == 77) {
write(0xFF & value >> 24);
write(0xFF & value >> 16);
write(0xFF & value >> 8);
write(0xFF & value);
} else {
write(0xFF & value);
write(0xFF & value >> 8);
write(0xFF & value >> 16);
write(0xFF & value >> 24);
}
}
public final void write2ByteInteger(int value) throws ImageWriteException, IOException {
if (this.byteOrder == 77) {
write(0xFF & value >> 8);
write(0xFF & value);
} else {
write(0xFF & value);
write(0xFF & value >> 8);
}
}
public final void writeByteArray(byte[] bytes) throws IOException {
this.os.write(bytes, 0, bytes.length);
this.count += bytes.length;
}
private byte[] convertValueToByteArray(int value, int n) {
byte[] result = new byte[n];
if (this.byteOrder == 77) {
for (int i = 0; i < n; i++) {
int b = 0xFF & value >> 8 * (n - i - 1);
result[i] = (byte)b;
}
} else {
for (int i = 0; i < n; i++) {
int b = 0xFF & value >> 8 * i;
result[i] = (byte)b;
}
}
return result;
}
private final void writeNBytes(int value, int n) throws ImageWriteException, IOException {
write(convertValueToByteArray(value, n));
}
}

View file

@ -0,0 +1,81 @@
package org.apache.sanselan.common;
import java.io.IOException;
import java.io.InputStream;
public class BitInputStream extends InputStream implements BinaryConstants {
private final InputStream is;
private int cache;
public BitInputStream(InputStream is) {
this.is = is;
}
public int read() throws IOException {
if (this.cacheBitsRemaining > 0)
throw new IOException("BitInputStream: incomplete bit read");
return this.is.read();
}
private int cacheBitsRemaining = 0;
private long bytes_read = 0L;
public final int readBits(int count) throws IOException {
if (count < 8) {
if (this.cacheBitsRemaining == 0) {
this.cache = this.is.read();
this.cacheBitsRemaining = 8;
this.bytes_read++;
}
if (count > this.cacheBitsRemaining)
throw new IOException("BitInputStream: can't read bit fields across bytes");
this.cacheBitsRemaining -= count;
int bits = this.cache >> this.cacheBitsRemaining;
switch (count) {
case 1:
return bits & 0x1;
case 2:
return bits & 0x3;
case 3:
return bits & 0x7;
case 4:
return bits & 0xF;
case 5:
return bits & 0x1F;
case 6:
return bits & 0x3F;
case 7:
return bits & 0x7F;
}
}
if (this.cacheBitsRemaining > 0)
throw new IOException("BitInputStream: incomplete bit read");
if (count == 8) {
this.bytes_read++;
return this.is.read();
}
if (count == 16) {
this.bytes_read += 2L;
return this.is.read() << 8 | this.is.read() << 0;
}
if (count == 24) {
this.bytes_read += 3L;
return this.is.read() << 16 | this.is.read() << 8 | this.is.read() << 0;
}
if (count == 32) {
this.bytes_read += 4L;
return this.is.read() << 24 | this.is.read() << 16 | this.is.read() << 8 | this.is.read() << 0;
}
throw new IOException("BitInputStream: unknown error");
}
public void flushCache() {
this.cacheBitsRemaining = 0;
}
public long getBytesRead() {
return this.bytes_read;
}
}

View file

@ -0,0 +1,69 @@
package org.apache.sanselan.common;
import java.io.IOException;
import java.io.InputStream;
public class BitInputStreamFlexible extends InputStream implements BinaryConstants {
private final InputStream is;
private int cache;
public BitInputStreamFlexible(InputStream is) {
this.is = is;
}
public int read() throws IOException {
if (this.cacheBitsRemaining > 0)
throw new IOException("BitInputStream: incomplete bit read");
return this.is.read();
}
private int cacheBitsRemaining = 0;
private long bytesRead = 0L;
public final int readBits(int count) throws IOException {
if (count <= 32) {
int result = 0;
if (this.cacheBitsRemaining > 0)
if (count >= this.cacheBitsRemaining) {
result = (1 << this.cacheBitsRemaining) - 1 & this.cache;
count -= this.cacheBitsRemaining;
this.cacheBitsRemaining = 0;
} else {
this.cacheBitsRemaining -= count;
result = (1 << count) - 1 & this.cache >> this.cacheBitsRemaining;
count = 0;
}
while (count >= 8) {
this.cache = this.is.read();
if (this.cache < 0)
throw new IOException("couldn't read bits");
System.out.println("cache 1: " + this.cache + " (" + Integer.toHexString(this.cache) + ", " + Integer.toBinaryString(this.cache) + ")");
this.bytesRead++;
result = result << 8 | 0xFF & this.cache;
count -= 8;
}
if (count > 0) {
this.cache = this.is.read();
if (this.cache < 0)
throw new IOException("couldn't read bits");
System.out.println("cache 2: " + this.cache + " (" + Integer.toHexString(this.cache) + ", " + Integer.toBinaryString(this.cache) + ")");
this.bytesRead++;
this.cacheBitsRemaining = 8 - count;
result = result << count | (1 << count) - 1 & this.cache >> this.cacheBitsRemaining;
count = 0;
}
return result;
}
throw new IOException("BitInputStream: unknown error");
}
public void flushCache() {
this.cacheBitsRemaining = 0;
}
public long getBytesRead() {
return this.bytesRead;
}
}

View file

@ -0,0 +1,28 @@
package org.apache.sanselan.common;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.mylzw.MyLZWCompressor;
import org.apache.sanselan.common.mylzw.MyLZWDecompressor;
public class Compression {
public byte[] decompressLZW(byte[] compressed, int LZWMinimumCodeSize, int expectedSize, int byteOrder) throws IOException {
InputStream is = new ByteArrayInputStream(compressed);
MyLZWDecompressor decompressor = new MyLZWDecompressor(LZWMinimumCodeSize, byteOrder);
byte[] result = decompressor.decompress(is, expectedSize);
return result;
}
public byte[] decompressPackBits(byte[] compressed, int expectedSize, int byteOrder) throws ImageReadException, IOException {
byte[] unpacked = new PackBits().decompress(compressed, expectedSize);
return unpacked;
}
public byte[] compressLZW(byte[] src, int LZWMinimumCodeSize, int byteOrder, boolean earlyLimit) throws IOException {
MyLZWCompressor compressor = new MyLZWCompressor(LZWMinimumCodeSize, byteOrder, earlyLimit);
byte[] compressed = compressor.compress(src);
return compressed;
}
}

View file

@ -0,0 +1,9 @@
package org.apache.sanselan.common;
import java.awt.image.BufferedImage;
public interface IBufferedImageFactory {
BufferedImage getColorBufferedImage(int paramInt1, int paramInt2, boolean paramBoolean);
BufferedImage getGrayscaleBufferedImage(int paramInt1, int paramInt2, boolean paramBoolean);
}

View file

@ -0,0 +1,15 @@
package org.apache.sanselan.common;
import java.util.ArrayList;
public interface IImageMetadata {
String toString(String paramString);
ArrayList getItems();
public static interface IImageMetadataItem {
String toString(String param1String);
String toString();
}
}

View file

@ -0,0 +1,68 @@
package org.apache.sanselan.common;
import java.util.ArrayList;
public class ImageMetadata implements IImageMetadata {
private final ArrayList items = new ArrayList();
public void add(String keyword, String text) {
add(new Item(keyword, text));
}
public void add(IImageMetadata.IImageMetadataItem item) {
this.items.add(item);
}
public ArrayList getItems() {
return new ArrayList(this.items);
}
protected static final String newline = System.getProperty("line.separator");
public String toString() {
return toString(null);
}
public String toString(String prefix) {
if (null == prefix)
prefix = "";
StringBuffer result = new StringBuffer();
for (int i = 0; i < this.items.size(); i++) {
if (i > 0)
result.append(newline);
IImageMetadata.IImageMetadataItem item = (IImageMetadata.IImageMetadataItem)this.items.get(i);
result.append(item.toString(prefix + "\t"));
}
return result.toString();
}
public static class Item implements IImageMetadata.IImageMetadataItem {
private final String keyword;
private final String text;
public Item(String keyword, String text) {
this.keyword = keyword;
this.text = text;
}
public String getKeyword() {
return this.keyword;
}
public String getText() {
return this.text;
}
public String toString() {
return toString(null);
}
public String toString(String prefix) {
String result = this.keyword + ": " + this.text;
if (null != prefix)
result = prefix + result;
return result;
}
}
}

View file

@ -0,0 +1,34 @@
package org.apache.sanselan.common;
import java.io.IOException;
import java.io.OutputStream;
public class MyByteArrayOutputStream extends OutputStream {
private final byte[] bytes;
public MyByteArrayOutputStream(int length) {
this.bytes = new byte[length];
}
private int count = 0;
public void write(int value) throws IOException {
if (this.count >= this.bytes.length)
throw new IOException("Write exceeded expected length (" + this.count + ", " + this.bytes.length + ")");
this.bytes[this.count] = (byte)value;
this.count++;
}
public byte[] toByteArray() {
if (this.count < this.bytes.length) {
byte[] result = new byte[this.count];
System.arraycopy(this.bytes, 0, result, 0, this.count);
return result;
}
return this.bytes;
}
public int getBytesWritten() {
return this.count;
}
}

View file

@ -0,0 +1,97 @@
package org.apache.sanselan.common;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.sanselan.ImageReadException;
public class PackBits {
public byte[] decompress(byte[] bytes, int expected) throws ImageReadException, IOException {
int total = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = 0;
while (total < expected) {
if (i >= bytes.length)
throw new ImageReadException("Tiff: Unpack bits source exhausted: " + i + ", done + " + total + ", expected + " + expected);
int n = bytes[i++];
if (n >= 0 && n <= 127) {
int count = n + 1;
total += count;
for (int j = 0; j < count; j++)
baos.write(bytes[i++]);
continue;
}
if (n >= -127 && n <= -1) {
int b = bytes[i++];
int count = -n + 1;
total += count;
for (int j = 0; j < count; j++)
baos.write(b);
continue;
}
if (n == -128)
throw new ImageReadException("Packbits: " + n);
}
byte[] result = baos.toByteArray();
return result;
}
private int findNextDuplicate(byte[] bytes, int start) {
if (start >= bytes.length)
return -1;
byte prev = bytes[start];
for (int i = start + 1; i < bytes.length; i++) {
byte b = bytes[i];
if (b == prev)
return i - 1;
prev = b;
}
return -1;
}
private int findRunLength(byte[] bytes, int start) {
byte b = bytes[start];
int i;
for (i = start + 1; i < bytes.length && bytes[i] == b; i++);
return i - start;
}
public byte[] compress(byte[] bytes) throws IOException {
MyByteArrayOutputStream baos = new MyByteArrayOutputStream(bytes.length * 2);
int ptr = 0;
int count = 0;
while (ptr < bytes.length) {
count++;
int dup = findNextDuplicate(bytes, ptr);
if (dup == ptr) {
int j = findRunLength(bytes, dup);
int k = Math.min(j, 128);
baos.write(-(k - 1));
baos.write(bytes[ptr]);
ptr += k;
continue;
}
int len = dup - ptr;
if (dup > 0) {
int runlen = findRunLength(bytes, dup);
if (runlen < 3) {
int nextptr = ptr + len + runlen;
int nextdup = findNextDuplicate(bytes, nextptr);
if (nextdup != nextptr) {
dup = nextdup;
len = dup - ptr;
}
}
}
if (dup < 0)
len = bytes.length - ptr;
int actual_len = Math.min(len, 128);
baos.write(actual_len - 1);
for (int i = 0; i < actual_len; i++) {
baos.write(bytes[ptr]);
ptr++;
}
}
byte[] result = baos.toByteArray();
return result;
}
}

View file

@ -0,0 +1,80 @@
package org.apache.sanselan.common;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class RationalNumber extends Number {
private static final long serialVersionUID = -1L;
public final int numerator;
public final int divisor;
public RationalNumber(int numerator, int divisor) {
this.numerator = numerator;
this.divisor = divisor;
}
public static final RationalNumber factoryMethod(long n, long d) {
if (n > Integer.MAX_VALUE || n < Integer.MIN_VALUE || d > Integer.MAX_VALUE || d < Integer.MIN_VALUE) {
while ((n > Integer.MAX_VALUE || n < Integer.MIN_VALUE || d > Integer.MAX_VALUE || d < Integer.MIN_VALUE) && Math.abs(n) > 1L && Math.abs(d) > 1L) {
n >>= 1L;
d >>= 1L;
}
if (d == 0L)
throw new NumberFormatException("Invalid value, numerator: " + n + ", divisor: " + d);
}
long gcd = gcd(n, d);
d /= gcd;
n /= gcd;
return new RationalNumber((int)n, (int)d);
}
private static long gcd(long a, long b) {
if (b == 0L)
return a;
return gcd(b, a % b);
}
public RationalNumber negate() {
return new RationalNumber(-this.numerator, this.divisor);
}
public double doubleValue() {
return (double)this.numerator / (double)this.divisor;
}
public float floatValue() {
return (float)this.numerator / (float)this.divisor;
}
public int intValue() {
return this.numerator / this.divisor;
}
public long longValue() {
return (long)this.numerator / (long)this.divisor;
}
public boolean isValid() {
return (this.divisor != 0);
}
private static final NumberFormat nf = DecimalFormat.getInstance();
public String toString() {
if (this.divisor == 0)
return "Invalid rational (" + this.numerator + "/" + this.divisor + ")";
if (this.numerator % this.divisor == 0)
return nf.format((long)(this.numerator / this.divisor));
return this.numerator + "/" + this.divisor + " (" + nf.format((double)this.numerator / (double)this.divisor) + ")";
}
public String toDisplayString() {
if (this.numerator % this.divisor == 0)
return "" + (this.numerator / this.divisor);
NumberFormat nf = DecimalFormat.getInstance();
nf.setMaximumFractionDigits(3);
return nf.format((double)this.numerator / (double)this.divisor);
}
}

View file

@ -0,0 +1,79 @@
package org.apache.sanselan.common;
public abstract class RationalNumberUtilities extends Number {
private static final double TOLERANCE = 1.0E-8D;
private static class Option {
public final RationalNumber rationalNumber;
public final double error;
private Option(RationalNumber rationalNumber, double error) {
this.rationalNumber = rationalNumber;
this.error = error;
}
public static final Option factory(RationalNumber rationalNumber, double value) {
return new Option(rationalNumber, Math.abs(rationalNumber.doubleValue() - value));
}
public String toString() {
return this.rationalNumber.toString();
}
}
public static final RationalNumber getRationalNumber(double value) {
RationalNumber l, h;
if (value >= Integer.MAX_VALUE)
return new RationalNumber(Integer.MAX_VALUE, 1);
if (value <= -2.147483647E9D)
return new RationalNumber(-2147483647, 1);
boolean negative = false;
if (value < 0.0D) {
negative = true;
value = Math.abs(value);
}
if (value == 0.0D)
return new RationalNumber(0, 1);
if (value >= 1.0D) {
int approx = (int)value;
if ((double)approx < value) {
l = new RationalNumber(approx, 1);
h = new RationalNumber(approx + 1, 1);
} else {
l = new RationalNumber(approx - 1, 1);
h = new RationalNumber(approx, 1);
}
} else {
int approx = (int)(1.0D / value);
if (1.0D / (double)approx < value) {
l = new RationalNumber(1, approx);
h = new RationalNumber(1, approx - 1);
} else {
l = new RationalNumber(1, approx + 1);
h = new RationalNumber(1, approx);
}
}
Option low = Option.factory(l, value);
Option high = Option.factory(h, value);
Option bestOption = (low.error < high.error) ? low : high;
int MAX_ITERATIONS = 100;
int count = 0;
for (; bestOption.error > 1.0E-8D && count < 100; count++) {
RationalNumber mediant = RationalNumber.factoryMethod((long)low.rationalNumber.numerator + (long)high.rationalNumber.numerator, (long)low.rationalNumber.divisor + (long)high.rationalNumber.divisor);
Option mediantOption = Option.factory(mediant, value);
if (value < mediant.doubleValue()) {
if (high.error <= mediantOption.error)
break;
high = mediantOption;
} else {
if (low.error <= mediantOption.error)
break;
low = mediantOption;
}
if (mediantOption.error < bestOption.error)
bestOption = mediantOption;
}
return negative ? bestOption.rationalNumber.negate() : bestOption.rationalNumber;
}
}

View file

@ -0,0 +1,15 @@
package org.apache.sanselan.common;
import java.awt.image.BufferedImage;
public class RgbBufferedImageFactory implements IBufferedImageFactory {
public BufferedImage getColorBufferedImage(int width, int height, boolean hasAlpha) {
if (hasAlpha)
return new BufferedImage(width, height, 2);
return new BufferedImage(width, height, 1);
}
public BufferedImage getGrayscaleBufferedImage(int width, int height, boolean hasAlpha) {
return getColorBufferedImage(width, height, hasAlpha);
}
}

View file

@ -0,0 +1,17 @@
package org.apache.sanselan.common;
import java.awt.image.BufferedImage;
public class SimpleBufferedImageFactory implements IBufferedImageFactory {
public BufferedImage getColorBufferedImage(int width, int height, boolean hasAlpha) {
if (hasAlpha)
return new BufferedImage(width, height, 2);
return new BufferedImage(width, height, 1);
}
public BufferedImage getGrayscaleBufferedImage(int width, int height, boolean hasAlpha) {
if (hasAlpha)
return new BufferedImage(width, height, 2);
return new BufferedImage(width, height, 10);
}
}

View file

@ -0,0 +1,23 @@
package org.apache.sanselan.common;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
public class ZLibUtils extends BinaryFileFunctions {
public final byte[] inflate(byte[] bytes) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
InflaterInputStream zIn = new InflaterInputStream(in);
return getStreamBytes(zIn);
}
public final byte[] deflate(byte[] bytes) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(baos);
dos.write(bytes);
dos.close();
return baos.toByteArray();
}
}

View file

@ -0,0 +1,33 @@
package org.apache.sanselan.common.byteSources;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.common.BinaryFileFunctions;
public abstract class ByteSource extends BinaryFileFunctions {
protected final String filename;
public ByteSource(String filename) {
this.filename = filename;
}
public final InputStream getInputStream(int start) throws IOException {
InputStream is = getInputStream();
skipBytes(is, start);
return is;
}
public abstract InputStream getInputStream() throws IOException;
public abstract byte[] getBlock(int paramInt1, int paramInt2) throws IOException;
public abstract byte[] getAll() throws IOException;
public abstract long getLength() throws IOException;
public abstract String getDescription();
public final String getFilename() {
return this.filename;
}
}

View file

@ -0,0 +1,43 @@
package org.apache.sanselan.common.byteSources;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ByteSourceArray extends ByteSource {
private final byte[] bytes;
public ByteSourceArray(String filename, byte[] bytes) {
super(filename);
this.bytes = bytes;
}
public ByteSourceArray(byte[] bytes) {
super(null);
this.bytes = bytes;
}
public InputStream getInputStream() {
return new ByteArrayInputStream(this.bytes);
}
public byte[] getBlock(int start, int length) throws IOException {
if (start + length > this.bytes.length)
throw new IOException("Could not read block (block start: " + start + ", block length: " + length + ", data length: " + this.bytes.length + ").");
byte[] result = new byte[length];
System.arraycopy(this.bytes, start, result, 0, length);
return result;
}
public long getLength() {
return (long)this.bytes.length;
}
public byte[] getAll() throws IOException {
return this.bytes;
}
public String getDescription() {
return this.bytes.length + " byte array";
}
}

View file

@ -0,0 +1,68 @@
package org.apache.sanselan.common.byteSources;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import org.apache.sanselan.util.Debug;
public class ByteSourceFile extends ByteSource {
private final File file;
public ByteSourceFile(File file) {
super(file.getName());
this.file = file;
}
public InputStream getInputStream() throws IOException {
FileInputStream is = null;
BufferedInputStream bis = null;
is = new FileInputStream(this.file);
bis = new BufferedInputStream(is);
return bis;
}
public byte[] getBlock(int start, int length) throws IOException {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(this.file, "r");
return getRAFBytes(raf, (long)start, length, "Could not read value from file");
} finally {
try {
raf.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
public long getLength() {
return this.file.length();
}
public byte[] getAll() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
try {
is = new FileInputStream(this.file);
is = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int read;
while ((read = is.read(buffer)) > 0)
baos.write(buffer, 0, read);
return baos.toByteArray();
} finally {
try {
if (null != is)
is.close();
} catch (IOException e) {}
}
}
public String getDescription() {
return "File: '" + this.file.getAbsolutePath() + "'";
}
}

View file

@ -0,0 +1,171 @@
package org.apache.sanselan.common.byteSources;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ByteSourceInputStream extends ByteSource {
private final InputStream is;
private CacheBlock cacheHead = null;
private static final int BLOCK_SIZE = 1024;
public ByteSourceInputStream(InputStream is, String filename) {
super(filename);
this.is = new BufferedInputStream(is);
}
private class CacheBlock {
public final byte[] bytes;
private CacheBlock next = null;
private boolean triedNext = false;
public CacheBlock(byte[] bytes) {
this.bytes = bytes;
}
public CacheBlock getNext() throws IOException {
if (null != this.next)
return this.next;
if (this.triedNext)
return null;
this.triedNext = true;
this.next = ByteSourceInputStream.this.readBlock();
return this.next;
}
}
private byte[] readBuffer = null;
private CacheBlock readBlock() throws IOException {
if (null == this.readBuffer)
this.readBuffer = new byte[1024];
int read = this.is.read(this.readBuffer);
if (read < 1)
return null;
if (read < 1024) {
byte[] arrayOfByte = new byte[read];
System.arraycopy(this.readBuffer, 0, arrayOfByte, 0, read);
return new CacheBlock(arrayOfByte);
}
byte[] result = this.readBuffer;
this.readBuffer = null;
return new CacheBlock(result);
}
private CacheBlock getFirstBlock() throws IOException {
if (null == this.cacheHead)
this.cacheHead = readBlock();
return this.cacheHead;
}
private class CacheReadingInputStream extends InputStream {
CacheReadingInputStream(ByteSourceInputStream.null x1) {
this();
}
private ByteSourceInputStream.CacheBlock block = null;
private boolean readFirst = false;
private int blockIndex = 0;
public int read() throws IOException {
if (null == this.block) {
if (this.readFirst)
return -1;
this.block = ByteSourceInputStream.this.getFirstBlock();
this.readFirst = true;
}
if (this.block != null && this.blockIndex >= this.block.bytes.length) {
this.block = this.block.getNext();
this.blockIndex = 0;
}
if (null == this.block)
return -1;
if (this.blockIndex >= this.block.bytes.length)
return -1;
return 0xFF & this.block.bytes[this.blockIndex++];
}
public int read(byte[] b, int off, int len) throws IOException {
if (b == null)
throw new NullPointerException();
if (off < 0 || off > b.length || len < 0 || off + len > b.length || off + len < 0)
throw new IndexOutOfBoundsException();
if (len == 0)
return 0;
if (null == this.block) {
if (this.readFirst)
return -1;
this.block = ByteSourceInputStream.this.getFirstBlock();
this.readFirst = true;
}
if (this.block != null && this.blockIndex >= this.block.bytes.length) {
this.block = this.block.getNext();
this.blockIndex = 0;
}
if (null == this.block)
return -1;
if (this.blockIndex >= this.block.bytes.length)
return -1;
int readSize = Math.min(len, this.block.bytes.length - this.blockIndex);
System.arraycopy(this.block.bytes, this.blockIndex, b, off, readSize);
this.blockIndex += readSize;
return readSize;
}
private CacheReadingInputStream() {}
}
public InputStream getInputStream() throws IOException {
return new CacheReadingInputStream();
}
public byte[] getBlock(int start, int length) throws IOException {
InputStream is = getInputStream();
is.skip((long)start);
byte[] bytes = new byte[length];
int total = 0;
while (true) {
int read = is.read(bytes, total, bytes.length - total);
if (read < 1)
throw new IOException("Could not read block.");
total += read;
if (total >= length)
return bytes;
}
}
private Long length = null;
public long getLength() throws IOException {
if (this.length != null)
return this.length.longValue();
InputStream is = getInputStream();
long result = 0L;
long skipped;
while ((skipped = is.skip(1024L)) > 0L)
result += skipped;
this.length = new Long(result);
return result;
}
public byte[] getAll() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CacheBlock block = getFirstBlock();
while (block != null) {
baos.write(block.bytes);
block = block.getNext();
}
return baos.toByteArray();
}
public String getDescription() {
return "Inputstream: '" + this.filename + "'";
}
}

View file

@ -0,0 +1,36 @@
package org.apache.sanselan.common.mylzw;
import java.io.IOException;
import java.io.InputStream;
public class BitsToByteInputStream extends InputStream {
private final MyBitInputStream is;
private final int desiredDepth;
public BitsToByteInputStream(MyBitInputStream is, int desiredDepth) {
this.is = is;
this.desiredDepth = desiredDepth;
}
public int read() throws IOException {
return readBits(8);
}
public int readBits(int bitCount) throws IOException {
int i = this.is.readBits(bitCount);
if (bitCount < this.desiredDepth) {
i <<= this.desiredDepth - bitCount;
} else if (bitCount > this.desiredDepth) {
i >>= bitCount - this.desiredDepth;
}
return i;
}
public int[] readBitsArray(int sampleBits, int length) throws IOException {
int[] result = new int[length];
for (int i = 0; i < length; i++)
result[i] = readBits(sampleBits);
return result;
}
}

View file

@ -0,0 +1,77 @@
package org.apache.sanselan.common.mylzw;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.common.BinaryConstants;
public class MyBitInputStream extends InputStream implements BinaryConstants {
private final InputStream is;
private final int byteOrder;
private boolean tiffLZWMode = false;
public MyBitInputStream(InputStream is, int byteOrder) {
this.byteOrder = byteOrder;
this.is = is;
}
public int read() throws IOException {
return readBits(8);
}
private long bytesRead = 0L;
private int bitsInCache = 0;
private int bitCache = 0;
public void setTiffLZWMode() {
this.tiffLZWMode = true;
}
public int readBits(int SampleBits) throws IOException {
int sample;
while (this.bitsInCache < SampleBits) {
int next = this.is.read();
if (next < 0) {
if (this.tiffLZWMode)
return 257;
return -1;
}
int newByte = 0xFF & next;
if (this.byteOrder == 77) {
this.bitCache = this.bitCache << 8 | newByte;
} else if (this.byteOrder == 73) {
this.bitCache = newByte << this.bitsInCache | this.bitCache;
} else {
throw new IOException("Unknown byte order: " + this.byteOrder);
}
this.bytesRead++;
this.bitsInCache += 8;
}
int sampleMask = (1 << SampleBits) - 1;
if (this.byteOrder == 77) {
sample = sampleMask & this.bitCache >> this.bitsInCache - SampleBits;
} else if (this.byteOrder == 73) {
sample = sampleMask & this.bitCache;
this.bitCache >>= SampleBits;
} else {
throw new IOException("Unknown byte order: " + this.byteOrder);
}
int result = sample;
this.bitsInCache -= SampleBits;
int remainderMask = (1 << this.bitsInCache) - 1;
this.bitCache &= remainderMask;
return result;
}
public void flushCache() {
this.bitsInCache = 0;
this.bitCache = 0;
}
public long getBytesRead() {
return this.bytesRead;
}
}

View file

@ -0,0 +1,77 @@
package org.apache.sanselan.common.mylzw;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.sanselan.common.BinaryConstants;
public class MyBitOutputStream extends OutputStream implements BinaryConstants {
private final OutputStream os;
private final int byteOrder;
public MyBitOutputStream(OutputStream os, int byteOrder) {
this.byteOrder = byteOrder;
this.os = os;
}
public void write(int value) throws IOException {
writeBits(value, 8);
}
private int bitsInCache = 0;
private int bitCache = 0;
public void writeBits(int value, int SampleBits) throws IOException {
int sampleMask = (1 << SampleBits) - 1;
value &= sampleMask;
if (this.byteOrder == 77) {
this.bitCache = this.bitCache << SampleBits | value;
} else if (this.byteOrder == 73) {
this.bitCache |= value << this.bitsInCache;
} else {
throw new IOException("Unknown byte order: " + this.byteOrder);
}
this.bitsInCache += SampleBits;
while (this.bitsInCache >= 8) {
if (this.byteOrder == 77) {
int b = 0xFF & this.bitCache >> this.bitsInCache - 8;
actualWrite(b);
this.bitsInCache -= 8;
} else if (this.byteOrder == 73) {
int b = 0xFF & this.bitCache;
actualWrite(b);
this.bitCache >>= 8;
this.bitsInCache -= 8;
}
int remainderMask = (1 << this.bitsInCache) - 1;
this.bitCache &= remainderMask;
}
}
private int bytesWritten = 0;
private void actualWrite(int value) throws IOException {
this.os.write(value);
this.bytesWritten++;
}
public void flushCache() throws IOException {
if (this.bitsInCache > 0) {
int bitMask = (1 << this.bitsInCache) - 1;
int b = bitMask & this.bitCache;
if (this.byteOrder == 77) {
b <<= 8 - this.bitsInCache;
this.os.write(b);
} else if (this.byteOrder == 73) {
this.os.write(b);
}
}
this.bitsInCache = 0;
this.bitCache = 0;
}
public int getBytesWritten() {
return this.bytesWritten + ((this.bitsInCache > 0) ? 1 : 0);
}
}

View file

@ -0,0 +1,213 @@
package org.apache.sanselan.common.mylzw;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class MyLZWCompressor {
private int codeSize;
private final int initialCodeSize;
private int codes = -1;
private final int byteOrder;
private final boolean earlyLimit;
private final int clearCode;
private final int eoiCode;
private final Listener listener;
public MyLZWCompressor(int initialCodeSize, int byteOrder, boolean earlyLimit) {
this(initialCodeSize, byteOrder, earlyLimit, null);
}
public MyLZWCompressor(int initialCodeSize, int byteOrder, boolean earlyLimit, Listener listener) {
this.listener = listener;
this.byteOrder = byteOrder;
this.earlyLimit = earlyLimit;
this.initialCodeSize = initialCodeSize;
this.clearCode = 1 << initialCodeSize;
this.eoiCode = this.clearCode + 1;
if (null != listener)
listener.init(this.clearCode, this.eoiCode);
InitializeStringTable();
}
private final Map map = new HashMap();
private final void InitializeStringTable() {
this.codeSize = this.initialCodeSize;
int intial_entries_count = (1 << this.codeSize) + 2;
this.map.clear();
for (this.codes = 0; this.codes < intial_entries_count; this.codes++) {
if (this.codes != this.clearCode && this.codes != this.eoiCode) {
Object key = arrayToKey((byte)this.codes);
this.map.put(key, new Integer(this.codes));
}
}
}
private final void clearTable() {
InitializeStringTable();
incrementCodeSize();
}
private final void incrementCodeSize() {
if (this.codeSize != 12)
this.codeSize++;
}
private final Object arrayToKey(byte b) {
return arrayToKey(new byte[] { (byte)b }, 0, 1);
}
public static interface Listener {
void dataCode(int param1Int);
void eoiCode(int param1Int);
void clearCode(int param1Int);
void init(int param1Int1, int param1Int2);
}
private static final class ByteArray {
private final byte[] bytes;
private final int start;
private final int length;
private final int hash;
public ByteArray(byte[] bytes) {
this(bytes, 0, bytes.length);
}
public ByteArray(byte[] bytes, int start, int length) {
this.bytes = bytes;
this.start = start;
this.length = length;
int tempHash = length;
for (int i = 0; i < length; i++) {
int b = 0xFF & bytes[i + start];
tempHash = tempHash + (tempHash << 8) ^ b ^ i;
}
this.hash = tempHash;
}
public final int hashCode() {
return this.hash;
}
public final boolean equals(Object o) {
ByteArray other = (ByteArray)o;
if (other.hash != this.hash)
return false;
if (other.length != this.length)
return false;
for (int i = 0; i < this.length; i++) {
if (other.bytes[i + other.start] != this.bytes[i + this.start])
return false;
}
return true;
}
}
private final Object arrayToKey(byte[] bytes, int start, int length) {
return new ByteArray(bytes, start, length);
}
private final void writeDataCode(MyBitOutputStream bos, int code) throws IOException {
if (null != this.listener)
this.listener.dataCode(code);
writeCode(bos, code);
}
private final void writeClearCode(MyBitOutputStream bos) throws IOException {
if (null != this.listener)
this.listener.dataCode(this.clearCode);
writeCode(bos, this.clearCode);
}
private final void writeEoiCode(MyBitOutputStream bos) throws IOException {
if (null != this.listener)
this.listener.eoiCode(this.eoiCode);
writeCode(bos, this.eoiCode);
}
private final void writeCode(MyBitOutputStream bos, int code) throws IOException {
bos.writeBits(code, this.codeSize);
}
private final boolean isInTable(byte[] bytes, int start, int length) {
Object key = arrayToKey(bytes, start, length);
return this.map.containsKey(key);
}
private final int codeFromString(byte[] bytes, int start, int length) throws IOException {
Object key = arrayToKey(bytes, start, length);
Object o = this.map.get(key);
if (o == null)
throw new IOException("CodeFromString");
return ((Integer)o).intValue();
}
private final boolean addTableEntry(MyBitOutputStream bos, byte[] bytes, int start, int length) throws IOException {
Object key = arrayToKey(bytes, start, length);
return addTableEntry(bos, key);
}
private final boolean addTableEntry(MyBitOutputStream bos, Object key) throws IOException {
boolean cleared = false;
int limit = 1 << this.codeSize;
if (this.earlyLimit)
limit--;
if (this.codes == limit)
if (this.codeSize < 12) {
incrementCodeSize();
} else {
writeClearCode(bos);
clearTable();
cleared = true;
}
if (!cleared) {
this.map.put(key, new Integer(this.codes));
this.codes++;
}
return cleared;
}
public byte[] compress(byte[] bytes) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length);
MyBitOutputStream bos = new MyBitOutputStream(baos, this.byteOrder);
InitializeStringTable();
clearTable();
writeClearCode(bos);
boolean cleared = false;
int w_start = 0;
int w_length = 0;
for (int i = 0; i < bytes.length; i++) {
if (isInTable(bytes, w_start, w_length + 1)) {
w_length++;
cleared = false;
} else {
int j = codeFromString(bytes, w_start, w_length);
writeDataCode(bos, j);
cleared = addTableEntry(bos, bytes, w_start, w_length + 1);
w_start = i;
w_length = 1;
}
}
int code = codeFromString(bytes, w_start, w_length);
writeDataCode(bos, code);
writeEoiCode(bos);
bos.flushCache();
return baos.toByteArray();
}
}

View file

@ -0,0 +1,161 @@
package org.apache.sanselan.common.mylzw;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public final class MyLZWDecompressor {
private static final int MAX_TABLE_SIZE = 4096;
private final byte[][] table;
private int codeSize;
private final int initialCodeSize;
private int codes = -1;
private final int byteOrder;
private final Listener listener;
private final int clearCode;
private final int eoiCode;
public MyLZWDecompressor(int initialCodeSize, int byteOrder) {
this(initialCodeSize, byteOrder, null);
}
public MyLZWDecompressor(int initialCodeSize, int byteOrder, Listener listener) {
this.listener = listener;
this.byteOrder = byteOrder;
this.initialCodeSize = initialCodeSize;
this.table = new byte[4096][];
this.clearCode = 1 << initialCodeSize;
this.eoiCode = this.clearCode + 1;
if (null != listener)
listener.init(this.clearCode, this.eoiCode);
InitializeTable();
}
private final void InitializeTable() {
this.codeSize = this.initialCodeSize;
int intial_entries_count = 1 << this.codeSize + 2;
for (int i = 0; i < intial_entries_count; i++) {
this.table[i] = new byte[] { (byte)i };
}
}
private final void clearTable() {
this.codes = (1 << this.initialCodeSize) + 2;
this.codeSize = this.initialCodeSize;
incrementCodeSize();
}
private final int getNextCode(MyBitInputStream is) throws IOException {
int code = is.readBits(this.codeSize);
if (null != this.listener)
this.listener.code(code);
return code;
}
private final byte[] stringFromCode(int code) throws IOException {
if (code >= this.codes || code < 0)
throw new IOException("Bad Code: " + code + " codes: " + this.codes + " code_size: " + this.codeSize + ", table: " + this.table.length);
return this.table[code];
}
private final boolean isInTable(int Code) {
return (Code < this.codes);
}
private final byte firstChar(byte[] bytes) {
return bytes[0];
}
private final void addStringToTable(byte[] bytes) throws IOException {
if (this.codes < 1 << this.codeSize) {
this.table[this.codes] = bytes;
this.codes++;
} else {
throw new IOException("AddStringToTable: codes: " + this.codes + " code_size: " + this.codeSize);
}
checkCodeSize();
}
private final byte[] appendBytes(byte[] bytes, byte b) {
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0, result, 0, bytes.length);
result[result.length - 1] = b;
return result;
}
private int written = 0;
private final void writeToResult(OutputStream os, byte[] bytes) throws IOException {
os.write(bytes);
this.written += bytes.length;
}
private boolean tiffLZWMode = false;
public void setTiffLZWMode() {
this.tiffLZWMode = true;
}
public byte[] decompress(InputStream is, int expectedLength) throws IOException {
int oldCode = -1;
MyBitInputStream mbis = new MyBitInputStream(is, this.byteOrder);
if (this.tiffLZWMode)
mbis.setTiffLZWMode();
ByteArrayOutputStream baos = new ByteArrayOutputStream(expectedLength);
clearTable();
int code;
while ((code = getNextCode(mbis)) != this.eoiCode) {
if (code == this.clearCode) {
clearTable();
if (this.written >= expectedLength)
break;
code = getNextCode(mbis);
if (code == this.eoiCode)
break;
writeToResult(baos, stringFromCode(code));
oldCode = code;
} else if (isInTable(code)) {
writeToResult(baos, stringFromCode(code));
addStringToTable(appendBytes(stringFromCode(oldCode), firstChar(stringFromCode(code))));
oldCode = code;
} else {
byte[] OutString = appendBytes(stringFromCode(oldCode), firstChar(stringFromCode(oldCode)));
writeToResult(baos, OutString);
addStringToTable(OutString);
oldCode = code;
}
if (this.written >= expectedLength)
break;
}
byte[] result = baos.toByteArray();
return result;
}
private final void checkCodeSize() {
int limit = 1 << this.codeSize;
if (this.tiffLZWMode)
limit--;
if (this.codes == limit)
incrementCodeSize();
}
private final void incrementCodeSize() {
if (this.codeSize != 12)
this.codeSize++;
}
public static interface Listener {
void code(int param1Int);
void init(int param1Int1, int param1Int2);
}
}

View file

@ -0,0 +1,54 @@
package org.apache.sanselan.formats.bmp;
public class BmpHeaderInfo {
public final byte identifier1;
public final byte identifier2;
public final int fileSize;
public final int reserved;
public final int bitmapDataOffset;
public final int bitmapHeaderSize;
public final int width;
public final int height;
public final int planes;
public final int bitsPerPixel;
public final int compression;
public final int bitmapDataSize;
public final int hResolution;
public final int vResolution;
public final int colorsUsed;
public final int colorsImportant;
public BmpHeaderInfo(byte identifier1, byte identifier2, int fileSize, int reserved, int bitmapDataOffset, int bitmapHeaderSize, int width, int height, int planes, int bitsPerPixel, int compression, int bitmapDataSize, int hResolution, int vResolution, int colorsUsed, int colorsImportant) {
this.identifier1 = identifier1;
this.identifier2 = identifier2;
this.fileSize = fileSize;
this.reserved = reserved;
this.bitmapDataOffset = bitmapDataOffset;
this.bitmapHeaderSize = bitmapHeaderSize;
this.width = width;
this.height = height;
this.planes = planes;
this.bitsPerPixel = bitsPerPixel;
this.compression = compression;
this.bitmapDataSize = bitmapDataSize;
this.hResolution = hResolution;
this.vResolution = vResolution;
this.colorsUsed = colorsUsed;
this.colorsImportant = colorsImportant;
}
}

View file

@ -0,0 +1,447 @@
package org.apache.sanselan.formats.bmp;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.sanselan.FormatCompliance;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryOutputStream;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.bmp.pixelparsers.PixelParser;
import org.apache.sanselan.formats.bmp.pixelparsers.PixelParserBitFields;
import org.apache.sanselan.formats.bmp.pixelparsers.PixelParserRgb;
import org.apache.sanselan.formats.bmp.pixelparsers.PixelParserRle;
import org.apache.sanselan.formats.bmp.writers.BMPWriter;
import org.apache.sanselan.formats.bmp.writers.BMPWriterPalette;
import org.apache.sanselan.formats.bmp.writers.BMPWriterRGB;
import org.apache.sanselan.palette.PaletteFactory;
import org.apache.sanselan.palette.SimplePalette;
import org.apache.sanselan.util.Debug;
import org.apache.sanselan.util.ParamMap;
public class BmpImageParser extends ImageParser {
private static final String DEFAULT_EXTENSION = ".bmp";
public BmpImageParser() {
setByteOrder(73);
}
public String getName() {
return "Bmp-Custom";
}
public String getDefaultExtension() {
return ".bmp";
}
private static final String[] ACCEPTED_EXTENSIONS = new String[] { ".bmp" };
protected String[] getAcceptedExtensions() {
return ACCEPTED_EXTENSIONS;
}
protected ImageFormat[] getAcceptedTypes() {
return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_BMP };
}
private static final byte[] BMP_HEADER_SIGNATURE = new byte[] { 66, 77 };
private static final int BI_RGB = 0;
private static final int BI_RLE4 = 2;
private static final int BI_RLE8 = 1;
private static final int BI_BITFIELDS = 3;
private static final int BITMAP_FILE_HEADER_SIZE = 14;
private static final int BITMAP_INFO_HEADER_SIZE = 40;
private BmpHeaderInfo readBmpHeaderInfo(InputStream is, FormatCompliance formatCompliance, boolean verbose) throws ImageReadException, IOException {
byte identifier1 = readByte("Identifier1", is, "Not a Valid BMP File");
byte identifier2 = readByte("Identifier2", is, "Not a Valid BMP File");
if (formatCompliance != null)
formatCompliance.compare_bytes("Signature", BMP_HEADER_SIGNATURE, new byte[] { (byte)identifier1, (byte)identifier2 });
int fileSize = read4Bytes("File Size", is, "Not a Valid BMP File");
int reserved = read4Bytes("Reserved", is, "Not a Valid BMP File");
int bitmapDataOffset = read4Bytes("Bitmap Data Offset", is, "Not a Valid BMP File");
int bitmapHeaderSize = read4Bytes("Bitmap Header Size", is, "Not a Valid BMP File");
int width = read4Bytes("Width", is, "Not a Valid BMP File");
int height = read4Bytes("Height", is, "Not a Valid BMP File");
int planes = read2Bytes("Planes", is, "Not a Valid BMP File");
int bitsPerPixel = read2Bytes("Bits Per Pixel", is, "Not a Valid BMP File");
int compression = read4Bytes("Compression", is, "Not a Valid BMP File");
int bitmapDataSize = read4Bytes("Bitmap Data Size", is, "Not a Valid BMP File");
int hResolution = read4Bytes("HResolution", is, "Not a Valid BMP File");
int vResolution = read4Bytes("VResolution", is, "Not a Valid BMP File");
int colorsUsed = read4Bytes("ColorsUsed", is, "Not a Valid BMP File");
int colorsImportant = read4Bytes("ColorsImportant", is, "Not a Valid BMP File");
if (verbose) {
debugNumber("identifier1", identifier1, 1);
debugNumber("identifier2", identifier2, 1);
debugNumber("fileSize", fileSize, 4);
debugNumber("reserved", reserved, 4);
debugNumber("bitmapDataOffset", bitmapDataOffset, 4);
debugNumber("bitmapHeaderSize", bitmapHeaderSize, 4);
debugNumber("width", width, 4);
debugNumber("height", height, 4);
debugNumber("planes", planes, 2);
debugNumber("bitsPerPixel", bitsPerPixel, 2);
debugNumber("compression", compression, 4);
debugNumber("bitmapDataSize", bitmapDataSize, 4);
debugNumber("hResolution", hResolution, 4);
debugNumber("vResolution", vResolution, 4);
debugNumber("colorsUsed", colorsUsed, 4);
debugNumber("colorsImportant", colorsImportant, 4);
}
BmpHeaderInfo result = new BmpHeaderInfo(identifier1, identifier2, fileSize, reserved, bitmapDataOffset, bitmapHeaderSize, width, height, planes, bitsPerPixel, compression, bitmapDataSize, hResolution, vResolution, colorsUsed, colorsImportant);
return result;
}
private byte[] getRLEBytes(InputStream is, int RLESamplesPerByte) throws ImageReadException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
boolean done = false;
while (!done) {
int a = 0xFF & readByte("RLE a", is, "BMP: Bad RLE");
baos.write(a);
int b = 0xFF & readByte("RLE b", is, "BMP: Bad RLE");
baos.write(b);
if (a == 0) {
int c, d;
switch (b) {
case 0:
continue;
case 1:
done = true;
continue;
case 2:
c = 0xFF & readByte("RLE c", is, "BMP: Bad RLE");
baos.write(c);
d = 0xFF & readByte("RLE d", is, "BMP: Bad RLE");
baos.write(d);
continue;
}
int size = b / RLESamplesPerByte;
if (b % RLESamplesPerByte > 0)
size++;
if (size % 2 != 0)
size++;
byte[] bytes = readByteArray("bytes", size, is, "RLE: Absolute Mode");
baos.write(bytes);
}
}
return baos.toByteArray();
}
private ImageContents readImageContents(InputStream is, FormatCompliance formatCompliance, boolean verbose) throws ImageReadException, IOException {
int i;
byte[] imageData;
PixelParser pixelParser;
BmpHeaderInfo bhi = readBmpHeaderInfo(is, formatCompliance, verbose);
int colorTableSize = bhi.colorsUsed;
if (colorTableSize == 0)
colorTableSize = 1 << bhi.bitsPerPixel;
if (verbose) {
debugNumber("ColorsUsed", bhi.colorsUsed, 4);
debugNumber("BitsPerPixel", bhi.bitsPerPixel, 4);
debugNumber("ColorTableSize", colorTableSize, 4);
debugNumber("bhi.colorsUsed", bhi.colorsUsed, 4);
debugNumber("Compression", bhi.compression, 4);
}
int rleSamplesPerByte = 0;
boolean rle = false;
switch (bhi.compression) {
case 0:
if (verbose)
System.out.println("Compression: BI_RGB");
if (bhi.bitsPerPixel <= 8) {
int paletteLength = 4 * colorTableSize;
} else {
int paletteLength = 0;
}
break;
case 2:
if (verbose)
System.out.println("Compression: BI_RLE4");
i = 4 * colorTableSize;
rleSamplesPerByte = 2;
rle = true;
break;
case 1:
if (verbose)
System.out.println("Compression: BI_RLE8");
i = 4 * colorTableSize;
rleSamplesPerByte = 1;
rle = true;
break;
case 3:
if (verbose)
System.out.println("Compression: BI_BITFIELDS");
i = 12;
break;
default:
throw new ImageReadException("BMP: Unknown Compression: " + bhi.compression);
}
byte[] colorTable = null;
if (i > 0)
colorTable = readByteArray("ColorTable", i, is, "Not a Valid BMP File");
if (verbose) {
debugNumber("paletteLength", i, 4);
System.out.println("ColorTable: " + ((colorTable == null) ? "null" : ("" + colorTable.length)));
}
int pixelCount = bhi.width * bhi.height;
int imageLineLength = (bhi.bitsPerPixel * bhi.width + 7) / 8;
if (verbose) {
debugNumber("bhi.Width", bhi.width, 4);
debugNumber("bhi.Height", bhi.height, 4);
debugNumber("ImageLineLength", imageLineLength, 4);
debugNumber("PixelCount", pixelCount, 4);
}
while (imageLineLength % 4 != 0)
imageLineLength++;
int headerSize = 54;
int expectedDataOffset = 54 + i;
if (verbose) {
debugNumber("bhi.BitmapDataOffset", bhi.bitmapDataOffset, 4);
debugNumber("expectedDataOffset", expectedDataOffset, 4);
}
int extraBytes = bhi.bitmapDataOffset - expectedDataOffset;
if (extraBytes < 0)
throw new ImageReadException("BMP has invalid image data offset: " + bhi.bitmapDataOffset + " (expected: " + expectedDataOffset + ", paletteLength: " + i + ", headerSize: " + '6' + ")");
if (extraBytes > 0)
readByteArray("BitmapDataOffset", extraBytes, is, "Not a Valid BMP File");
int imageDataSize = bhi.height * imageLineLength;
if (verbose)
debugNumber("imageDataSize", imageDataSize, 4);
if (rle) {
imageData = getRLEBytes(is, rleSamplesPerByte);
} else {
imageData = readByteArray("ImageData", imageDataSize, is, "Not a Valid BMP File");
}
if (verbose)
debugNumber("ImageData.length", imageData.length, 4);
switch (bhi.compression) {
case 1:
case 2:
pixelParser = new PixelParserRle(bhi, colorTable, imageData);
break;
case 0:
pixelParser = new PixelParserRgb(bhi, colorTable, imageData);
break;
case 3:
pixelParser = new PixelParserBitFields(bhi, colorTable, imageData);
break;
default:
throw new ImageReadException("BMP: Unknown Compression: " + bhi.compression);
}
return new ImageContents(bhi, colorTable, imageData, pixelParser);
}
private BmpHeaderInfo readBmpHeaderInfo(ByteSource byteSource, boolean verbose) throws ImageReadException, IOException {
InputStream is = null;
try {
is = byteSource.getInputStream();
return readBmpHeaderInfo(is, null, verbose);
} finally {
try {
is.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
public byte[] getICCProfileBytes(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
public Dimension getImageSize(ByteSource byteSource, Map params) throws ImageReadException, IOException {
params = (params == null) ? new HashMap() : new HashMap(params);
boolean verbose = ParamMap.getParamBoolean(params, "VERBOSE", false);
if (params.containsKey("VERBOSE"))
params.remove("VERBOSE");
if (params.size() > 0) {
Object firstKey = params.keySet().iterator().next();
throw new ImageReadException("Unknown parameter: " + firstKey);
}
BmpHeaderInfo bhi = readBmpHeaderInfo(byteSource, verbose);
if (bhi == null)
throw new ImageReadException("BMP: couldn't read header");
return new Dimension(bhi.width, bhi.height);
}
public byte[] embedICCProfile(byte[] image, byte[] profile) {
return null;
}
public boolean embedICCProfile(File src, File dst, byte[] profile) {
return false;
}
public IImageMetadata getMetadata(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
private String getBmpTypeDescription(int Identifier1, int Identifier2) {
if (Identifier1 == 66 && Identifier2 == 77)
return "Windows 3.1x, 95, NT,";
if (Identifier1 == 66 && Identifier2 == 65)
return "OS/2 Bitmap Array";
if (Identifier1 == 67 && Identifier2 == 73)
return "OS/2 Color Icon";
if (Identifier1 == 67 && Identifier2 == 80)
return "OS/2 Color Pointer";
if (Identifier1 == 73 && Identifier2 == 67)
return "OS/2 Icon";
if (Identifier1 == 80 && Identifier2 == 84)
return "OS/2 Pointer";
return "Unknown";
}
public ImageInfo getImageInfo(ByteSource byteSource, Map params) throws ImageReadException, IOException {
params = (params == null) ? new HashMap() : new HashMap(params);
boolean verbose = ParamMap.getParamBoolean(params, "VERBOSE", false);
if (params.containsKey("VERBOSE"))
params.remove("VERBOSE");
if (params.size() > 0) {
Object firstKey = params.keySet().iterator().next();
throw new ImageReadException("Unknown parameter: " + firstKey);
}
ImageContents ic = readImageContents(byteSource.getInputStream(), FormatCompliance.getDefault(), verbose);
if (ic == null)
throw new ImageReadException("Couldn't read BMP Data");
BmpHeaderInfo bhi = ic.bhi;
byte[] colorTable = ic.colorTable;
if (bhi == null)
throw new ImageReadException("BMP: couldn't read header");
int height = bhi.height;
int width = bhi.width;
ArrayList comments = new ArrayList();
int bitsPerPixel = bhi.bitsPerPixel;
ImageFormat format = ImageFormat.IMAGE_FORMAT_BMP;
String name = "BMP Windows Bitmap";
String mimeType = "image/x-ms-bmp";
int numberOfImages = -1;
boolean isProgressive = false;
int physicalWidthDpi = (int)((double)bhi.hResolution * 1000.0D / 2.54D);
float physicalWidthInch = (float)((double)width / (double)physicalWidthDpi);
int physicalHeightDpi = (int)((double)bhi.vResolution * 1000.0D / 2.54D);
float physicalHeightInch = (float)((double)height / (double)physicalHeightDpi);
String formatDetails = "Bmp (" + (char)bhi.identifier1 + (char)bhi.identifier2 + ": " + getBmpTypeDescription(bhi.identifier1, bhi.identifier2) + ")";
boolean isTransparent = false;
boolean usesPalette = (colorTable != null);
int colorType = 2;
String compressionAlgorithm = "RLE: Run-Length Encoding";
ImageInfo result = new ImageInfo(formatDetails, bitsPerPixel, comments, format, name, height, mimeType, numberOfImages, physicalHeightDpi, physicalHeightInch, physicalWidthDpi, physicalWidthInch, width, isProgressive, isTransparent, usesPalette, colorType, compressionAlgorithm);
return result;
}
public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource) throws ImageReadException, IOException {
pw.println("bmp.dumpImageFile");
ImageInfo imageData = getImageInfo(byteSource, null);
if (imageData == null)
return false;
imageData.toString(pw, "");
pw.println("");
return true;
}
public FormatCompliance getFormatCompliance(ByteSource byteSource) throws ImageReadException, IOException {
boolean verbose = false;
FormatCompliance result = new FormatCompliance(byteSource.getDescription());
readImageContents(byteSource.getInputStream(), result, verbose);
return result;
}
public BufferedImage getBufferedImage(ByteSource byteSource, Map params) throws ImageReadException, IOException {
params = (params == null) ? new HashMap() : new HashMap(params);
boolean verbose = ParamMap.getParamBoolean(params, "VERBOSE", false);
if (params.containsKey("VERBOSE"))
params.remove("VERBOSE");
if (params.containsKey("BUFFERED_IMAGE_FACTORY"))
params.remove("BUFFERED_IMAGE_FACTORY");
if (params.size() > 0) {
Object firstKey = params.keySet().iterator().next();
throw new ImageReadException("Unknown parameter: " + firstKey);
}
ImageContents ic = readImageContents(byteSource.getInputStream(), FormatCompliance.getDefault(), verbose);
if (ic == null)
throw new ImageReadException("Couldn't read BMP Data");
BmpHeaderInfo bhi = ic.bhi;
int width = bhi.width;
int height = bhi.height;
boolean hasAlpha = false;
BufferedImage result = getBufferedImageFactory(params).getColorBufferedImage(width, height, hasAlpha);
if (verbose) {
System.out.println("width: " + width);
System.out.println("height: " + height);
System.out.println("width*height: " + (width * height));
System.out.println("width*height*4: " + (width * height * 4));
}
PixelParser pixelParser = ic.pixelParser;
pixelParser.processImage(result);
return result;
}
public void writeImage(BufferedImage src, OutputStream os, Map params) throws ImageWriteException, IOException {
params = (params == null) ? new HashMap() : new HashMap(params);
if (params.containsKey("FORMAT"))
params.remove("FORMAT");
if (params.size() > 0) {
Object firstKey = params.keySet().iterator().next();
throw new ImageWriteException("Unknown parameter: " + firstKey);
}
SimplePalette palette = new PaletteFactory().makePaletteSimple(src, 256);
BMPWriter writer = null;
if (palette == null) {
writer = new BMPWriterRGB();
} else {
writer = new BMPWriterPalette(palette);
}
byte[] imagedata = writer.getImageData(src);
BinaryOutputStream bos = new BinaryOutputStream(os, 73);
os.write(66);
os.write(77);
int filesize = 54 + 4 * writer.getPaletteSize() + imagedata.length;
bos.write4Bytes(filesize);
bos.write4Bytes(0);
bos.write4Bytes(54 + 4 * writer.getPaletteSize());
int width = src.getWidth();
int height = src.getHeight();
bos.write4Bytes(40);
bos.write4Bytes(width);
bos.write4Bytes(height);
bos.write2Bytes(1);
bos.write2Bytes(writer.getBitsPerPixel());
bos.write4Bytes(0);
bos.write4Bytes(imagedata.length);
bos.write4Bytes(0);
bos.write4Bytes(0);
if (palette == null) {
bos.write4Bytes(0);
} else {
bos.write4Bytes(palette.length());
}
bos.write4Bytes(0);
writer.writePalette(bos);
bos.writeByteArray(imagedata);
}
public String getXmpXml(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
}

View file

@ -0,0 +1,20 @@
package org.apache.sanselan.formats.bmp;
import org.apache.sanselan.formats.bmp.pixelparsers.PixelParser;
class ImageContents {
public final BmpHeaderInfo bhi;
public final byte[] colorTable;
public final byte[] imageData;
public final PixelParser pixelParser;
public ImageContents(BmpHeaderInfo bhi, byte[] ColorTable, byte[] ImageData, PixelParser fPixelParser) {
this.bhi = bhi;
this.colorTable = ColorTable;
this.imageData = ImageData;
this.pixelParser = fPixelParser;
}
}

View file

@ -0,0 +1,40 @@
package org.apache.sanselan.formats.bmp.pixelparsers;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.formats.bmp.BmpHeaderInfo;
public abstract class PixelParser {
public final BmpHeaderInfo bhi;
public final byte[] colorTable;
public final byte[] imageData;
protected final BinaryFileParser bfp;
protected final ByteArrayInputStream is;
public PixelParser(BmpHeaderInfo bhi, byte[] ColorTable, byte[] ImageData) {
this.bhi = bhi;
this.colorTable = ColorTable;
this.imageData = ImageData;
this.bfp = new BinaryFileParser(73);
this.is = new ByteArrayInputStream(ImageData);
}
public abstract void processImage(BufferedImage paramBufferedImage) throws ImageReadException, IOException;
protected int getColorTableRGB(int index) {
index *= 4;
int blue = 0xFF & this.colorTable[index + 0];
int green = 0xFF & this.colorTable[index + 1];
int red = 0xFF & this.colorTable[index + 2];
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
return rgb;
}
}

View file

@ -0,0 +1,83 @@
package org.apache.sanselan.formats.bmp.pixelparsers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.formats.bmp.BmpHeaderInfo;
public class PixelParserBitFields extends PixelParserSimple {
private final int redShift;
private final int greenShift;
private final int blueShift;
private final int redMask;
private final int greenMask;
private final int blueMask;
public PixelParserBitFields(BmpHeaderInfo bhi, byte[] ColorTable, byte[] ImageData) throws ImageReadException, IOException {
super(bhi, ColorTable, ImageData);
InputStream bais = new ByteArrayInputStream(ColorTable);
this.redMask = this.bfp.read4Bytes("redMask", bais, "BMP BI_BITFIELDS Bad Color Table");
this.greenMask = this.bfp.read4Bytes("greenMask", bais, "BMP BI_BITFIELDS Bad Color Table");
this.blueMask = this.bfp.read4Bytes("blueMask", bais, "BMP BI_BITFIELDS Bad Color Table");
this.redShift = getMaskShift(this.redMask);
this.greenShift = getMaskShift(this.greenMask);
this.blueShift = getMaskShift(this.blueMask);
}
private int getMaskShift(int mask) {
int trailingZeroes = 0;
while ((0x1 & mask) == 0) {
mask = Integer.MAX_VALUE & mask >> 1;
trailingZeroes++;
}
int maskLength = 0;
while ((0x1 & mask) == 1) {
mask = Integer.MAX_VALUE & mask >> 1;
maskLength++;
}
return trailingZeroes - (8 - maskLength);
}
private int bytecount = 0;
public int getNextRGB() throws ImageReadException, IOException {
int data;
if (this.bhi.bitsPerPixel == 8) {
data = 0xFF & this.imageData[this.bytecount + 0];
this.bytecount++;
} else if (this.bhi.bitsPerPixel == 24) {
data = this.bfp.read3Bytes("Pixel", this.is, "BMP Image Data");
this.bytecount += 3;
} else if (this.bhi.bitsPerPixel == 32) {
data = this.bfp.read4Bytes("Pixel", this.is, "BMP Image Data");
this.bytecount += 4;
} else if (this.bhi.bitsPerPixel == 16) {
data = this.bfp.read2Bytes("Pixel", this.is, "BMP Image Data");
this.bytecount += 2;
} else {
throw new ImageReadException("Unknown BitsPerPixel: " + this.bhi.bitsPerPixel);
}
int red = this.redMask & data;
int green = this.greenMask & data;
int blue = this.blueMask & data;
red = (this.redShift >= 0) ? (red >> this.redShift) : (red << -this.redShift);
green = (this.greenShift >= 0) ? (green >> this.greenShift) : (green << -this.greenShift);
blue = (this.blueShift >= 0) ? (blue >> this.blueShift) : (blue << -this.blueShift);
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
return rgb;
}
public void newline() throws ImageReadException, IOException {
while (this.bytecount % 4 != 0) {
this.bfp.readByte("Pixel", this.is, "BMP Image Data");
this.bytecount++;
}
}
}

View file

@ -0,0 +1,81 @@
package org.apache.sanselan.formats.bmp.pixelparsers;
import java.io.IOException;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.formats.bmp.BmpHeaderInfo;
public class PixelParserRgb extends PixelParserSimple {
public PixelParserRgb(BmpHeaderInfo bhi, byte[] ColorTable, byte[] ImageData) {
super(bhi, ColorTable, ImageData);
}
private int bytecount = 0;
private int cached_bit_count = 0;
private int cached_byte = 0;
int pixelCount = 0;
public int getNextRGB() throws ImageReadException, IOException {
this.pixelCount++;
if (this.bhi.bitsPerPixel == 1 || this.bhi.bitsPerPixel == 4) {
if (this.cached_bit_count < this.bhi.bitsPerPixel) {
if (this.cached_bit_count != 0)
throw new ImageReadException("Unexpected leftover bits: " + this.cached_bit_count + "/" + this.bhi.bitsPerPixel);
this.cached_bit_count += 8;
this.cached_byte = 0xFF & this.imageData[this.bytecount];
this.bytecount++;
}
int cache_mask = (1 << this.bhi.bitsPerPixel) - 1;
int sample = cache_mask & this.cached_byte >> 8 - this.bhi.bitsPerPixel;
this.cached_byte = 0xFF & this.cached_byte << this.bhi.bitsPerPixel;
this.cached_bit_count -= this.bhi.bitsPerPixel;
int rgb = getColorTableRGB(sample);
return rgb;
}
if (this.bhi.bitsPerPixel == 8) {
int sample = 0xFF & this.imageData[this.bytecount + 0];
int rgb = getColorTableRGB(sample);
this.bytecount++;
return rgb;
}
if (this.bhi.bitsPerPixel == 16) {
int data = this.bfp.read2Bytes("Pixel", this.is, "BMP Image Data");
int blue = (0x1F & data >> 0) << 3;
int green = (0x1F & data >> 5) << 3;
int red = (0x1F & data >> 10) << 3;
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
this.bytecount += 2;
return rgb;
}
if (this.bhi.bitsPerPixel == 24) {
int blue = 0xFF & this.imageData[this.bytecount + 0];
int green = 0xFF & this.imageData[this.bytecount + 1];
int red = 0xFF & this.imageData[this.bytecount + 2];
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
this.bytecount += 3;
return rgb;
}
if (this.bhi.bitsPerPixel == 32) {
int blue = 0xFF & this.imageData[this.bytecount + 0];
int green = 0xFF & this.imageData[this.bytecount + 1];
int red = 0xFF & this.imageData[this.bytecount + 2];
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
this.bytecount += 4;
return rgb;
}
throw new ImageReadException("Unknown BitsPerPixel: " + this.bhi.bitsPerPixel);
}
public void newline() throws ImageReadException, IOException {
this.cached_bit_count = 0;
while (this.bytecount % 4 != 0) {
this.bfp.readByte("Pixel", this.is, "BMP Image Data");
this.bytecount++;
}
}
}

View file

@ -0,0 +1,101 @@
package org.apache.sanselan.formats.bmp.pixelparsers;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.IOException;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.formats.bmp.BmpHeaderInfo;
public class PixelParserRle extends PixelParser {
public PixelParserRle(BmpHeaderInfo bhi, byte[] ColorTable, byte[] ImageData) {
super(bhi, ColorTable, ImageData);
}
private int getSamplesPerByte() throws ImageReadException, IOException {
if (this.bhi.bitsPerPixel == 8)
return 1;
if (this.bhi.bitsPerPixel == 4)
return 2;
throw new ImageReadException("BMP RLE: bad BitsPerPixel: " + this.bhi.bitsPerPixel);
}
private int[] convertDataToSamples(int data) throws ImageReadException, IOException {
int[] rgbs;
if (this.bhi.bitsPerPixel == 8) {
rgbs = new int[1];
rgbs[0] = getColorTableRGB(data);
} else if (this.bhi.bitsPerPixel == 4) {
rgbs = new int[2];
int sample1 = data >> 4;
int sample2 = 0xF & data;
rgbs[0] = getColorTableRGB(sample1);
rgbs[1] = getColorTableRGB(sample2);
} else {
throw new ImageReadException("BMP RLE: bad BitsPerPixel: " + this.bhi.bitsPerPixel);
}
return rgbs;
}
private int processByteOfData(int[] rgbs, int repeat, int x, int y, int width, int height, DataBuffer db, BufferedImage bi) throws ImageReadException {
int pixels_written = 0;
for (int i = 0; i < repeat; i++) {
if (x >= 0 && x < width && y >= 0 && y < height) {
int rgb = rgbs[i % rgbs.length];
db.setElem(y * this.bhi.width + x, rgb);
} else {
System.out.println("skipping bad pixel (" + x + "," + y + ")");
}
x++;
pixels_written++;
}
return pixels_written;
}
public void processImage(BufferedImage bi) throws ImageReadException, IOException {
DataBuffer db = bi.getRaster().getDataBuffer();
int count = 0;
int width = this.bhi.width;
int height = this.bhi.height;
int x = 0, y = height - 1;
boolean done = false;
while (!done) {
count++;
int a = 0xFF & this.bfp.readByte("RLE (" + x + "," + y + ") a", this.is, "BMP: Bad RLE");
int b = 0xFF & this.bfp.readByte("RLE (" + x + "," + y + ") b", this.is, "BMP: Bad RLE");
if (a == 0) {
int c, d;
switch (b) {
case 0:
y--;
x = 0;
continue;
case 1:
done = true;
continue;
case 2:
c = 0xFF & this.bfp.readByte("RLE c", this.is, "BMP: Bad RLE");
d = 0xFF & this.bfp.readByte("RLE d", this.is, "BMP: Bad RLE");
continue;
}
int SamplesPerByte = getSamplesPerByte();
int size = b / SamplesPerByte;
if (b % SamplesPerByte > 0)
size++;
if (size % 2 != 0)
size++;
byte[] bytes = this.bfp.readByteArray("bytes", size, this.is, "RLE: Absolute Mode");
int remaining = b;
for (int i = 0; remaining > 0; i++) {
int[] samples = convertDataToSamples(0xFF & bytes[i]);
int towrite = Math.min(remaining, SamplesPerByte);
int written = processByteOfData(samples, towrite, x, y, width, height, db, bi);
x += written;
remaining -= written;
}
continue;
}
int[] rgbs = convertDataToSamples(b);
x += processByteOfData(rgbs, a, x, y, width, height, db, bi);
}
}
}

View file

@ -0,0 +1,26 @@
package org.apache.sanselan.formats.bmp.pixelparsers;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.formats.bmp.BmpHeaderInfo;
public abstract class PixelParserSimple extends PixelParser {
public PixelParserSimple(BmpHeaderInfo bhi, byte[] ColorTable, byte[] ImageData) {
super(bhi, ColorTable, ImageData);
}
public abstract int getNextRGB() throws ImageReadException, IOException;
public abstract void newline() throws ImageReadException, IOException;
public void processImage(BufferedImage bi) throws ImageReadException, IOException {
for (int y = this.bhi.height - 1; y >= 0; y--) {
for (int x = 0; x < this.bhi.width; x++) {
int rgb = getNextRGB();
bi.setRGB(x, y, rgb);
}
newline();
}
}
}

View file

@ -0,0 +1,15 @@
package org.apache.sanselan.formats.bmp.writers;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.apache.sanselan.common.BinaryOutputStream;
public abstract class BMPWriter {
public abstract int getPaletteSize();
public abstract int getBitsPerPixel();
public abstract void writePalette(BinaryOutputStream paramBinaryOutputStream) throws IOException;
public abstract byte[] getImageData(BufferedImage paramBufferedImage);
}

View file

@ -0,0 +1,86 @@
package org.apache.sanselan.formats.bmp.writers;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.sanselan.common.BinaryOutputStream;
import org.apache.sanselan.palette.SimplePalette;
public class BMPWriterPalette extends BMPWriter {
private final SimplePalette palette;
private final int bitsPerSample;
public BMPWriterPalette(SimplePalette palette) {
this.palette = palette;
if (palette.length() <= 2) {
this.bitsPerSample = 1;
} else if (palette.length() <= 16) {
this.bitsPerSample = 4;
} else {
this.bitsPerSample = 8;
}
}
public int getPaletteSize() {
return this.palette.length();
}
public int getBitsPerPixel() {
return this.bitsPerSample;
}
public void writePalette(BinaryOutputStream bos) throws IOException {
for (int i = 0; i < this.palette.length(); i++) {
int rgb = this.palette.getEntry(i);
int red = 0xFF & rgb >> 16;
int green = 0xFF & rgb >> 8;
int blue = 0xFF & rgb >> 0;
bos.write(blue);
bos.write(green);
bos.write(red);
bos.write(0);
}
}
public byte[] getImageData(BufferedImage src) {
int width = src.getWidth();
int height = src.getHeight();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bit_cache = 0;
int bits_in_cache = 0;
int bytecount = 0;
for (int y = height - 1; y >= 0; y--) {
for (int x = 0; x < width; x++) {
int argb = src.getRGB(x, y);
int rgb = 0xFFFFFF & argb;
int index = this.palette.getPaletteIndex(rgb);
if (this.bitsPerSample == 8) {
baos.write(0xFF & index);
bytecount++;
} else {
bit_cache = bit_cache << this.bitsPerSample | index;
bits_in_cache += this.bitsPerSample;
if (bits_in_cache >= 8) {
baos.write(0xFF & bit_cache);
bytecount++;
bit_cache = 0;
bits_in_cache = 0;
}
}
}
if (bits_in_cache > 0) {
bit_cache <<= 8 - bits_in_cache;
baos.write(0xFF & bit_cache);
bytecount++;
bit_cache = 0;
bits_in_cache = 0;
}
while (bytecount % 4 != 0) {
baos.write(0);
bytecount++;
}
}
return baos.toByteArray();
}
}

View file

@ -0,0 +1,43 @@
package org.apache.sanselan.formats.bmp.writers;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.sanselan.common.BinaryOutputStream;
public class BMPWriterRGB extends BMPWriter {
public int getPaletteSize() {
return 0;
}
public int getBitsPerPixel() {
return 24;
}
public void writePalette(BinaryOutputStream bos) throws IOException {}
public byte[] getImageData(BufferedImage src) {
int width = src.getWidth();
int height = src.getHeight();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bytecount = 0;
for (int y = height - 1; y >= 0; y--) {
for (int x = 0; x < width; x++) {
int argb = src.getRGB(x, y);
int rgb = 0xFFFFFF & argb;
int red = 0xFF & rgb >> 16;
int green = 0xFF & rgb >> 8;
int blue = 0xFF & rgb >> 0;
baos.write(blue);
baos.write(green);
baos.write(red);
bytecount += 3;
}
while (bytecount % 4 != 0) {
baos.write(0);
bytecount++;
}
}
return baos.toByteArray();
}
}

View file

@ -0,0 +1,9 @@
package org.apache.sanselan.formats.gif;
class GIFBlock {
public final int blockCode;
public GIFBlock(int blockCode) {
this.blockCode = blockCode;
}
}

View file

@ -0,0 +1,51 @@
package org.apache.sanselan.formats.gif;
class GIFHeaderInfo {
public final byte identifier1;
public final byte identifier2;
public final byte identifier3;
public final byte version1;
public final byte version2;
public final byte version3;
public final int logicalScreenWidth;
public final int logicalScreenHeight;
public final byte packedFields;
public final byte backgroundColorIndex;
public final byte pixelAspectRatio;
public final boolean globalColorTableFlag;
public final byte colorResolution;
public final boolean sortFlag;
public final byte sizeOfGlobalColorTable;
public GIFHeaderInfo(byte Identifier1, byte Identifier2, byte Identifier3, byte Version1, byte Version2, byte Version3, int LogicalScreenWidth, int LogicalScreenHeight, byte PackedFields, byte BackgroundColorIndex, byte PixelAspectRatio, boolean GlobalColorTableFlag, byte ColorResolution, boolean SortFlag, byte SizeofGlobalColorTable) {
this.identifier1 = Identifier1;
this.identifier2 = Identifier2;
this.identifier3 = Identifier3;
this.version1 = Version1;
this.version2 = Version2;
this.version3 = Version3;
this.logicalScreenWidth = LogicalScreenWidth;
this.logicalScreenHeight = LogicalScreenHeight;
this.packedFields = PackedFields;
this.backgroundColorIndex = BackgroundColorIndex;
this.pixelAspectRatio = PixelAspectRatio;
this.globalColorTableFlag = GlobalColorTableFlag;
this.colorResolution = ColorResolution;
this.sortFlag = SortFlag;
this.sizeOfGlobalColorTable = SizeofGlobalColorTable;
}
}

View file

@ -0,0 +1,29 @@
package org.apache.sanselan.formats.gif;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
class GenericGIFBlock extends GIFBlock {
public final ArrayList subblocks;
public GenericGIFBlock(int blockCode, ArrayList subblocks) {
super(blockCode);
this.subblocks = subblocks;
}
public byte[] appendSubBlocks() throws IOException {
return appendSubBlocks(false);
}
public byte[] appendSubBlocks(boolean includeLengths) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int i = 0; i < this.subblocks.size(); i++) {
byte[] subblock = (byte[])this.subblocks.get(i);
if (includeLengths && i > 0)
out.write(subblock.length);
out.write(subblock);
}
return out.toByteArray();
}
}

View file

@ -0,0 +1,713 @@
package org.apache.sanselan.formats.gif;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.sanselan.FormatCompliance;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryOutputStream;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.mylzw.MyLZWCompressor;
import org.apache.sanselan.common.mylzw.MyLZWDecompressor;
import org.apache.sanselan.palette.Palette;
import org.apache.sanselan.palette.PaletteFactory;
import org.apache.sanselan.util.Debug;
import org.apache.sanselan.util.ParamMap;
public class GifImageParser extends ImageParser {
private static final String DEFAULT_EXTENSION = ".gif";
public GifImageParser() {
setByteOrder(73);
}
public String getName() {
return "Gif-Custom";
}
public String getDefaultExtension() {
return ".gif";
}
private static final String[] ACCEPTED_EXTENSIONS = new String[] { ".gif" };
protected String[] getAcceptedExtensions() {
return ACCEPTED_EXTENSIONS;
}
protected ImageFormat[] getAcceptedTypes() {
return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_GIF };
}
private static final byte[] GIF_HEADER_SIGNATURE = new byte[] { 71, 73, 70 };
private static final int EXTENSION_CODE = 33;
private static final int IMAGE_SEPARATOR = 44;
private static final int GRAPHIC_CONTROL_EXTENSION = 8697;
private static final int COMMENT_EXTENSION = 254;
private static final int PLAIN_TEXT_EXTENSION = 1;
private static final int XMP_EXTENSION = 255;
private static final int TERMINATOR_BYTE = 59;
private static final int APPLICATION_EXTENSION_LABEL = 255;
private static final int XMP_COMPLETE_CODE = 8703;
private static final int LOCAL_COLOR_TABLE_FLAG_MASK = 128;
private static final int INTERLACE_FLAG_MASK = 64;
private static final int SORT_FLAG_MASK = 32;
private GIFHeaderInfo readHeader(InputStream is, FormatCompliance formatCompliance) throws ImageReadException, IOException {
byte identifier1 = readByte("identifier1", is, "Not a Valid GIF File");
byte identifier2 = readByte("identifier2", is, "Not a Valid GIF File");
byte identifier3 = readByte("identifier3", is, "Not a Valid GIF File");
byte version1 = readByte("version1", is, "Not a Valid GIF File");
byte version2 = readByte("version2", is, "Not a Valid GIF File");
byte version3 = readByte("version3", is, "Not a Valid GIF File");
if (formatCompliance != null) {
formatCompliance.compare_bytes("Signature", GIF_HEADER_SIGNATURE, new byte[] { (byte)identifier1, (byte)identifier2, (byte)identifier3 });
formatCompliance.compare("version", 56, version1);
formatCompliance.compare("version", new int[] { 55, 57 }, version2);
formatCompliance.compare("version", 97, version3);
}
if (this.debug)
printCharQuad("identifier: ", identifier1 << 16 | identifier2 << 8 | identifier3 << 0);
if (this.debug)
printCharQuad("version: ", version1 << 16 | version2 << 8 | version3 << 0);
int logicalScreenWidth = read2Bytes("Logical Screen Width", is, "Not a Valid GIF File");
int logicalScreenHeight = read2Bytes("Logical Screen Height", is, "Not a Valid GIF File");
if (formatCompliance != null) {
formatCompliance.checkBounds("Width", 1, Integer.MAX_VALUE, logicalScreenWidth);
formatCompliance.checkBounds("Height", 1, Integer.MAX_VALUE, logicalScreenHeight);
}
byte packedFields = readByte("Packed Fields", is, "Not a Valid GIF File");
byte backgroundColorIndex = readByte("Background Color Index", is, "Not a Valid GIF File");
byte pixelAspectRatio = readByte("Pixel Aspect Ratio", is, "Not a Valid GIF File");
if (this.debug)
printByteBits("PackedFields bits", packedFields);
boolean globalColorTableFlag = ((packedFields & 0x80) > 0);
if (this.debug)
System.out.println("GlobalColorTableFlag: " + globalColorTableFlag);
byte colorResolution = (byte)(packedFields >> 4 & 0x7);
if (this.debug)
System.out.println("ColorResolution: " + colorResolution);
boolean sortFlag = ((packedFields & 0x8) > 0);
if (this.debug)
System.out.println("SortFlag: " + sortFlag);
byte sizeofGlobalColorTable = (byte)(packedFields & 0x7);
if (this.debug)
System.out.println("SizeofGlobalColorTable: " + sizeofGlobalColorTable);
if (formatCompliance != null)
if (globalColorTableFlag && backgroundColorIndex != -1)
formatCompliance.checkBounds("Background Color Index", 0, convertColorTableSize(sizeofGlobalColorTable), backgroundColorIndex);
return new GIFHeaderInfo(identifier1, identifier2, identifier3, version1, version2, version3, logicalScreenWidth, logicalScreenHeight, packedFields, backgroundColorIndex, pixelAspectRatio, globalColorTableFlag, colorResolution, sortFlag, sizeofGlobalColorTable);
}
private GraphicControlExtension readGraphicControlExtension(int code, InputStream is) throws ImageReadException, IOException {
readByte("block_size", is, "GIF: corrupt GraphicControlExt");
int packed = readByte("packed fields", is, "GIF: corrupt GraphicControlExt");
int dispose = (packed & 0x1C) >> 2;
boolean transparency = ((packed & 0x1) != 0);
int delay = read2Bytes("delay in milliseconds", is, "GIF: corrupt GraphicControlExt");
int transparentColorIndex = 0xFF & readByte("transparent color index", is, "GIF: corrupt GraphicControlExt");
readByte("block terminator", is, "GIF: corrupt GraphicControlExt");
return new GraphicControlExtension(code, packed, dispose, transparency, delay, transparentColorIndex);
}
private byte[] readSubBlock(InputStream is) throws ImageReadException, IOException {
int block_size = 0xFF & readByte("block_size", is, "GIF: corrupt block");
byte[] bytes = readByteArray("block", block_size, is, "GIF: corrupt block");
return bytes;
}
protected GenericGIFBlock readGenericGIFBlock(InputStream is, int code) throws ImageReadException, IOException {
return readGenericGIFBlock(is, code, null);
}
protected GenericGIFBlock readGenericGIFBlock(InputStream is, int code, byte[] first) throws ImageReadException, IOException {
ArrayList subblocks = new ArrayList();
if (first != null)
subblocks.add(first);
while (true) {
byte[] bytes = readSubBlock(is);
if (bytes.length < 1)
break;
subblocks.add(bytes);
}
return new GenericGIFBlock(code, subblocks);
}
private ArrayList readBlocks(GIFHeaderInfo ghi, InputStream is, boolean stopBeforeImageData, FormatCompliance formatCompliance) throws ImageReadException, IOException {
int code;
ArrayList result = new ArrayList();
while (true) {
ImageDescriptor id;
int extensionCode, completeCode;
GraphicControlExtension gce;
GenericGIFBlock genericGIFBlock1;
byte[] label;
GenericGIFBlock block;
code = is.read();
switch (code) {
case -1:
throw new ImageReadException("GIF: unexpected end of data");
case 44:
id = readImageDescriptor(ghi, code, is, stopBeforeImageData, formatCompliance);
result.add(id);
continue;
case 33:
extensionCode = is.read();
completeCode = (0xFF & code) << 8 | 0xFF & extensionCode;
switch (extensionCode) {
case 249:
gce = readGraphicControlExtension(completeCode, is);
result.add(gce);
continue;
case 1:
case 254:
genericGIFBlock1 = readGenericGIFBlock(is, completeCode);
result.add(genericGIFBlock1);
continue;
case 255:
label = readSubBlock(is);
if (formatCompliance != null)
formatCompliance.addComment("Unknown Application Extension (" + new String(label) + ")", completeCode);
if (label != null && label.length > 0) {
GenericGIFBlock genericGIFBlock = readGenericGIFBlock(is, completeCode, label);
byte[] bytes = genericGIFBlock.appendSubBlocks();
result.add(genericGIFBlock);
}
continue;
}
if (formatCompliance != null)
formatCompliance.addComment("Unknown block", completeCode);
block = readGenericGIFBlock(is, completeCode);
result.add(block);
continue;
case 59:
return result;
case 0:
continue;
}
break;
}
throw new ImageReadException("GIF: unknown code: " + code);
}
private ImageDescriptor readImageDescriptor(GIFHeaderInfo ghi, int blockCode, InputStream is, boolean stopBeforeImageData, FormatCompliance formatCompliance) throws ImageReadException, IOException {
int ImageLeftPosition = read2Bytes("Image Left Position", is, "Not a Valid GIF File");
int ImageTopPosition = read2Bytes("Image Top Position", is, "Not a Valid GIF File");
int imageWidth = read2Bytes("Image Width", is, "Not a Valid GIF File");
int imageHeight = read2Bytes("Image Height", is, "Not a Valid GIF File");
byte PackedFields = readByte("Packed Fields", is, "Not a Valid GIF File");
if (formatCompliance != null) {
formatCompliance.checkBounds("Width", 1, ghi.logicalScreenWidth, imageWidth);
formatCompliance.checkBounds("Height", 1, ghi.logicalScreenHeight, imageHeight);
formatCompliance.checkBounds("Left Position", 0, ghi.logicalScreenWidth - imageWidth, ImageLeftPosition);
formatCompliance.checkBounds("Top Position", 0, ghi.logicalScreenHeight - imageHeight, ImageTopPosition);
}
if (this.debug)
printByteBits("PackedFields bits", PackedFields);
boolean LocalColorTableFlag = ((PackedFields >> 7 & 0x1) > 0);
if (this.debug)
System.out.println("LocalColorTableFlag: " + LocalColorTableFlag);
boolean InterlaceFlag = ((PackedFields >> 6 & 0x1) > 0);
if (this.debug)
System.out.println("Interlace Flag: " + InterlaceFlag);
boolean SortFlag = ((PackedFields >> 5 & 0x1) > 0);
if (this.debug)
System.out.println("Sort Flag: " + SortFlag);
byte SizeofLocalColorTable = (byte)(PackedFields & 0x7);
if (this.debug)
System.out.println("SizeofLocalColorTable: " + SizeofLocalColorTable);
byte[] LocalColorTable = null;
if (LocalColorTableFlag)
LocalColorTable = readColorTable(is, SizeofLocalColorTable, formatCompliance);
byte[] imageData = null;
if (!stopBeforeImageData) {
int LZWMinimumCodeSize = is.read();
GenericGIFBlock block = readGenericGIFBlock(is, -1);
byte[] bytes = block.appendSubBlocks();
InputStream bais = new ByteArrayInputStream(bytes);
int size = imageWidth * imageHeight;
MyLZWDecompressor myLzwDecompressor = new MyLZWDecompressor(LZWMinimumCodeSize, 73);
imageData = myLzwDecompressor.decompress(bais, size);
} else {
int LZWMinimumCodeSize = is.read();
if (this.debug)
System.out.println("LZWMinimumCodeSize: " + LZWMinimumCodeSize);
readGenericGIFBlock(is, -1);
}
ImageDescriptor result = new ImageDescriptor(blockCode, ImageLeftPosition, ImageTopPosition, imageWidth, imageHeight, PackedFields, LocalColorTableFlag, InterlaceFlag, SortFlag, SizeofLocalColorTable, LocalColorTable, imageData);
return result;
}
private int simple_pow(int base, int power) {
int result = 1;
for (int i = 0; i < power; i++)
result *= base;
return result;
}
private int convertColorTableSize(int ct_size) {
return 3 * simple_pow(2, ct_size + 1);
}
private byte[] readColorTable(InputStream is, int ct_size, FormatCompliance formatCompliance) throws ImageReadException, IOException {
int actual_size = convertColorTableSize(ct_size);
byte[] bytes = readByteArray("block", actual_size, is, "GIF: corrupt Color Table");
return bytes;
}
private GIFHeaderInfo readHeader(ByteSource byteSource) throws ImageReadException, IOException {
InputStream is = null;
try {
is = byteSource.getInputStream();
return readHeader(is, FormatCompliance.getDefault());
} finally {
try {
is.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
private GIFBlock findBlock(ArrayList v, int code) {
for (int i = 0; i < v.size(); i++) {
GIFBlock gifBlock = (GIFBlock)v.get(i);
if (gifBlock.blockCode == code)
return gifBlock;
}
return null;
}
private ImageContents readFile(ByteSource byteSource, boolean stopBeforeImageData) throws ImageReadException, IOException {
return readFile(byteSource, stopBeforeImageData, FormatCompliance.getDefault());
}
private ImageContents readFile(ByteSource byteSource, boolean stopBeforeImageData, FormatCompliance formatCompliance) throws ImageReadException, IOException {
InputStream is = null;
try {
is = byteSource.getInputStream();
GIFHeaderInfo ghi = readHeader(is, formatCompliance);
byte[] globalColorTable = null;
if (ghi.globalColorTableFlag)
globalColorTable = readColorTable(is, ghi.sizeOfGlobalColorTable, formatCompliance);
ArrayList blocks = readBlocks(ghi, is, stopBeforeImageData, formatCompliance);
ImageContents result = new ImageContents(ghi, globalColorTable, blocks);
return result;
} finally {
try {
is.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
public byte[] getICCProfileBytes(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
public Dimension getImageSize(ByteSource byteSource, Map params) throws ImageReadException, IOException {
GIFHeaderInfo bhi = readHeader(byteSource);
if (bhi == null)
throw new ImageReadException("GIF: Couldn't read Header");
return new Dimension(bhi.logicalScreenWidth, bhi.logicalScreenHeight);
}
public byte[] embedICCProfile(byte[] image, byte[] profile) {
return null;
}
public boolean embedICCProfile(File src, File dst, byte[] profile) {
return false;
}
public IImageMetadata getMetadata(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
private ArrayList getComments(ArrayList v) throws ImageReadException, IOException {
ArrayList result = new ArrayList();
int code = 8702;
for (int i = 0; i < v.size(); i++) {
GIFBlock block = (GIFBlock)v.get(i);
if (block.blockCode == code) {
byte[] bytes = ((GenericGIFBlock)block).appendSubBlocks();
result.add(new String(bytes));
}
}
return result;
}
public ImageInfo getImageInfo(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ImageContents blocks = readFile(byteSource, false);
if (blocks == null)
throw new ImageReadException("GIF: Couldn't read blocks");
GIFHeaderInfo bhi = blocks.gifHeaderInfo;
if (bhi == null)
throw new ImageReadException("GIF: Couldn't read Header");
ImageDescriptor id = (ImageDescriptor)findBlock(blocks.blocks, 44);
if (id == null)
throw new ImageReadException("GIF: Couldn't read ImageDescriptor");
GraphicControlExtension gce = (GraphicControlExtension)findBlock(blocks.blocks, 8697);
int Height = bhi.logicalScreenHeight;
int Width = bhi.logicalScreenWidth;
ArrayList Comments = getComments(blocks.blocks);
int BitsPerPixel = (bhi.colorResolution + 1) * 3;
ImageFormat Format = ImageFormat.IMAGE_FORMAT_GIF;
String FormatName = "GIF Graphics Interchange Format";
String MimeType = "image/gif";
int NumberOfImages = -1;
boolean isProgressive = id.interlaceFlag;
int PhysicalWidthDpi = 72;
float PhysicalWidthInch = (float)((double)Width / (double)PhysicalWidthDpi);
int PhysicalHeightDpi = 72;
float PhysicalHeightInch = (float)((double)Height / (double)PhysicalHeightDpi);
String FormatDetails = "Gif " + (char)blocks.gifHeaderInfo.version1 + (char)blocks.gifHeaderInfo.version2 + (char)blocks.gifHeaderInfo.version3;
boolean isTransparent = false;
if (gce != null && gce.transparency)
isTransparent = true;
boolean usesPalette = true;
int colorType = 2;
String compressionAlgorithm = "LZW";
ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments, Format, FormatName, Height, MimeType, NumberOfImages, PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi, PhysicalWidthInch, Width, isProgressive, isTransparent, usesPalette, colorType, compressionAlgorithm);
return result;
}
public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource) throws ImageReadException, IOException {
pw.println("gif.dumpImageFile");
ImageInfo imageData = getImageInfo(byteSource);
if (imageData == null)
return false;
imageData.toString(pw, "");
ImageContents blocks = readFile(byteSource, false);
if (blocks == null)
return false;
pw.println("gif.blocks: " + blocks.blocks.size());
for (int i = 0; i < blocks.blocks.size(); i++) {
GIFBlock gifBlock = (GIFBlock)blocks.blocks.get(i);
debugNumber(pw, "\t" + i + " (" + gifBlock.getClass().getName() + ")", gifBlock.blockCode, 4);
}
pw.println("");
return true;
}
private int[] getColorTable(byte[] bytes) throws ImageReadException, IOException {
if (bytes.length % 3 != 0)
throw new ImageReadException("Bad Color Table Length: " + bytes.length);
int length = bytes.length / 3;
int[] result = new int[length];
for (int i = 0; i < length; i++) {
int red = 0xFF & bytes[i * 3 + 0];
int green = 0xFF & bytes[i * 3 + 1];
int blue = 0xFF & bytes[i * 3 + 2];
int alpha = 255;
int rgb = alpha << 24 | red << 16 | green << 8 | blue << 0;
result[i] = rgb;
}
return result;
}
public FormatCompliance getFormatCompliance(ByteSource byteSource) throws ImageReadException, IOException {
FormatCompliance result = new FormatCompliance(byteSource.getDescription());
readFile(byteSource, false, result);
return result;
}
public BufferedImage getBufferedImage(ByteSource byteSource, Map params) throws ImageReadException, IOException {
int[] colortable;
ImageContents imageContents = readFile(byteSource, false);
if (imageContents == null)
throw new ImageReadException("GIF: Couldn't read blocks");
GIFHeaderInfo ghi = imageContents.gifHeaderInfo;
if (ghi == null)
throw new ImageReadException("GIF: Couldn't read Header");
ImageDescriptor id = (ImageDescriptor)findBlock(imageContents.blocks, 44);
if (id == null)
throw new ImageReadException("GIF: Couldn't read Image Descriptor");
GraphicControlExtension gce = (GraphicControlExtension)findBlock(imageContents.blocks, 8697);
int width = ghi.logicalScreenWidth;
int height = ghi.logicalScreenHeight;
boolean hasAlpha = false;
if (gce != null && gce.transparency)
hasAlpha = true;
BufferedImage result = getBufferedImageFactory(params).getColorBufferedImage(width, height, hasAlpha);
if (id.localColorTable != null) {
colortable = getColorTable(id.localColorTable);
} else if (imageContents.globalColorTable != null) {
colortable = getColorTable(imageContents.globalColorTable);
} else {
throw new ImageReadException("Gif: No Color Table");
}
int transparentIndex = -1;
if (hasAlpha)
transparentIndex = gce.transparentColorIndex;
int counter = 0;
int rows_in_pass_1 = (height + 7) / 8;
int rows_in_pass_2 = (height + 3) / 8;
int rows_in_pass_3 = (height + 1) / 4;
int rows_in_pass_4 = height / 2;
DataBuffer db = result.getRaster().getDataBuffer();
for (int row = 0; row < height; row++) {
int y;
if (id.interlaceFlag) {
int the_row = row;
if (the_row < rows_in_pass_1) {
y = the_row * 8;
} else {
the_row -= rows_in_pass_1;
if (the_row < rows_in_pass_2) {
y = 4 + the_row * 8;
} else {
the_row -= rows_in_pass_2;
if (the_row < rows_in_pass_3) {
y = 2 + the_row * 4;
} else {
the_row -= rows_in_pass_3;
if (the_row < rows_in_pass_4) {
y = 1 + the_row * 2;
} else {
throw new ImageReadException("Gif: Strange Row");
}
}
}
}
} else {
y = row;
}
for (int x = 0; x < width; x++) {
int index = 0xFF & id.imageData[counter++];
int rgb = colortable[index];
if (transparentIndex == index)
rgb = 0;
db.setElem(y * width + x, rgb);
}
}
return result;
}
private void writeAsSubBlocks(OutputStream os, byte[] bytes) throws IOException {
int index = 0;
while (index < bytes.length) {
int block_size = Math.min(bytes.length - index, 255);
os.write(block_size);
os.write(bytes, index, block_size);
index += block_size;
}
os.write(0);
}
public void writeImage(BufferedImage src, OutputStream os, Map params) throws ImageWriteException, IOException {
params = new HashMap(params);
boolean verbose = ParamMap.getParamBoolean(params, "VERBOSE", false);
if (params.containsKey("FORMAT"))
params.remove("FORMAT");
if (params.containsKey("VERBOSE"))
params.remove("VERBOSE");
String xmpXml = null;
if (params.containsKey("XMP_XML")) {
xmpXml = (String)params.get("XMP_XML");
params.remove("XMP_XML");
}
if (params.size() > 0) {
Object firstKey = params.keySet().iterator().next();
throw new ImageWriteException("Unknown parameter: " + firstKey);
}
int width = src.getWidth();
int height = src.getHeight();
boolean hasAlpha = new PaletteFactory().hasTransparency(src);
int max_colors = hasAlpha ? 255 : 256;
Palette palette2 = new PaletteFactory().makePaletteSimple(src, max_colors);
if (palette2 == null) {
palette2 = new PaletteFactory().makePaletteQuantized(src, max_colors);
if (verbose)
System.out.println("quantizing");
} else if (verbose) {
System.out.println("exact palette");
}
if (palette2 == null)
throw new ImageWriteException("Gif: can't write images with more than 256 colors");
int palette_size = palette2.length() + (hasAlpha ? 1 : 0);
BinaryOutputStream bos = new BinaryOutputStream(os, 73);
os.write(71);
os.write(73);
os.write(70);
os.write(56);
os.write(57);
os.write(97);
bos.write2Bytes(width);
bos.write2Bytes(height);
int colorTableScaleLessOne = (palette_size > 128) ? 7 : ((palette_size > 64) ? 6 : ((palette_size > 32) ? 5 : ((palette_size > 16) ? 4 : ((palette_size > 8) ? 3 : ((palette_size > 4) ? 2 : ((palette_size > 2) ? 1 : 0))))));
int colorTableSizeInFormat = 1 << colorTableScaleLessOne + 1;
int actual_size = 3 * simple_pow(2, colorTableScaleLessOne + 1);
byte colorResolution = (byte)colorTableScaleLessOne;
boolean globalColorTableFlag = false;
boolean sortFlag = false;
int globalColorTableFlagMask = 128;
int sortFlagMask = 8;
int sizeOfGlobalColorTable = 0;
int j = (globalColorTableFlag ? globalColorTableFlagMask : 0) | (sortFlag ? sortFlagMask : 0) | (0x7 & colorResolution) << 4 | 0x7 & sizeOfGlobalColorTable;
bos.write(j);
byte BackgroundColorIndex = 0;
bos.write(BackgroundColorIndex);
byte PixelAspectRatio = 0;
bos.write(PixelAspectRatio);
bos.write(33);
bos.write(-7);
bos.write(4);
int packedFields = hasAlpha ? 1 : 0;
bos.write((byte)packedFields);
bos.write(0);
bos.write(0);
bos.write((byte)(hasAlpha ? palette2.length() : 0));
bos.write(0);
if (null != xmpXml) {
bos.write(33);
bos.write(255);
bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE.length);
bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE);
byte[] xmpXmlBytes = xmpXml.getBytes("utf-8");
bos.write(xmpXmlBytes);
for (int magic = 0; magic <= 255; magic++)
bos.write(255 - magic);
bos.write(0);
}
bos.write(44);
bos.write2Bytes(0);
bos.write2Bytes(0);
bos.write2Bytes(width);
bos.write2Bytes(height);
boolean LocalColorTableFlag = true;
boolean InterlaceFlag = false;
boolean SortFlag = false;
int SizeOfLocalColorTable = colorTableScaleLessOne;
int PackedFields = (LocalColorTableFlag ? 128 : 0) | (InterlaceFlag ? 64 : 0) | (SortFlag ? 32 : 0) | 0x7 & SizeOfLocalColorTable;
bos.write(PackedFields);
for (int i = 0; i < colorTableSizeInFormat; i++) {
if (i < palette2.length()) {
int rgb = palette2.getEntry(i);
int red = 0xFF & rgb >> 16;
int green = 0xFF & rgb >> 8;
int blue = 0xFF & rgb >> 0;
bos.write(red);
bos.write(green);
bos.write(blue);
} else {
bos.write(0);
bos.write(0);
bos.write(0);
}
}
int image_data_total = 0;
int LZWMinimumCodeSize = colorTableScaleLessOne + 1;
if (LZWMinimumCodeSize < 2)
LZWMinimumCodeSize = 2;
bos.write(LZWMinimumCodeSize);
MyLZWCompressor compressor = new MyLZWCompressor(LZWMinimumCodeSize, 73, false);
byte[] imagedata = new byte[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int index;
int argb = src.getRGB(x, y);
int rgb = 0xFFFFFF & argb;
if (hasAlpha) {
int alpha = 0xFF & argb >> 24;
int alphaThreshold = 255;
if (alpha < 255) {
index = palette2.length();
} else {
index = palette2.getPaletteIndex(rgb);
}
} else {
index = palette2.getPaletteIndex(rgb);
}
imagedata[y * width + x] = (byte)index;
}
}
byte[] compressed = compressor.compress(imagedata);
writeAsSubBlocks(bos, compressed);
image_data_total += compressed.length;
bos.write(59);
bos.close();
os.close();
}
private static final byte[] XMP_APPLICATION_ID_AND_AUTH_CODE = new byte[] {
88, 77, 80, 32, 68, 97, 116, 97, 88, 77,
80 };
public String getXmpXml(ByteSource byteSource, Map params) throws ImageReadException, IOException {
InputStream is = null;
try {
is = byteSource.getInputStream();
FormatCompliance formatCompliance = null;
GIFHeaderInfo ghi = readHeader(is, formatCompliance);
byte[] globalColorTable = null;
if (ghi.globalColorTableFlag)
globalColorTable = readColorTable(is, ghi.sizeOfGlobalColorTable, formatCompliance);
ArrayList blocks = readBlocks(ghi, is, true, formatCompliance);
List result = new ArrayList();
for (int i = 0; i < blocks.size(); i++) {
GIFBlock block = (GIFBlock)blocks.get(i);
if (block.blockCode == 8703) {
GenericGIFBlock genericBlock = (GenericGIFBlock)block;
byte[] blockBytes = genericBlock.appendSubBlocks(true);
if (blockBytes.length >= XMP_APPLICATION_ID_AND_AUTH_CODE.length)
if (compareByteArrays(blockBytes, 0, XMP_APPLICATION_ID_AND_AUTH_CODE, 0, XMP_APPLICATION_ID_AND_AUTH_CODE.length)) {
byte[] GIF_MAGIC_TRAILER = new byte[256];
for (int magic = 0; magic <= 255; magic++)
GIF_MAGIC_TRAILER[magic] = (byte)(255 - magic);
if (blockBytes.length >= XMP_APPLICATION_ID_AND_AUTH_CODE.length + GIF_MAGIC_TRAILER.length) {
if (!compareByteArrays(blockBytes, blockBytes.length - GIF_MAGIC_TRAILER.length, GIF_MAGIC_TRAILER, 0, GIF_MAGIC_TRAILER.length))
throw new ImageReadException("XMP block in GIF missing magic trailer.");
try {
String xml = new String(blockBytes, XMP_APPLICATION_ID_AND_AUTH_CODE.length, blockBytes.length - (XMP_APPLICATION_ID_AND_AUTH_CODE.length + GIF_MAGIC_TRAILER.length), "utf-8");
result.add(xml);
} catch (UnsupportedEncodingException e) {
throw new ImageReadException("Invalid XMP Block in GIF.");
}
}
}
}
}
if (result.size() < 1)
return null;
if (result.size() > 1)
throw new ImageReadException("More than one XMP Block in GIF.");
return (String)result.get(0);
} finally {
try {
is.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
}

View file

@ -0,0 +1,22 @@
package org.apache.sanselan.formats.gif;
class GraphicControlExtension extends GIFBlock {
public final int packed;
public final int dispose;
public final boolean transparency;
public final int delay;
public final int transparentColorIndex;
public GraphicControlExtension(int blockCode, int packed, int dispose, boolean transparency, int delay, int transparentColorIndex) {
super(blockCode);
this.packed = packed;
this.dispose = dispose;
this.transparency = transparency;
this.delay = delay;
this.transparentColorIndex = transparentColorIndex;
}
}

View file

@ -0,0 +1,17 @@
package org.apache.sanselan.formats.gif;
import java.util.ArrayList;
class ImageContents {
public final GIFHeaderInfo gifHeaderInfo;
public final ArrayList blocks;
public final byte[] globalColorTable;
public ImageContents(GIFHeaderInfo gifHeaderInfo, byte[] globalColorTable, ArrayList blocks) {
this.gifHeaderInfo = gifHeaderInfo;
this.globalColorTable = globalColorTable;
this.blocks = blocks;
}
}

View file

@ -0,0 +1,40 @@
package org.apache.sanselan.formats.gif;
public class ImageDescriptor extends GIFBlock {
public final int imageLeftPosition;
public final int imageTopPosition;
public final int imageWidth;
public final int imageHeight;
public final byte packedFields;
public final boolean localColorTableFlag;
public final boolean interlaceFlag;
public final boolean sortFlag;
public final byte sizeOfLocalColorTable;
public final byte[] localColorTable;
public final byte[] imageData;
public ImageDescriptor(int blockCode, int ImageLeftPosition, int ImageTopPosition, int ImageWidth, int ImageHeight, byte PackedFields, boolean LocalColorTableFlag, boolean InterlaceFlag, boolean SortFlag, byte SizeofLocalColorTable, byte[] LocalColorTable, byte[] ImageData) {
super(blockCode);
this.imageLeftPosition = ImageLeftPosition;
this.imageTopPosition = ImageTopPosition;
this.imageWidth = ImageWidth;
this.imageHeight = ImageHeight;
this.packedFields = PackedFields;
this.localColorTableFlag = LocalColorTableFlag;
this.interlaceFlag = InterlaceFlag;
this.sortFlag = SortFlag;
this.sizeOfLocalColorTable = SizeofLocalColorTable;
this.localColorTable = LocalColorTable;
this.imageData = ImageData;
}
}

View file

@ -0,0 +1,371 @@
package org.apache.sanselan.formats.ico;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.util.Debug;
public class IcoImageParser extends ImageParser {
private static final String DEFAULT_EXTENSION = ".ico";
public IcoImageParser() {
setByteOrder(73);
}
public String getName() {
return "ico-Custom";
}
public String getDefaultExtension() {
return ".ico";
}
private static final String[] ACCEPTED_EXTENSIONS = new String[] { ".ico", ".cur" };
protected String[] getAcceptedExtensions() {
return ACCEPTED_EXTENSIONS;
}
protected ImageFormat[] getAcceptedTypes() {
return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_ICO };
}
public boolean embedICCProfile(File src, File dst, byte[] profile) {
return false;
}
public IImageMetadata getMetadata(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
public ImageInfo getImageInfo(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
public Dimension getImageSize(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
public byte[] getICCProfileBytes(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
private static class FileHeader {
public final int reserved;
public final int iconType;
public final int iconCount;
public FileHeader(int reserved, int iconType, int iconCount) {
this.reserved = reserved;
this.iconType = iconType;
this.iconCount = iconCount;
}
public void dump() {
System.out.println("FileHeader");
System.out.println("Reserved: " + this.reserved);
System.out.println("IconType: " + this.iconType);
System.out.println("IconCount: " + this.iconCount);
System.out.println("");
}
}
private FileHeader readFileHeader(InputStream is) throws ImageReadException, IOException {
int Reserved = read2Bytes("Reserved", is, "Not a Valid ICO File");
int IconType = read2Bytes("IconType", is, "Not a Valid ICO File");
int IconCount = read2Bytes("IconCount", is, "Not a Valid ICO File");
return new FileHeader(Reserved, IconType, IconCount);
}
private static class IconInfo {
public final byte Width;
public final byte Height;
public final byte ColorCount;
public final byte Reserved;
public final int Planes;
public final int BitCount;
public final int ImageSize;
public final int ImageOffset;
public IconInfo(byte width, byte height, byte colorCount, byte reserved, int planes, int bitCount, int imageSize, int imageOffset) {
this.Width = width;
this.Height = height;
this.ColorCount = colorCount;
this.Reserved = reserved;
this.Planes = planes;
this.BitCount = bitCount;
this.ImageSize = imageSize;
this.ImageOffset = imageOffset;
}
public void dump() {
System.out.println("IconInfo");
System.out.println("Width: " + this.Width);
System.out.println("Height: " + this.Height);
System.out.println("ColorCount: " + this.ColorCount);
System.out.println("Reserved: " + this.Reserved);
System.out.println("Planes: " + this.Planes);
System.out.println("BitCount: " + this.BitCount);
System.out.println("ImageSize: " + this.ImageSize);
System.out.println("ImageOffset: " + this.ImageOffset);
System.out.println("");
}
}
private IconInfo readIconInfo(InputStream is) throws ImageReadException, IOException {
byte Width = readByte("Width", is, "Not a Valid ICO File");
byte Height = readByte("Height", is, "Not a Valid ICO File");
byte ColorCount = readByte("ColorCount", is, "Not a Valid ICO File");
byte Reserved = readByte("Reserved", is, "Not a Valid ICO File");
int Planes = read2Bytes("Planes", is, "Not a Valid ICO File");
int BitCount = read2Bytes("BitCount", is, "Not a Valid ICO File");
int ImageSize = read4Bytes("ImageSize", is, "Not a Valid ICO File");
int ImageOffset = read4Bytes("ImageOffset", is, "Not a Valid ICO File");
return new IconInfo(Width, Height, ColorCount, Reserved, Planes, BitCount, ImageSize, ImageOffset);
}
private static class BitmapHeader {
public final int Size;
public final int Width;
public final int Height;
public final int Planes;
public final int BitCount;
public final int Compression;
public final int SizeImage;
public final int XPelsPerMeter;
public final int YPelsPerMeter;
public final int ColorsUsed;
public final int ColorsImportant;
public BitmapHeader(int size, int width, int height, int planes, int bitCount, int compression, int sizeImage, int pelsPerMeter, int pelsPerMeter2, int colorsUsed, int colorsImportant) {
this.Size = size;
this.Width = width;
this.Height = height;
this.Planes = planes;
this.BitCount = bitCount;
this.Compression = compression;
this.SizeImage = sizeImage;
this.XPelsPerMeter = pelsPerMeter;
this.YPelsPerMeter = pelsPerMeter2;
this.ColorsUsed = colorsUsed;
this.ColorsImportant = colorsImportant;
}
public void dump() {
System.out.println("BitmapHeader");
System.out.println("Size: " + this.Size);
System.out.println("Width: " + this.Width);
System.out.println("Height: " + this.Height);
System.out.println("Planes: " + this.Planes);
System.out.println("BitCount: " + this.BitCount);
System.out.println("Compression: " + this.Compression);
System.out.println("SizeImage: " + this.SizeImage);
System.out.println("XPelsPerMeter: " + this.XPelsPerMeter);
System.out.println("YPelsPerMeter: " + this.YPelsPerMeter);
System.out.println("ColorsUsed: " + this.ColorsUsed);
System.out.println("ColorsImportant: " + this.ColorsImportant);
System.out.println("");
}
}
private static class IconData {
public final IcoImageParser.IconInfo iconInfo;
public final IcoImageParser.BitmapHeader header;
public final byte[] palette;
public final byte[] color_map;
public final int scanline_size;
public final byte[] transparency_map;
public final int t_scanline_size;
public IconData(IcoImageParser.IconInfo iconInfo, IcoImageParser.BitmapHeader header, byte[] palette, byte[] color_map, int scanline_size, byte[] transparency_map, int t_scanline_size) {
this.iconInfo = iconInfo;
this.header = header;
this.palette = palette;
this.color_map = color_map;
this.scanline_size = scanline_size;
this.transparency_map = transparency_map;
this.t_scanline_size = t_scanline_size;
}
public void dump() {
System.out.println("IconData");
this.iconInfo.dump();
this.header.dump();
System.out.println("scanline_size: " + this.scanline_size);
System.out.println("t_scanline_size: " + this.t_scanline_size);
System.out.println("palette: " + ((this.palette == null) ? "null" : ("" + this.palette.length)));
System.out.println("color_map: " + ((this.color_map == null) ? "null" : ("" + this.color_map.length)));
System.out.println("transparency_map: " + ((this.transparency_map == null) ? "null" : ("" + this.transparency_map.length)));
System.out.println("");
}
}
private IconData readIconData(InputStream is, IconInfo fIconInfo) throws ImageReadException, IOException {
int Size = read4Bytes("Size", is, "Not a Valid ICO File");
int Width = read4Bytes("Width", is, "Not a Valid ICO File");
int Height = read4Bytes("Height", is, "Not a Valid ICO File");
int Planes = read2Bytes("Planes", is, "Not a Valid ICO File");
int BitCount = read2Bytes("BitCount", is, "Not a Valid ICO File");
int Compression = read4Bytes("Compression", is, "Not a Valid ICO File");
int SizeImage = read4Bytes("SizeImage", is, "Not a Valid ICO File");
int XPelsPerMeter = read4Bytes("XPelsPerMeter", is, "Not a Valid ICO File");
int YPelsPerMeter = read4Bytes("YPelsPerMeter", is, "Not a Valid ICO File");
int ColorsUsed = read4Bytes("ColorsUsed", is, "Not a Valid ICO File");
int ColorsImportant = read4Bytes("ColorsImportant", is, "Not a Valid ICO File");
BitmapHeader header = new BitmapHeader(Size, Width, Height, Planes, BitCount, Compression, SizeImage, XPelsPerMeter, YPelsPerMeter, ColorsUsed, ColorsImportant);
int palette_size = 0;
byte[] palette = null;
if (fIconInfo.BitCount == 1 || fIconInfo.BitCount == 4 || fIconInfo.BitCount == 8) {
palette_size = 4 * (1 << fIconInfo.BitCount);
palette = readByteArray("palette", palette_size, is, "Not a Valid ICO File");
}
byte[] color_map = null;
int scanline_size = (fIconInfo.BitCount * fIconInfo.Width + 7) / 8;
if (scanline_size % 4 != 0)
scanline_size += 4 - scanline_size % 4;
int color_map_size_bytes = scanline_size * fIconInfo.Height;
color_map = readByteArray("color_map", color_map_size_bytes, is, "Not a Valid ICO File");
byte[] transparency_map = null;
int t_scanline_size = (fIconInfo.Width + 7) / 8;
if (t_scanline_size % 4 != 0)
t_scanline_size += 4 - t_scanline_size % 4;
int tcolor_map_size_bytes = t_scanline_size * fIconInfo.Height;
transparency_map = readByteArray("transparency_map", tcolor_map_size_bytes, is, "Not a Valid ICO File");
return new IconData(fIconInfo, header, palette, color_map, scanline_size, transparency_map, t_scanline_size);
}
private static class ImageContents {
public final IcoImageParser.FileHeader fileHeader;
public final IcoImageParser.IconData[] iconDatas;
public ImageContents(IcoImageParser.FileHeader fileHeader, IcoImageParser.IconData[] iconDatas) {
this.fileHeader = fileHeader;
this.iconDatas = iconDatas;
}
}
private ImageContents readImage(ByteSource byteSource) throws ImageReadException, IOException {
InputStream is = null;
try {
is = byteSource.getInputStream();
FileHeader fileHeader = readFileHeader(is);
IconInfo[] fIconInfos = new IconInfo[fileHeader.iconCount];
for (int i = 0; i < fileHeader.iconCount; i++)
fIconInfos[i] = readIconInfo(is);
IconData[] fIconDatas = new IconData[fileHeader.iconCount];
for (int j = 0; j < fileHeader.iconCount; j++)
fIconDatas[j] = readIconData(is, fIconInfos[j]);
return new ImageContents(fileHeader, fIconDatas);
} finally {
try {
is.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
public final BufferedImage getBufferedImage(ByteSource byteSource, Map params) throws ImageReadException, IOException {
throw new ImageReadException("Use getAllBufferedImages() instead for .ico images.");
}
private BufferedImage readBufferedImage(IconData fIconData) throws ImageReadException {
IconInfo fIconInfo = fIconData.iconInfo;
int width = fIconInfo.Width;
int height = fIconInfo.Height;
int[] data = new int[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb;
int alpha_byte = 0xFF & fIconData.transparency_map[fIconData.t_scanline_size * y + x / 8];
int alpha = 0x1 & alpha_byte >> 7 - x % 8;
alpha = (alpha == 0) ? 255 : 0;
if (fIconInfo.BitCount == 1 || fIconInfo.BitCount == 4 || fIconInfo.BitCount == 8) {
int bit_index = fIconData.scanline_size * y * 8 + x * fIconInfo.BitCount;
int b = 0xFF & fIconData.color_map[bit_index >> 3];
int mask = (1 << fIconInfo.BitCount) - 1;
int shift = 8 - bit_index % 8 - fIconInfo.BitCount;
int color_index = mask & b >> shift;
int red = 0xFF & fIconData.palette[4 * color_index + 2];
int green = 0xFF & fIconData.palette[4 * color_index + 1];
int blue = 0xFF & fIconData.palette[4 * color_index + 0];
rgb = (0xFF & red) << 16 | (0xFF & green) << 8 | (0xFF & blue) << 0;
} else if (fIconInfo.BitCount == 24 || fIconInfo.BitCount == 32) {
int byte_count = fIconInfo.BitCount >> 3;
int index = fIconData.scanline_size * y + x * byte_count;
int red = 0xFF & fIconData.color_map[index + 2];
int green = 0xFF & fIconData.color_map[index + 1];
int blue = 0xFF & fIconData.color_map[index + 0];
rgb = (0xFF & red) << 16 | (0xFF & green) << 8 | (0xFF & blue) << 0;
} else {
throw new ImageReadException("Unknown BitCount: " + fIconInfo.BitCount);
}
int argb = (alpha & 0xFF) << 24 | rgb & 0xFFFFFF;
data[(height - y - 1) * width + x] = argb;
}
}
ColorModel cModel = ColorModel.getRGBdefault();
DataBufferInt intBuf = new DataBufferInt(data, width * height);
SampleModel sModel = cModel.createCompatibleSampleModel(width, height);
WritableRaster raster = Raster.createWritableRaster(sModel, intBuf, null);
BufferedImage result = new BufferedImage(cModel, raster, false, null);
return result;
}
public ArrayList getAllBufferedImages(ByteSource byteSource) throws ImageReadException, IOException {
ArrayList result = new ArrayList();
ImageContents contents = readImage(byteSource);
FileHeader fileHeader = contents.fileHeader;
for (int i = 0; i < fileHeader.iconCount; i++) {
IconData iconData = contents.iconDatas[i];
BufferedImage image = readBufferedImage(iconData);
result.add(image);
}
return result;
}
public String getXmpXml(ByteSource byteSource, Map params) throws ImageReadException, IOException {
return null;
}
}

View file

@ -0,0 +1,85 @@
package org.apache.sanselan.formats.jpeg;
public interface JpegConstants {
public static final int MAX_SEGMENT_SIZE = 65535;
public static final byte[] JFIF0_SIGNATURE = new byte[] { 74, 70, 73, 70, 0 };
public static final byte[] JFIF0_SIGNATURE_ALTERNATIVE = new byte[] { 74, 70, 73, 70, 32 };
public static final byte[] EXIF_IDENTIFIER_CODE = new byte[] { 69, 120, 105, 102 };
public static final byte[] XMP_IDENTIFIER = new byte[] {
104, 116, 116, 112, 58, 47, 47, 110, 115, 46,
97, 100, 111, 98, 101, 46, 99, 111, 109, 47,
120, 97, 112, 47, 49, 46, 48, 47, 0 };
public static final byte[] SOI = new byte[] { -1, -40 };
public static final byte[] EOI = new byte[] { -1, -39 };
public static final int SOS_Marker = 65498;
public static final int JPEG_APP0 = 224;
public static final int JPEG_APP0_Marker = 65504;
public static final int JPEG_APP1_Marker = 65505;
public static final int JPEG_APP2_Marker = 65506;
public static final int JPEG_APP13_Marker = 65517;
public static final int JPEG_APP14_Marker = 65518;
public static final int JPEG_APP15_Marker = 65519;
public static final int JFIFMarker = 65504;
public static final int SOF0Marker = 65472;
public static final int SOF1Marker = 65473;
public static final int SOF2Marker = 65474;
public static final int SOF3Marker = 65475;
public static final int SOF4Marker = 65476;
public static final int SOF5Marker = 65477;
public static final int SOF6Marker = 65478;
public static final int SOF7Marker = 65479;
public static final int SOF8Marker = 65480;
public static final int SOF9Marker = 65481;
public static final int SOF10Marker = 65482;
public static final int SOF11Marker = 65483;
public static final int SOF12Marker = 65484;
public static final int SOF13Marker = 65485;
public static final int SOF14Marker = 65486;
public static final int SOF15Marker = 65487;
public static final int[] MARKERS = new int[] {
65498, 224, 65504, 65505, 65506, 65517, 65518, 65519, 65504, 65472,
65473, 65474, 65475, 65476, 65477, 65478, 65479, 65480, 65481, 65482,
65483, 65484, 65485, 65486, 65487 };
public static final byte[] icc_profile_label = new byte[] {
73, 67, 67, 95, 80, 82, 79, 70, 73, 76,
69, 0 };
public static final byte[] PHOTOSHOP_IDENTIFICATION_STRING = new byte[] {
80, 104, 111, 116, 111, 115, 104, 111, 112, 32,
51, 46, 48, 0 };
public static final byte[] CONST_8BIM = new byte[] { 56, 66, 73, 77 };
}

View file

@ -0,0 +1,110 @@
package org.apache.sanselan.formats.jpeg;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.formats.tiff.TiffField;
import org.apache.sanselan.formats.tiff.TiffImageData;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.constants.TagInfo;
import org.apache.sanselan.util.Debug;
public class JpegImageMetadata implements IImageMetadata {
private final JpegPhotoshopMetadata photoshop;
private final TiffImageMetadata exif;
public JpegImageMetadata(JpegPhotoshopMetadata photoshop, TiffImageMetadata exif) {
this.photoshop = photoshop;
this.exif = exif;
}
public TiffImageMetadata getExif() {
return this.exif;
}
public JpegPhotoshopMetadata getPhotoshop() {
return this.photoshop;
}
public TiffField findEXIFValue(TagInfo tagInfo) {
ArrayList items = getItems();
for (int i = 0; i < items.size(); i++) {
Object o = items.get(i);
if (o instanceof TiffImageMetadata.Item) {
TiffImageMetadata.Item item = (TiffImageMetadata.Item)o;
TiffField field = item.getTiffField();
if (field.tag == tagInfo.tag)
return field;
}
}
return null;
}
public BufferedImage getEXIFThumbnail() throws ImageReadException, IOException {
ArrayList dirs = this.exif.getDirectories();
for (int i = 0; i < dirs.size(); i++) {
TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory)dirs.get(i);
BufferedImage image = dir.getThumbnail();
if (null != image)
return image;
}
return null;
}
public TiffImageData getRawImageData() {
ArrayList dirs = this.exif.getDirectories();
for (int i = 0; i < dirs.size(); i++) {
TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory)dirs.get(i);
TiffImageData rawImageData = dir.getTiffImageData();
if (null != rawImageData)
return rawImageData;
}
return null;
}
public ArrayList getItems() {
ArrayList result = new ArrayList();
if (null != this.exif)
result.addAll(this.exif.getItems());
if (null != this.photoshop)
result.addAll(this.photoshop.getItems());
return result;
}
private static final String newline = System.getProperty("line.separator");
public String toString() {
return toString(null);
}
public String toString(String prefix) {
if (prefix == null)
prefix = "";
StringBuffer result = new StringBuffer();
result.append(prefix);
if (null == this.exif) {
result.append("No Exif metadata.");
} else {
result.append("Exif metadata:");
result.append(newline);
result.append(this.exif.toString("\t"));
}
result.append(newline);
result.append(prefix);
if (null == this.photoshop) {
result.append("No Photoshop (IPTC) metadata.");
} else {
result.append("Photoshop (IPTC) metadata:");
result.append(newline);
result.append(this.photoshop.toString("\t"));
}
return result.toString();
}
public void dump() {
Debug.debug(toString());
}
}

View file

@ -0,0 +1,496 @@
package org.apache.sanselan.formats.jpeg;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.jpeg.iptc.IPTCParser;
import org.apache.sanselan.formats.jpeg.iptc.PhotoshopApp13Data;
import org.apache.sanselan.formats.jpeg.segments.App13Segment;
import org.apache.sanselan.formats.jpeg.segments.App2Segment;
import org.apache.sanselan.formats.jpeg.segments.GenericSegment;
import org.apache.sanselan.formats.jpeg.segments.JFIFSegment;
import org.apache.sanselan.formats.jpeg.segments.SOFNSegment;
import org.apache.sanselan.formats.jpeg.segments.Segment;
import org.apache.sanselan.formats.jpeg.segments.UnknownSegment;
import org.apache.sanselan.formats.jpeg.xmp.JpegXmpParser;
import org.apache.sanselan.formats.tiff.TiffField;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.TiffImageParser;
import org.apache.sanselan.formats.tiff.constants.TiffTagConstants;
import org.apache.sanselan.util.Debug;
public class JpegImageParser extends ImageParser implements JpegConstants, TiffTagConstants {
private static final String DEFAULT_EXTENSION = ".jpg";
public JpegImageParser() {
setByteOrder(77);
}
protected ImageFormat[] getAcceptedTypes() {
return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_JPEG };
}
public String getName() {
return "Jpeg-Custom";
}
public String getDefaultExtension() {
return ".jpg";
}
public static final String[] AcceptedExtensions = new String[] { ".jpg", ".jpeg" };
public static final boolean permissive = true;
protected String[] getAcceptedExtensions() {
return AcceptedExtensions;
}
public final BufferedImage getBufferedImage(ByteSource byteSource, Map params) throws ImageReadException, IOException {
throw new ImageReadException("Sanselan cannot read or write JPEG images.");
}
private boolean keepMarker(int marker, int[] markers) {
if (markers == null)
return true;
for (int i = 0; i < markers.length; i++) {
if (markers[i] == marker)
return true;
}
return false;
}
public ArrayList readSegments(ByteSource byteSource, final int[] markers, final boolean returnAfterFirst, boolean readEverything) throws ImageReadException, IOException {
final ArrayList result = new ArrayList();
final JpegImageParser parser = this;
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
public boolean beginSOS() {
return false;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {}
public boolean visitSegment(int marker, byte[] markerBytes, int markerLength, byte[] markerLengthBytes, byte[] segmentData) throws ImageReadException, IOException {
if (marker == 65497)
return false;
if (!JpegImageParser.this.keepMarker(marker, markers))
return true;
if (marker == 65517) {
result.add(new App13Segment(parser, marker, segmentData));
} else if (marker == 65506) {
result.add(new App2Segment(marker, segmentData));
} else if (marker == 65504) {
result.add(new JFIFSegment(marker, segmentData));
} else if (marker >= 65472 && marker <= 65487) {
result.add(new SOFNSegment(marker, segmentData));
} else if (marker >= 65505 && marker <= 65519) {
result.add(new UnknownSegment(marker, segmentData));
}
if (returnAfterFirst)
return false;
return true;
}
};
new JpegUtils().traverseJFIF(byteSource, visitor);
return result;
}
private byte[] assembleSegments(ArrayList v) throws ImageReadException, IOException {
try {
return assembleSegments(v, false);
} catch (ImageReadException e) {
return assembleSegments(v, true);
}
}
private byte[] assembleSegments(ArrayList v, boolean start_with_zero) throws ImageReadException, IOException {
if (v.size() < 1)
throw new ImageReadException("No App2 Segments Found.");
int markerCount = ((App2Segment)v.get(0)).num_markers;
if (v.size() != markerCount)
throw new ImageReadException("App2 Segments Missing. Found: " + v.size() + ", Expected: " + markerCount + ".");
Collections.sort(v);
int offset = start_with_zero ? 0 : 1;
int total = 0;
for (int i = 0; i < v.size(); i++) {
App2Segment segment = (App2Segment)v.get(i);
if (i + offset != segment.cur_marker) {
dumpSegments(v);
throw new ImageReadException("Incoherent App2 Segment Ordering. i: " + i + ", segment[" + i + "].cur_marker: " + segment.cur_marker + ".");
}
if (markerCount != segment.num_markers) {
dumpSegments(v);
throw new ImageReadException("Inconsistent App2 Segment Count info. markerCount: " + markerCount + ", segment[" + i + "].num_markers: " + segment.num_markers + ".");
}
total += segment.icc_bytes.length;
}
byte[] result = new byte[total];
int progress = 0;
for (int j = 0; j < v.size(); j++) {
App2Segment segment = (App2Segment)v.get(j);
System.arraycopy(segment.icc_bytes, 0, result, progress, segment.icc_bytes.length);
progress += segment.icc_bytes.length;
}
return result;
}
private void dumpSegments(ArrayList v) {
Debug.debug();
Debug.debug("dumpSegments", v.size());
for (int i = 0; i < v.size(); i++) {
App2Segment segment = (App2Segment)v.get(i);
Debug.debug(i + ": " + segment.cur_marker + " / " + segment.num_markers);
}
Debug.debug();
}
public ArrayList readSegments(ByteSource byteSource, int[] markers, boolean returnAfterFirst) throws ImageReadException, IOException {
return readSegments(byteSource, markers, returnAfterFirst, false);
}
public byte[] getICCProfileBytes(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ArrayList segments = readSegments(byteSource, new int[] { 65506 }, false);
if (segments != null) {
ArrayList filtered = new ArrayList();
for (int i = 0; i < segments.size(); i++) {
App2Segment segment = (App2Segment)segments.get(i);
if (segment.icc_bytes != null)
filtered.add(segment);
}
segments = filtered;
}
if (segments == null || segments.size() < 1)
return null;
byte[] bytes = assembleSegments(segments);
if (this.debug)
System.out.println("bytes: " + ((bytes == null) ? null : ("" + bytes.length)));
if (this.debug)
System.out.println("");
return bytes;
}
public IImageMetadata getMetadata(ByteSource byteSource, Map params) throws ImageReadException, IOException {
TiffImageMetadata exif = getExifMetadata(byteSource, params);
JpegPhotoshopMetadata photoshop = getPhotoshopMetadata(byteSource, params);
if (null == exif && null == photoshop)
return null;
JpegImageMetadata result = new JpegImageMetadata(photoshop, exif);
return result;
}
public static boolean isExifAPP1Segment(GenericSegment segment) {
return byteArrayHasPrefix(segment.bytes, EXIF_IDENTIFIER_CODE);
}
private ArrayList filterAPP1Segments(ArrayList v) {
ArrayList result = new ArrayList();
for (int i = 0; i < v.size(); i++) {
GenericSegment segment = (GenericSegment)v.get(i);
if (isExifAPP1Segment(segment))
result.add(segment);
}
return result;
}
private ArrayList filterSegments(ArrayList v, List markers) {
ArrayList result = new ArrayList();
for (int i = 0; i < v.size(); i++) {
Segment segment = (Segment)v.get(i);
Integer marker = new Integer(segment.marker);
if (markers.contains(marker))
result.add(segment);
}
return result;
}
public TiffImageMetadata getExifMetadata(ByteSource byteSource, Map params) throws ImageReadException, IOException {
byte[] bytes = getExifRawData(byteSource);
if (null == bytes)
return null;
if (params == null)
params = new HashMap();
if (!params.containsKey("READ_THUMBNAILS"))
params.put("READ_THUMBNAILS", Boolean.TRUE);
return (TiffImageMetadata)new TiffImageParser().getMetadata(bytes, params);
}
public byte[] getExifRawData(ByteSource byteSource) throws ImageReadException, IOException {
ArrayList segments = readSegments(byteSource, new int[] { 65505 }, false);
if (segments == null || segments.size() < 1)
return null;
ArrayList exifSegments = filterAPP1Segments(segments);
if (this.debug)
System.out.println("exif_segments.size: " + exifSegments.size());
if (exifSegments.size() < 1)
return null;
if (exifSegments.size() > 1)
throw new ImageReadException("Sanselan currently can't parse EXIF metadata split across multiple APP1 segments. Please send this image to the Sanselan project.");
GenericSegment segment = (GenericSegment)exifSegments.get(0);
byte[] bytes = segment.bytes;
return getByteArrayTail("trimmed exif bytes", bytes, 6);
}
public boolean hasExifSegment(ByteSource byteSource) throws ImageReadException, IOException {
final boolean[] result = { false };
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
public boolean beginSOS() {
return false;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {}
public boolean visitSegment(int marker, byte[] markerBytes, int markerLength, byte[] markerLengthBytes, byte[] segmentData) throws ImageReadException, IOException {
if (marker == 65497)
return false;
if (marker == 65505)
if (BinaryFileParser.byteArrayHasPrefix(segmentData, JpegConstants.EXIF_IDENTIFIER_CODE)) {
result[0] = true;
return false;
}
return true;
}
};
new JpegUtils().traverseJFIF(byteSource, visitor);
return result[0];
}
public boolean hasIptcSegment(ByteSource byteSource) throws ImageReadException, IOException {
final boolean[] result = { false };
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
public boolean beginSOS() {
return false;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {}
public boolean visitSegment(int marker, byte[] markerBytes, int markerLength, byte[] markerLengthBytes, byte[] segmentData) throws ImageReadException, IOException {
if (marker == 65497)
return false;
if (marker == 65517)
if (new IPTCParser().isPhotoshopJpegSegment(segmentData)) {
result[0] = true;
return false;
}
return true;
}
};
new JpegUtils().traverseJFIF(byteSource, visitor);
return result[0];
}
public boolean hasXmpSegment(ByteSource byteSource) throws ImageReadException, IOException {
final boolean[] result = { false };
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
public boolean beginSOS() {
return false;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {}
public boolean visitSegment(int marker, byte[] markerBytes, int markerLength, byte[] markerLengthBytes, byte[] segmentData) throws ImageReadException, IOException {
if (marker == 65497)
return false;
if (marker == 65505)
if (new JpegXmpParser().isXmpJpegSegment(segmentData)) {
result[0] = true;
return false;
}
return true;
}
};
new JpegUtils().traverseJFIF(byteSource, visitor);
return result[0];
}
public String getXmpXml(ByteSource byteSource, Map params) throws ImageReadException, IOException {
final List result = new ArrayList();
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
public boolean beginSOS() {
return false;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {}
public boolean visitSegment(int marker, byte[] markerBytes, int markerLength, byte[] markerLengthBytes, byte[] segmentData) throws ImageReadException, IOException {
if (marker == 65497)
return false;
if (marker == 65505)
if (new JpegXmpParser().isXmpJpegSegment(segmentData)) {
result.add(new JpegXmpParser().parseXmpJpegSegment(segmentData));
return false;
}
return true;
}
};
new JpegUtils().traverseJFIF(byteSource, visitor);
if (result.size() < 1)
return null;
if (result.size() > 1)
throw new ImageReadException("Jpeg file contains more than one XMP segment.");
return (String)result.get(0);
}
public JpegPhotoshopMetadata getPhotoshopMetadata(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ArrayList segments = readSegments(byteSource, new int[] { 65517 }, false);
if (segments == null || segments.size() < 1)
return null;
PhotoshopApp13Data photoshopApp13Data = null;
for (int i = 0; i < segments.size(); i++) {
App13Segment segment = (App13Segment)segments.get(i);
PhotoshopApp13Data data = segment.parsePhotoshopSegment(params);
if (data != null && photoshopApp13Data != null)
throw new ImageReadException("Jpeg contains more than one Photoshop App13 segment.");
photoshopApp13Data = data;
}
if (null == photoshopApp13Data)
return null;
return new JpegPhotoshopMetadata(photoshopApp13Data);
}
public Dimension getImageSize(ByteSource byteSource, Map params) throws ImageReadException, IOException {
ArrayList segments = readSegments(byteSource, new int[] {
65472, 65473, 65474, 65475, 65477, 65478, 65479, 65481, 65482, 65483,
65485, 65486, 65487 }, true);
if (segments == null || segments.size() < 1)
throw new ImageReadException("No JFIF Data Found.");
if (segments.size() > 1)
throw new ImageReadException("Redundant JFIF Data Found.");
SOFNSegment fSOFNSegment = (SOFNSegment)segments.get(0);
return new Dimension(fSOFNSegment.width, fSOFNSegment.height);
}
public byte[] embedICCProfile(byte[] image, byte[] profile) {
return null;
}
public boolean embedICCProfile(File src, File dst, byte[] profile) {
return false;
}
public ImageInfo getImageInfo(ByteSource byteSource, Map params) throws ImageReadException, IOException {
String FormatDetails;
int ColorType;
ArrayList SOF_segments = readSegments(byteSource, new int[] {
65472, 65473, 65474, 65475, 65477, 65478, 65479, 65481, 65482, 65483,
65485, 65486, 65487 }, false);
if (SOF_segments == null)
throw new ImageReadException("No SOFN Data Found.");
ArrayList jfifSegments = readSegments(byteSource, new int[] { 65504 }, true);
SOFNSegment fSOFNSegment = (SOFNSegment)SOF_segments.get(0);
if (fSOFNSegment == null)
throw new ImageReadException("No SOFN Data Found.");
int Width = fSOFNSegment.width;
int Height = fSOFNSegment.height;
JFIFSegment jfifSegment = null;
if (jfifSegments != null && jfifSegments.size() > 0)
jfifSegment = (JFIFSegment)jfifSegments.get(0);
double x_density = -1.0D;
double y_density = -1.0D;
double units_per_inch = -1.0D;
if (jfifSegment != null) {
x_density = (double)jfifSegment.xDensity;
y_density = (double)jfifSegment.yDensity;
int density_units = jfifSegment.densityUnits;
FormatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion + "." + jfifSegment.jfifMinorVersion;
switch (density_units) {
case 1:
units_per_inch = 1.0D;
break;
case 2:
units_per_inch = 2.54D;
break;
}
} else {
JpegImageMetadata metadata = (JpegImageMetadata)getMetadata(byteSource, params);
if (metadata != null) {
TiffField field = metadata.findEXIFValue(TIFF_TAG_XRESOLUTION);
if (field != null)
x_density = ((Number)field.getValue()).doubleValue();
field = metadata.findEXIFValue(TIFF_TAG_YRESOLUTION);
if (field != null)
y_density = ((Number)field.getValue()).doubleValue();
field = metadata.findEXIFValue(TIFF_TAG_RESOLUTION_UNIT);
if (field != null) {
int density_units = ((Number)field.getValue()).intValue();
switch (density_units) {
case 2:
units_per_inch = 1.0D;
break;
case 3:
units_per_inch = 2.54D;
break;
}
}
}
FormatDetails = "Jpeg/DCM";
}
int PhysicalHeightDpi = -1;
float PhysicalHeightInch = -1.0F;
int PhysicalWidthDpi = -1;
float PhysicalWidthInch = -1.0F;
if (units_per_inch > 0.0D) {
PhysicalWidthDpi = (int)Math.round(x_density / units_per_inch);
PhysicalWidthInch = (float)((double)Width / (x_density * units_per_inch));
PhysicalHeightDpi = (int)Math.round(y_density * units_per_inch);
PhysicalHeightInch = (float)((double)Height / (y_density * units_per_inch));
}
ArrayList Comments = new ArrayList();
int Number_of_components = fSOFNSegment.numberOfComponents;
int Precision = fSOFNSegment.precision;
int BitsPerPixel = Number_of_components * Precision;
ImageFormat Format = ImageFormat.IMAGE_FORMAT_JPEG;
String FormatName = "JPEG (Joint Photographic Experts Group) Format";
String MimeType = "image/jpeg";
int NumberOfImages = 1;
boolean isProgressive = (fSOFNSegment.marker == 65474);
boolean isTransparent = false;
boolean usesPalette = false;
if (Number_of_components == 1) {
ColorType = 0;
} else if (Number_of_components == 3) {
ColorType = 2;
} else if (Number_of_components == 4) {
ColorType = 3;
} else {
ColorType = -2;
}
String compressionAlgorithm = "JPEG";
ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments, Format, FormatName, Height, MimeType, NumberOfImages, PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi, PhysicalWidthInch, Width, isProgressive, isTransparent, usesPalette, ColorType, compressionAlgorithm);
return result;
}
public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource) throws ImageReadException, IOException {
pw.println("tiff.dumpImageFile");
ImageInfo imageInfo = getImageInfo(byteSource);
if (imageInfo == null)
return false;
imageInfo.toString(pw, "");
pw.println("");
ArrayList segments = readSegments(byteSource, null, false);
if (segments == null)
throw new ImageReadException("No Segments Found.");
for (int d = 0; d < segments.size(); d++) {
Segment segment = (Segment)segments.get(d);
NumberFormat nf = NumberFormat.getIntegerInstance();
pw.println(d + ": marker: " + Integer.toHexString(segment.marker) + ", " + segment.getDescription() + " (length: " + nf.format((long)segment.length) + ")");
segment.dump(pw);
}
pw.println("");
return true;
}
}

View file

@ -0,0 +1,28 @@
package org.apache.sanselan.formats.jpeg;
import java.util.Collections;
import java.util.List;
import org.apache.sanselan.common.ImageMetadata;
import org.apache.sanselan.formats.jpeg.iptc.IPTCConstants;
import org.apache.sanselan.formats.jpeg.iptc.IPTCRecord;
import org.apache.sanselan.formats.jpeg.iptc.PhotoshopApp13Data;
import org.apache.sanselan.util.Debug;
public class JpegPhotoshopMetadata extends ImageMetadata implements IPTCConstants {
public final PhotoshopApp13Data photoshopApp13Data;
public JpegPhotoshopMetadata(PhotoshopApp13Data photoshopApp13Data) {
this.photoshopApp13Data = photoshopApp13Data;
List records = photoshopApp13Data.getRecords();
Collections.sort(records, IPTCRecord.COMPARATOR);
for (int j = 0; j < records.size(); j++) {
IPTCRecord element = (IPTCRecord)records.get(j);
if (element.iptcType.type != IPTC_TYPE_RECORD_VERSION.type)
add(element.getIptcTypeName(), element.getValue());
}
}
public void dump() {
Debug.debug(toString());
}
}

View file

@ -0,0 +1,125 @@
package org.apache.sanselan.formats.jpeg;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.util.Debug;
public class JpegUtils extends BinaryFileParser implements JpegConstants {
public JpegUtils() {
setByteOrder(77);
}
public void traverseJFIF(ByteSource byteSource, Visitor visitor) throws ImageReadException, IOException {
InputStream is = null;
try {
is = byteSource.getInputStream();
readAndVerifyBytes(is, SOI, "Not a Valid JPEG File: doesn't begin with 0xffd8");
int byteOrder = getByteOrder();
for (int markerCount = 0;; markerCount++) {
byte[] markerBytes = readByteArray("markerBytes", 2, is, "markerBytes");
int marker = convertByteArrayToShort("marker", markerBytes, byteOrder);
if (marker == 65497 || marker == 65498) {
if (!visitor.beginSOS())
return;
byte[] imageData = getStreamBytes(is);
visitor.visitSOS(marker, markerBytes, imageData);
break;
}
byte[] segmentLengthBytes = readByteArray("segmentLengthBytes", 2, is, "segmentLengthBytes");
int segmentLength = convertByteArrayToShort("segmentLength", segmentLengthBytes, byteOrder);
byte[] segmentData = readByteArray("Segment Data", segmentLength - 2, is, "Invalid Segment: insufficient data");
if (!visitor.visitSegment(marker, markerBytes, segmentLength, segmentLengthBytes, segmentData))
return;
}
} finally {
try {
is.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
public static String getMarkerName(int marker) {
switch (marker) {
case 65498:
return "SOS_Marker";
case 65505:
return "JPEG_APP1_Marker";
case 65506:
return "JPEG_APP2_Marker";
case 65517:
return "JPEG_APP13_Marker";
case 65518:
return "JPEG_APP14_Marker";
case 65519:
return "JPEG_APP15_Marker";
case 65504:
return "JFIFMarker";
case 65472:
return "SOF0Marker";
case 65473:
return "SOF1Marker";
case 65474:
return "SOF2Marker";
case 65475:
return "SOF3Marker";
case 65476:
return "SOF4Marker";
case 65477:
return "SOF5Marker";
case 65478:
return "SOF6Marker";
case 65479:
return "SOF7Marker";
case 65480:
return "SOF8Marker";
case 65481:
return "SOF9Marker";
case 65482:
return "SOF10Marker";
case 65483:
return "SOF11Marker";
case 65484:
return "SOF12Marker";
case 65485:
return "SOF13Marker";
case 65486:
return "SOF14Marker";
case 65487:
return "SOF15Marker";
}
return "Unknown";
}
public void dumpJFIF(ByteSource byteSource) throws ImageReadException, IOException, ImageWriteException {
Visitor visitor = new Visitor() {
public boolean beginSOS() {
return true;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {
Debug.debug("SOS marker. " + imageData.length + " bytes of image data.");
Debug.debug("");
}
public boolean visitSegment(int marker, byte[] markerBytes, int segmentLength, byte[] segmentLengthBytes, byte[] segmentData) {
Debug.debug("Segment marker: " + Integer.toHexString(marker) + " (" + JpegUtils.getMarkerName(marker) + "), " + segmentData.length + " bytes of segment data.");
return true;
}
};
traverseJFIF(byteSource, visitor);
}
public static interface Visitor {
boolean beginSOS();
void visitSOS(int param1Int, byte[] param1ArrayOfbyte1, byte[] param1ArrayOfbyte2);
boolean visitSegment(int param1Int1, byte[] param1ArrayOfbyte1, int param1Int2, byte[] param1ArrayOfbyte2, byte[] param1ArrayOfbyte3) throws ImageReadException, IOException;
}
}

View file

@ -0,0 +1,275 @@
package org.apache.sanselan.formats.jpeg.exifRewrite;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceArray;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
import org.apache.sanselan.formats.jpeg.JpegConstants;
import org.apache.sanselan.formats.jpeg.JpegUtils;
import org.apache.sanselan.formats.tiff.write.TiffImageWriterBase;
import org.apache.sanselan.formats.tiff.write.TiffImageWriterLossless;
import org.apache.sanselan.formats.tiff.write.TiffImageWriterLossy;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
import org.apache.sanselan.util.Debug;
public class ExifRewriter extends BinaryFileParser implements JpegConstants {
public ExifRewriter() {
setByteOrder(77);
}
public ExifRewriter(int byteOrder) {
setByteOrder(byteOrder);
}
private static class JFIFPieces {
public final List pieces;
public final List exifPieces;
public JFIFPieces(List pieces, List exifPieces) {
this.pieces = pieces;
this.exifPieces = exifPieces;
}
}
private static abstract class JFIFPiece {
private JFIFPiece() {}
protected abstract void write(OutputStream param1OutputStream) throws IOException;
JFIFPiece(ExifRewriter.null x0) {
this();
}
}
private static class JFIFPieceSegment extends JFIFPiece {
public final int marker;
public final byte[] markerBytes;
public final byte[] markerLengthBytes;
public final byte[] segmentData;
public JFIFPieceSegment(int marker, byte[] markerBytes, byte[] markerLengthBytes, byte[] segmentData) {
this.marker = marker;
this.markerBytes = markerBytes;
this.markerLengthBytes = markerLengthBytes;
this.segmentData = segmentData;
}
protected void write(OutputStream os) throws IOException {
os.write(this.markerBytes);
os.write(this.markerLengthBytes);
os.write(this.segmentData);
}
}
private static class JFIFPieceSegmentExif extends JFIFPieceSegment {
public JFIFPieceSegmentExif(int marker, byte[] markerBytes, byte[] markerLengthBytes, byte[] segmentData) {
super(marker, markerBytes, markerLengthBytes, segmentData);
}
}
private static class JFIFPieceImageData extends JFIFPiece {
public final byte[] markerBytes;
public final byte[] imageData;
public JFIFPieceImageData(byte[] markerBytes, byte[] imageData) {
this.markerBytes = markerBytes;
this.imageData = imageData;
}
protected void write(OutputStream os) throws IOException {
os.write(this.markerBytes);
os.write(this.imageData);
}
}
private JFIFPieces analyzeJFIF(ByteSource byteSource) throws ImageReadException, IOException {
final ArrayList pieces = new ArrayList();
final List exifPieces = new ArrayList();
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
public boolean beginSOS() {
return true;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {
pieces.add(new ExifRewriter.JFIFPieceImageData(markerBytes, imageData));
}
public boolean visitSegment(int marker, byte[] markerBytes, int markerLength, byte[] markerLengthBytes, byte[] segmentData) throws ImageReadException, IOException {
if (marker != 65505) {
pieces.add(new ExifRewriter.JFIFPieceSegment(marker, markerBytes, markerLengthBytes, segmentData));
} else if (!BinaryFileParser.byteArrayHasPrefix(segmentData, JpegConstants.EXIF_IDENTIFIER_CODE)) {
pieces.add(new ExifRewriter.JFIFPieceSegment(marker, markerBytes, markerLengthBytes, segmentData));
} else {
ExifRewriter.JFIFPiece piece = new ExifRewriter.JFIFPieceSegmentExif(marker, markerBytes, markerLengthBytes, segmentData);
pieces.add(piece);
exifPieces.add(piece);
}
return true;
}
};
new JpegUtils().traverseJFIF(byteSource, visitor);
return new JFIFPieces(pieces, exifPieces);
}
public void removeExifMetadata(File src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceFile(src);
removeExifMetadata(byteSource, os);
}
public void removeExifMetadata(byte[] src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceArray(src);
removeExifMetadata(byteSource, os);
}
public void removeExifMetadata(InputStream src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceInputStream(src, null);
removeExifMetadata(byteSource, os);
}
public void removeExifMetadata(ByteSource byteSource, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
JFIFPieces jfifPieces = analyzeJFIF(byteSource);
List pieces = jfifPieces.pieces;
writeSegmentsReplacingExif(os, pieces, null);
}
public void updateExifMetadataLossless(File src, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceFile(src);
updateExifMetadataLossless(byteSource, os, outputSet);
}
public void updateExifMetadataLossless(byte[] src, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceArray(src);
updateExifMetadataLossless(byteSource, os, outputSet);
}
public void updateExifMetadataLossless(InputStream src, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceInputStream(src, null);
updateExifMetadataLossless(byteSource, os, outputSet);
}
public void updateExifMetadataLossless(ByteSource byteSource, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
TiffImageWriterBase writer;
JFIFPieces jfifPieces = analyzeJFIF(byteSource);
List pieces = jfifPieces.pieces;
if (jfifPieces.exifPieces.size() > 0) {
JFIFPieceSegment exifPiece = null;
exifPiece = (JFIFPieceSegment)jfifPieces.exifPieces.get(0);
byte[] exifBytes = exifPiece.segmentData;
exifBytes = getByteArrayTail("trimmed exif bytes", exifBytes, 6);
writer = new TiffImageWriterLossless(outputSet.byteOrder, exifBytes);
} else {
writer = new TiffImageWriterLossy(outputSet.byteOrder);
}
boolean includeEXIFPrefix = true;
byte[] newBytes = writeExifSegment(writer, outputSet, includeEXIFPrefix);
writeSegmentsReplacingExif(os, pieces, newBytes);
}
public void updateExifMetadataLossy(byte[] src, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceArray(src);
updateExifMetadataLossy(byteSource, os, outputSet);
}
public void updateExifMetadataLossy(InputStream src, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceInputStream(src, null);
updateExifMetadataLossy(byteSource, os, outputSet);
}
public void updateExifMetadataLossy(File src, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceFile(src);
updateExifMetadataLossy(byteSource, os, outputSet);
}
public void updateExifMetadataLossy(ByteSource byteSource, OutputStream os, TiffOutputSet outputSet) throws ImageReadException, IOException, ImageWriteException {
JFIFPieces jfifPieces = analyzeJFIF(byteSource);
List pieces = jfifPieces.pieces;
TiffImageWriterBase writer = new TiffImageWriterLossy(outputSet.byteOrder);
boolean includeEXIFPrefix = true;
byte[] newBytes = writeExifSegment(writer, outputSet, includeEXIFPrefix);
writeSegmentsReplacingExif(os, pieces, newBytes);
}
private void writeSegmentsReplacingExif(OutputStream os, List segments, byte[] newBytes) throws ImageWriteException, IOException {
int byteOrder = getByteOrder();
try {
os.write(SOI);
boolean hasExif = false;
for (int i = 0; i < segments.size(); i++) {
JFIFPiece piece = (JFIFPiece)segments.get(i);
if (piece instanceof JFIFPieceSegmentExif)
hasExif = true;
}
if (!hasExif && newBytes != null) {
byte[] markerBytes = convertShortToByteArray(65505, byteOrder);
if (newBytes.length > 65535)
throw new ExifOverflowException("APP1 Segment is too long: " + newBytes.length);
int markerLength = newBytes.length + 2;
byte[] markerLengthBytes = convertShortToByteArray(markerLength, byteOrder);
int index = 0;
JFIFPieceSegment firstSegment = (JFIFPieceSegment)segments.get(index);
if (firstSegment.marker == 65504)
index = 1;
segments.add(0, new JFIFPieceSegmentExif(65505, markerBytes, markerLengthBytes, newBytes));
}
boolean APP1Written = false;
for (int j = 0; j < segments.size(); j++) {
JFIFPiece piece = (JFIFPiece)segments.get(j);
if (piece instanceof JFIFPieceSegmentExif) {
if (!APP1Written) {
APP1Written = true;
if (newBytes != null) {
byte[] markerBytes = convertShortToByteArray(65505, byteOrder);
if (newBytes.length > 65535)
throw new ExifOverflowException("APP1 Segment is too long: " + newBytes.length);
int markerLength = newBytes.length + 2;
byte[] markerLengthBytes = convertShortToByteArray(markerLength, byteOrder);
os.write(markerBytes);
os.write(markerLengthBytes);
os.write(newBytes);
}
}
} else {
piece.write(os);
}
}
} finally {
try {
os.close();
} catch (Exception e) {
Debug.debug((Throwable)e);
}
}
}
public static class ExifOverflowException extends ImageWriteException {
public ExifOverflowException(String s) {
super(s);
}
}
private byte[] writeExifSegment(TiffImageWriterBase writer, TiffOutputSet outputSet, boolean includeEXIFPrefix) throws IOException, ImageWriteException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
if (includeEXIFPrefix) {
os.write(EXIF_IDENTIFIER_CODE);
os.write(0);
os.write(0);
}
writer.write(os, outputSet);
return os.toByteArray();
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.formats.jpeg.iptc;
public class IPTCBlock {
public final int blockType;
public final byte[] blockNameBytes;
public final byte[] blockData;
public IPTCBlock(int blockType, byte[] blockNameBytes, byte[] blockData) {
this.blockData = blockData;
this.blockNameBytes = blockNameBytes;
this.blockType = blockType;
}
public boolean isIPTCBlock() {
return (this.blockType == 1028);
}
}

View file

@ -0,0 +1,265 @@
package org.apache.sanselan.formats.jpeg.iptc;
import org.apache.sanselan.formats.jpeg.JpegConstants;
public interface IPTCConstants extends JpegConstants {
public static final byte[] IPTC_PREFIX = new byte[] { 28, 2 };
public static final int IPTC_NON_EXTENDED_RECORD_MAXIMUM_SIZE = 32767;
public static final int IMAGE_RESOURCE_BLOCK_PHOTOSHOP2_INFO = 1000;
public static final int IMAGE_RESOURCE_BLOCK_MACINTOSH_PRINT_INFO = 1001;
public static final int IMAGE_RESOURCE_BLOCK_XML_DATA = 1002;
public static final int IMAGE_RESOURCE_BLOCK_PHOTOSHOP2_COLOR_TABLE = 1003;
public static final int IMAGE_RESOURCE_BLOCK_RESOLUTION_INFO = 1005;
public static final int IMAGE_RESOURCE_BLOCK_ALPHA_CHANNELS_NAMES = 1006;
public static final int IMAGE_RESOURCE_BLOCK_DISPLAY_INFO = 1007;
public static final int IMAGE_RESOURCE_BLOCK_PSTRING_CAPTION = 1008;
public static final int IMAGE_RESOURCE_BLOCK_BORDER_INFORMATION = 1009;
public static final int IMAGE_RESOURCE_BLOCK_BACKGROUND_COLOR = 1010;
public static final int IMAGE_RESOURCE_BLOCK_PRINT_FLAGS = 1011;
public static final int IMAGE_RESOURCE_BLOCK_BW_HALFTONING_INFO = 1012;
public static final int IMAGE_RESOURCE_BLOCK_COLOR_HALFTONING_INFO = 1013;
public static final int IMAGE_RESOURCE_BLOCK_DUOTONE_HALFTONING_INFO = 1014;
public static final int IMAGE_RESOURCE_BLOCK_BW_TRANSFER_FUNC = 1015;
public static final int IMAGE_RESOURCE_BLOCK_COLOR_TRANSFER_FUNCS = 1016;
public static final int IMAGE_RESOURCE_BLOCK_DUOTONE_TRANSFER_FUNCS = 1017;
public static final int IMAGE_RESOURCE_BLOCK_DUOTONE_IMAGE_INFO = 1018;
public static final int IMAGE_RESOURCE_BLOCK_EFFECTIVE_BW = 1019;
public static final int IMAGE_RESOURCE_BLOCK_OBSOLETE_PHOTOSHOP_TAG1 = 1020;
public static final int IMAGE_RESOURCE_BLOCK_EPS_OPTIONS = 1021;
public static final int IMAGE_RESOURCE_BLOCK_QUICK_MASK_INFO = 1022;
public static final int IMAGE_RESOURCE_BLOCK_OBSOLETE_PHOTOSHOP_TAG2 = 1023;
public static final int IMAGE_RESOURCE_BLOCK_LAYER_STATE_INFO = 1024;
public static final int IMAGE_RESOURCE_BLOCK_WORKING_PATH = 1025;
public static final int IMAGE_RESOURCE_BLOCK_LAYERS_GROUP_INFO = 1026;
public static final int IMAGE_RESOURCE_BLOCK_OBSOLETE_PHOTOSHOP_TAG3 = 1027;
public static final int IMAGE_RESOURCE_BLOCK_IPTC_DATA = 1028;
public static final int IMAGE_RESOURCE_BLOCK_RAW_IMAGE_MODE = 1029;
public static final int IMAGE_RESOURCE_BLOCK_JPEG_QUALITY = 1030;
public static final int IMAGE_RESOURCE_BLOCK_GRID_GUIDES_INFO = 1032;
public static final int IMAGE_RESOURCE_BLOCK_PHOTOSHOP_BGR_THUMBNAIL = 1033;
public static final int IMAGE_RESOURCE_BLOCK_COPYRIGHT_FLAG = 1034;
public static final int IMAGE_RESOURCE_BLOCK_URL = 1035;
public static final int IMAGE_RESOURCE_BLOCK_PHOTOSHOP_THUMBNAIL = 1036;
public static final int IMAGE_RESOURCE_BLOCK_GLOBAL_ANGLE = 1037;
public static final int IMAGE_RESOURCE_BLOCK_COLOR_SAMPLERS_RESOURCE = 1038;
public static final int IMAGE_RESOURCE_BLOCK_ICC_PROFILE = 1039;
public static final int IMAGE_RESOURCE_BLOCK_WATERMARK = 1040;
public static final int IMAGE_RESOURCE_BLOCK_ICC_UNTAGGED = 1041;
public static final int IMAGE_RESOURCE_BLOCK_EFFECTS_VISIBLE = 1042;
public static final int IMAGE_RESOURCE_BLOCK_SPOT_HALFTONE = 1043;
public static final int IMAGE_RESOURCE_BLOCK_IDS_BASE_VALUE = 1044;
public static final int IMAGE_RESOURCE_BLOCK_UNICODE_ALPHA_NAMES = 1045;
public static final int IMAGE_RESOURCE_BLOCK_INDEXED_COLOUR_TABLE_COUNT = 1046;
public static final int IMAGE_RESOURCE_BLOCK_TRANSPARENT_INDEX = 1047;
public static final int IMAGE_RESOURCE_BLOCK_GLOBAL_ALTITUDE = 1049;
public static final int IMAGE_RESOURCE_BLOCK_SLICES = 1050;
public static final int IMAGE_RESOURCE_BLOCK_WORKFLOW_URL = 1051;
public static final int IMAGE_RESOURCE_BLOCK_JUMP_TO_XPEP = 1052;
public static final int IMAGE_RESOURCE_BLOCK_ALPHA_IDENTIFIERS = 1053;
public static final int IMAGE_RESOURCE_BLOCK_URL_LIST = 1054;
public static final int IMAGE_RESOURCE_BLOCK_VERSION_INFO = 1057;
public static final int IMAGE_RESOURCE_BLOCK_EXIFINFO = 1058;
public static final int IMAGE_RESOURCE_BLOCK_EXIF_INFO2 = 1059;
public static final int IMAGE_RESOURCE_BLOCK_XMP = 1060;
public static final int IMAGE_RESOURCE_BLOCK_CAPTION_DIGEST = 1061;
public static final int IMAGE_RESOURCE_BLOCK_PRINT_SCALE = 1062;
public static final int IMAGE_RESOURCE_BLOCK_PIXEL_ASPECT_RATIO = 1064;
public static final int IMAGE_RESOURCE_BLOCK_LAYER_COMPS = 1065;
public static final int IMAGE_RESOURCE_BLOCK_ALTERNATE_DUOTONE_COLORS = 1066;
public static final int IMAGE_RESOURCE_BLOCK_ALTERNATE_SPOT_COLORS = 1067;
public static final int IMAGE_RESOURCE_BLOCK_CLIPPING_PATH_NAME = 2999;
public static final int IMAGE_RESOURCE_BLOCK_PRINT_FLAGS_INFO = 10000;
public static final int IPTC_RECORD_TAG_MARKER = 28;
public static final int IPTC_ENVELOPE_RECORD_NUMBER = 1;
public static final int IPTC_APPLICATION_2_RECORD_NUMBER = 2;
public static final IPTCType IPTC_TYPE_RECORD_VERSION = new IPTCType(0, "Record Version");
public static final IPTCType IPTC_TYPE_OBJECT_TYPE_REFERENCE = new IPTCType(3, "Object Type Reference");
public static final IPTCType IPTC_TYPE_OBJECT_ATTRIBUTE_REFERENCE = new IPTCType(4, "Object Attribute Reference");
public static final IPTCType IPTC_TYPE_OBJECT_NAME = new IPTCType(5, "Object Name");
public static final IPTCType IPTC_TYPE_EDIT_STATUS = new IPTCType(7, "Edit Status");
public static final IPTCType IPTC_TYPE_EDITORIAL_UPDATE = new IPTCType(8, "Editorial Update");
public static final IPTCType IPTC_TYPE_URGENCY = new IPTCType(10, "Urgency");
public static final IPTCType IPTC_TYPE_SUBJECT_REFERENCE = new IPTCType(12, "Subject Reference");
public static final IPTCType IPTC_TYPE_CATEGORY = new IPTCType(15, "Category");
public static final IPTCType IPTC_TYPE_SUPPLEMENTAL_CATEGORY = new IPTCType(20, "Supplemental Category");
public static final IPTCType IPTC_TYPE_FIXTURE_IDENTIFIER = new IPTCType(22, "Fixture Identifier");
public static final IPTCType IPTC_TYPE_KEYWORDS = new IPTCType(25, "Keywords");
public static final IPTCType IPTC_TYPE_CONTENT_LOCATION_CODE = new IPTCType(26, "Content Location Code");
public static final IPTCType IPTC_TYPE_CONTENT_LOCATION_NAME = new IPTCType(27, "Content Location Name");
public static final IPTCType IPTC_TYPE_RELEASE_DATE = new IPTCType(30, "Release Date");
public static final IPTCType IPTC_TYPE_RELEASE_TIME = new IPTCType(35, "Release Time");
public static final IPTCType IPTC_TYPE_EXPIRATION_DATE = new IPTCType(37, "Expiration Date");
public static final IPTCType IPTC_TYPE_EXPIRATION_TIME = new IPTCType(38, "Expiration Time");
public static final IPTCType IPTC_TYPE_SPECIAL_INSTRUCTIONS = new IPTCType(40, "Special Instructions");
public static final IPTCType IPTC_TYPE_ACTION_ADVISED = new IPTCType(42, "Action Advised");
public static final IPTCType IPTC_TYPE_REFERENCE_SERVICE = new IPTCType(45, "Reference Service");
public static final IPTCType IPTC_TYPE_REFERENCE_DATE = new IPTCType(47, "Reference Date");
public static final IPTCType IPTC_TYPE_REFERENCE_NUMBER = new IPTCType(50, "Reference Number");
public static final IPTCType IPTC_TYPE_DATE_CREATED = new IPTCType(55, "Date Created");
public static final IPTCType IPTC_TYPE_TIME_CREATED = new IPTCType(60, "Time Created");
public static final IPTCType IPTC_TYPE_DIGITAL_CREATION_DATE = new IPTCType(62, "Digital Creation Date");
public static final IPTCType IPTC_TYPE_DIGITAL_CREATION_TIME = new IPTCType(63, "Digital Creation Time");
public static final IPTCType IPTC_TYPE_ORIGINATING_PROGRAM = new IPTCType(65, "Originating Program");
public static final IPTCType IPTC_TYPE_PROGRAM_VERSION = new IPTCType(70, "Program Version");
public static final IPTCType IPTC_TYPE_OBJECT_CYCLE = new IPTCType(75, "Object Cycle");
public static final IPTCType IPTC_TYPE_BYLINE = new IPTCType(80, "By-line");
public static final IPTCType IPTC_TYPE_BYLINE_TITLE = new IPTCType(85, "By-line Title");
public static final IPTCType IPTC_TYPE_CITY = new IPTCType(90, "City");
public static final IPTCType IPTC_TYPE_SUBLOCATION = new IPTCType(92, "Sublocation");
public static final IPTCType IPTC_TYPE_PROVINCE_STATE = new IPTCType(95, "Province/State");
public static final IPTCType IPTC_TYPE_COUNTRY_PRIMARY_LOCATION_CODE = new IPTCType(100, "Country/Primary Location Code");
public static final IPTCType IPTC_TYPE_COUNTRY_PRIMARY_LOCATION_NAME = new IPTCType(101, "Country/Primary Location Name");
public static final IPTCType IPTC_TYPE_ORIGINAL_TRANSMISSION_REFERENCE = new IPTCType(103, "Original Transmission, Reference");
public static final IPTCType IPTC_TYPE_HEADLINE = new IPTCType(105, "Headline");
public static final IPTCType IPTC_TYPE_CREDIT = new IPTCType(110, "Credit");
public static final IPTCType IPTC_TYPE_SOURCE = new IPTCType(115, "Source");
public static final IPTCType IPTC_TYPE_COPYRIGHT_NOTICE = new IPTCType(116, "Copyright Notice");
public static final IPTCType IPTC_TYPE_CONTACT = new IPTCType(118, "Contact");
public static final IPTCType IPTC_TYPE_CAPTION_ABSTRACT = new IPTCType(120, "Caption/Abstract");
public static final IPTCType IPTC_TYPE_WRITER_EDITOR = new IPTCType(122, "Writer/Editor");
public static final IPTCType IPTC_TYPE_RASTERIZED_CAPTION = new IPTCType(125, "Rasterized Caption");
public static final IPTCType IPTC_TYPE_IMAGE_TYPE = new IPTCType(130, "ImageType");
public static final IPTCType IPTC_TYPE_IMAGE_ORIENTATION = new IPTCType(131, "Image Orientation");
public static final IPTCType IPTC_TYPE_LANGUAGE_IDENTIFIER = new IPTCType(135, "Language Identifier");
public static final IPTCType IPTC_TYPE_AUDIO_TYPE = new IPTCType(150, "Audio Type");
public static final IPTCType IPTC_TYPE_AUDIO_SAMPLING_RATE = new IPTCType(151, "Audio Sampling Rate");
public static final IPTCType IPTC_TYPE_AUDIO_SAMPLING_RESOLUTION = new IPTCType(152, "Audio Sampling Resolution");
public static final IPTCType IPTC_TYPE_AUDIO_DURATION = new IPTCType(153, "Audio Duration");
public static final IPTCType IPTC_TYPE_AUDIO_OUTCUE = new IPTCType(154, "Audio Outcue");
public static final IPTCType IPTC_TYPE_OBJECT_DATA_PREVIEW_FILE_FORMAT = new IPTCType(200, "Object Data Preview, File Format");
public static final IPTCType IPTC_TYPE_OBJECT_DATA_PREVIEW_FILE_FORMAT_VERSION = new IPTCType(201, "Object Data Preview, File Format Version");
public static final IPTCType IPTC_TYPE_OBJECT_DATA_PREVIEW_DATA = new IPTCType(202, "Object Data Preview Data");
public static final IPTCType[] IPTC_TYPES = new IPTCType[] {
IPTC_TYPE_RECORD_VERSION, IPTC_TYPE_OBJECT_TYPE_REFERENCE, IPTC_TYPE_OBJECT_ATTRIBUTE_REFERENCE, IPTC_TYPE_OBJECT_NAME, IPTC_TYPE_EDIT_STATUS, IPTC_TYPE_EDITORIAL_UPDATE, IPTC_TYPE_URGENCY, IPTC_TYPE_SUBJECT_REFERENCE, IPTC_TYPE_CATEGORY, IPTC_TYPE_SUPPLEMENTAL_CATEGORY,
IPTC_TYPE_FIXTURE_IDENTIFIER, IPTC_TYPE_KEYWORDS, IPTC_TYPE_CONTENT_LOCATION_CODE, IPTC_TYPE_CONTENT_LOCATION_NAME, IPTC_TYPE_RELEASE_DATE, IPTC_TYPE_RELEASE_TIME, IPTC_TYPE_EXPIRATION_DATE, IPTC_TYPE_EXPIRATION_TIME, IPTC_TYPE_SPECIAL_INSTRUCTIONS, IPTC_TYPE_ACTION_ADVISED,
IPTC_TYPE_REFERENCE_SERVICE, IPTC_TYPE_REFERENCE_DATE, IPTC_TYPE_REFERENCE_NUMBER, IPTC_TYPE_DATE_CREATED, IPTC_TYPE_TIME_CREATED, IPTC_TYPE_DIGITAL_CREATION_DATE, IPTC_TYPE_DIGITAL_CREATION_TIME, IPTC_TYPE_ORIGINATING_PROGRAM, IPTC_TYPE_PROGRAM_VERSION, IPTC_TYPE_OBJECT_CYCLE,
IPTC_TYPE_BYLINE, IPTC_TYPE_BYLINE_TITLE, IPTC_TYPE_CITY, IPTC_TYPE_SUBLOCATION, IPTC_TYPE_PROVINCE_STATE, IPTC_TYPE_COUNTRY_PRIMARY_LOCATION_CODE, IPTC_TYPE_COUNTRY_PRIMARY_LOCATION_NAME, IPTC_TYPE_ORIGINAL_TRANSMISSION_REFERENCE, IPTC_TYPE_HEADLINE, IPTC_TYPE_CREDIT,
IPTC_TYPE_SOURCE, IPTC_TYPE_COPYRIGHT_NOTICE, IPTC_TYPE_CONTACT, IPTC_TYPE_CAPTION_ABSTRACT, IPTC_TYPE_WRITER_EDITOR, IPTC_TYPE_RASTERIZED_CAPTION, IPTC_TYPE_IMAGE_TYPE, IPTC_TYPE_IMAGE_ORIENTATION, IPTC_TYPE_LANGUAGE_IDENTIFIER, IPTC_TYPE_AUDIO_TYPE,
IPTC_TYPE_AUDIO_SAMPLING_RATE, IPTC_TYPE_AUDIO_SAMPLING_RESOLUTION, IPTC_TYPE_AUDIO_DURATION, IPTC_TYPE_AUDIO_OUTCUE, IPTC_TYPE_OBJECT_DATA_PREVIEW_FILE_FORMAT, IPTC_TYPE_OBJECT_DATA_PREVIEW_FILE_FORMAT_VERSION, IPTC_TYPE_OBJECT_DATA_PREVIEW_DATA };
}

View file

@ -0,0 +1,202 @@
package org.apache.sanselan.formats.jpeg.iptc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.BinaryInputStream;
import org.apache.sanselan.common.BinaryOutputStream;
import org.apache.sanselan.util.Debug;
import org.apache.sanselan.util.ParamMap;
public class IPTCParser extends BinaryFileParser implements IPTCConstants {
private static final int APP13_BYTE_ORDER = 77;
public IPTCParser() {
setByteOrder(77);
}
public boolean isPhotoshopJpegSegment(byte[] segmentData) {
if (!compareByteArrays(segmentData, 0, PHOTOSHOP_IDENTIFICATION_STRING, 0, PHOTOSHOP_IDENTIFICATION_STRING.length))
return false;
int index = PHOTOSHOP_IDENTIFICATION_STRING.length;
if (index + CONST_8BIM.length > segmentData.length)
return false;
if (!compareByteArrays(segmentData, index, CONST_8BIM, 0, CONST_8BIM.length))
return false;
return true;
}
public PhotoshopApp13Data parsePhotoshopSegment(byte[] bytes, Map params) throws ImageReadException, IOException {
boolean strict = ParamMap.getParamBoolean(params, "STRICT", false);
boolean verbose = ParamMap.getParamBoolean(params, "VERBOSE", false);
return parsePhotoshopSegment(bytes, verbose, strict);
}
public PhotoshopApp13Data parsePhotoshopSegment(byte[] bytes, boolean verbose, boolean strict) throws ImageReadException, IOException {
ArrayList records = new ArrayList();
List allBlocks = parseAllBlocks(bytes, verbose, strict);
for (int i = 0; i < allBlocks.size(); i++) {
IPTCBlock block = (IPTCBlock)allBlocks.get(i);
if (block.isIPTCBlock())
records.addAll(parseIPTCBlock(block.blockData, verbose));
}
return new PhotoshopApp13Data(records, allBlocks);
}
protected List parseIPTCBlock(byte[] bytes, boolean verbose) throws ImageReadException, IOException {
ArrayList elements = new ArrayList();
int index = 0;
while (index + 1 < bytes.length) {
int tagMarker = 0xFF & bytes[index++];
if (verbose)
Debug.debug("tagMarker", tagMarker + " (0x" + Integer.toHexString(tagMarker) + ")");
if (tagMarker != 28) {
if (verbose)
System.out.println("Unexpected record tag marker in IPTC data.");
return elements;
}
int recordNumber = 0xFF & bytes[index++];
if (verbose)
Debug.debug("recordNumber", recordNumber + " (0x" + Integer.toHexString(recordNumber) + ")");
if (recordNumber != 2)
continue;
int recordType = 0xFF & bytes[index];
if (verbose)
Debug.debug("recordType", recordType + " (0x" + Integer.toHexString(recordType) + ")");
index++;
int recordSize = convertByteArrayToShort("recordSize", index, bytes);
index += 2;
boolean extendedDataset = (recordSize > 32767);
int dataFieldCountLength = recordSize & 0x7FFF;
if (extendedDataset && verbose)
Debug.debug("extendedDataset. dataFieldCountLength: " + dataFieldCountLength);
if (extendedDataset)
return elements;
byte[] recordData = readBytearray("recordData", bytes, index, recordSize);
index += recordSize;
if (recordType == 0) {
if (verbose)
System.out.println("ignore record version record! " + elements.size());
continue;
}
String value = new String(recordData, "ISO-8859-1");
IPTCType iptcType = IPTCTypeLookup.getIptcType(recordType);
IPTCRecord element = new IPTCRecord(iptcType, value);
elements.add(element);
}
return elements;
}
protected List parseAllBlocks(byte[] bytes, boolean verbose, boolean strict) throws ImageReadException, IOException {
List blocks = new ArrayList();
BinaryInputStream bis = new BinaryInputStream(bytes, 77);
byte[] idString = bis.readByteArray(PHOTOSHOP_IDENTIFICATION_STRING.length, "App13 Segment missing identification string");
if (!compareByteArrays(idString, PHOTOSHOP_IDENTIFICATION_STRING))
throw new ImageReadException("Not a Photoshop App13 Segment");
while (true) {
byte[] blockNameBytes;
byte[] imageResourceBlockSignature = bis.readByteArray(CONST_8BIM.length, "App13 Segment missing identification string", false, false);
if (null == imageResourceBlockSignature)
break;
if (!compareByteArrays(imageResourceBlockSignature, CONST_8BIM))
throw new ImageReadException("Invalid Image Resource Block Signature");
int blockType = bis.read2ByteInteger("Image Resource Block missing type");
if (verbose)
Debug.debug("blockType", blockType + " (0x" + Integer.toHexString(blockType) + ")");
int blockNameLength = bis.read1ByteInteger("Image Resource Block missing name length");
if (verbose && blockNameLength > 0)
Debug.debug("blockNameLength", blockNameLength + " (0x" + Integer.toHexString(blockNameLength) + ")");
if (blockNameLength == 0) {
bis.read1ByteInteger("Image Resource Block has invalid name");
blockNameBytes = new byte[0];
} else {
blockNameBytes = bis.readByteArray(blockNameLength, "Invalid Image Resource Block name", verbose, strict);
if (null == blockNameBytes)
break;
if (blockNameLength % 2 == 0)
bis.read1ByteInteger("Image Resource Block missing padding byte");
}
int blockSize = bis.read4ByteInteger("Image Resource Block missing size");
if (verbose)
Debug.debug("blockSize", blockSize + " (0x" + Integer.toHexString(blockSize) + ")");
byte[] blockData = bis.readByteArray(blockSize, "Invalid Image Resource Block data", verbose, strict);
if (null == blockData)
break;
blocks.add(new IPTCBlock(blockType, blockNameBytes, blockData));
if (blockSize % 2 != 0)
bis.read1ByteInteger("Image Resource Block missing padding byte");
}
return blocks;
}
public byte[] writePhotoshopApp13Segment(PhotoshopApp13Data data) throws IOException, ImageWriteException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
BinaryOutputStream bos = new BinaryOutputStream(os);
bos.write(PHOTOSHOP_IDENTIFICATION_STRING);
List blocks = data.getRawBlocks();
for (int i = 0; i < blocks.size(); i++) {
IPTCBlock block = (IPTCBlock)blocks.get(i);
bos.write(CONST_8BIM);
if (block.blockType < 0 || block.blockType > 65535)
throw new ImageWriteException("Invalid IPTC block type.");
bos.write2ByteInteger(block.blockType);
if (block.blockNameBytes.length > 255)
throw new ImageWriteException("IPTC block name is too long: " + block.blockNameBytes.length);
bos.write(block.blockNameBytes.length);
bos.write(block.blockNameBytes);
if (block.blockNameBytes.length % 2 == 0)
bos.write(0);
if (block.blockData.length > 32767)
throw new ImageWriteException("IPTC block data is too long: " + block.blockData.length);
bos.write4ByteInteger(block.blockData.length);
bos.write(block.blockData);
if (block.blockData.length % 2 == 1)
bos.write(0);
}
bos.flush();
return os.toByteArray();
}
public byte[] writeIPTCBlock(List elements) throws ImageWriteException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BinaryOutputStream bos = new BinaryOutputStream(baos, getByteOrder());
bos.write(28);
bos.write(2);
bos.write(IPTC_TYPE_RECORD_VERSION.type);
bos.write2Bytes(2);
bos.write2Bytes(2);
elements = new ArrayList(elements);
Comparator comparator = new Comparator() {
public int compare(Object o1, Object o2) {
IPTCRecord e1 = (IPTCRecord)o1;
IPTCRecord e2 = (IPTCRecord)o2;
return e2.iptcType.type - e1.iptcType.type;
}
};
Collections.sort(elements, comparator);
for (int i = 0; i < elements.size(); i++) {
IPTCRecord element = (IPTCRecord)elements.get(i);
if (element.iptcType.type != IPTC_TYPE_RECORD_VERSION.type) {
bos.write(28);
bos.write(2);
if (element.iptcType.type < 0 || element.iptcType.type > 255)
throw new ImageWriteException("Invalid record type: " + element.iptcType.type);
bos.write(element.iptcType.type);
byte[] recordData = element.value.getBytes("ISO-8859-1");
if (!new String(recordData, "ISO-8859-1").equals(element.value))
throw new ImageWriteException("Invalid record value, not ISO-8859-1");
bos.write2Bytes(recordData.length);
bos.write(recordData);
}
}
byte[] blockData = baos.toByteArray();
return blockData;
}
}

View file

@ -0,0 +1,30 @@
package org.apache.sanselan.formats.jpeg.iptc;
import java.util.Comparator;
public class IPTCRecord {
public final IPTCType iptcType;
public final String value;
public IPTCRecord(IPTCType iptcType, String value) {
this.iptcType = iptcType;
this.value = value;
}
public String getValue() {
return this.value;
}
public String getIptcTypeName() {
return this.iptcType.name;
}
public static final Comparator COMPARATOR = new Comparator() {
public int compare(Object o1, Object o2) {
IPTCRecord e1 = (IPTCRecord)o1;
IPTCRecord e2 = (IPTCRecord)o2;
return e1.iptcType.type - e2.iptcType.type;
}
};
}

View file

@ -0,0 +1,22 @@
package org.apache.sanselan.formats.jpeg.iptc;
import org.apache.sanselan.formats.jpeg.JpegConstants;
public class IPTCType implements JpegConstants, IPTCConstants {
public final int type;
public final String name;
public IPTCType(int type, String name) {
this.type = type;
this.name = name;
}
public String toString() {
return this.name + " (" + this.type + ")";
}
public static IPTCType getUnknown(int type) {
return new IPTCType(type, "Unknown");
}
}

View file

@ -0,0 +1,23 @@
package org.apache.sanselan.formats.jpeg.iptc;
import java.util.HashMap;
import java.util.Map;
public abstract class IPTCTypeLookup implements IPTCConstants {
private static final Map IPTC_TYPE_MAP = new HashMap();
static {
for (int i = 0; i < IPTC_TYPES.length; i++) {
IPTCType iptcType = IPTC_TYPES[i];
Integer key = new Integer(iptcType.type);
IPTC_TYPE_MAP.put(key, iptcType);
}
}
public static final IPTCType getIptcType(int type) {
Integer key = new Integer(type);
if (!IPTC_TYPE_MAP.containsKey(key))
return IPTCType.getUnknown(type);
return (IPTCType)IPTC_TYPE_MAP.get(key);
}
}

View file

@ -0,0 +1,91 @@
package org.apache.sanselan.formats.jpeg.iptc;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceArray;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
import org.apache.sanselan.formats.jpeg.xmp.JpegRewriter;
public class JpegIptcRewriter extends JpegRewriter implements IPTCConstants {
public void removeIPTC(File src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceFile(src);
removeIPTC(byteSource, os);
}
public void removeIPTC(byte[] src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceArray(src);
removeIPTC(byteSource, os);
}
public void removeIPTC(InputStream src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceInputStream(src, null);
removeIPTC(byteSource, os);
}
public void removeIPTC(ByteSource byteSource, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
JpegRewriter.JFIFPieces jfifPieces = analyzeJFIF(byteSource);
List oldPieces = jfifPieces.pieces;
List photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces);
if (photoshopApp13Segments.size() > 1)
throw new ImageReadException("Image contains more than one Photoshop App13 segment.");
List newPieces = removePhotoshopApp13Segments(oldPieces);
if (photoshopApp13Segments.size() == 1) {
JpegRewriter.JFIFPieceSegment oldSegment = (JpegRewriter.JFIFPieceSegment)photoshopApp13Segments.get(0);
Map params = new HashMap();
PhotoshopApp13Data oldData = new IPTCParser().parsePhotoshopSegment(oldSegment.segmentData, params);
List newBlocks = oldData.getNonIptcBlocks();
List newRecords = new ArrayList();
PhotoshopApp13Data newData = new PhotoshopApp13Data(newRecords, newBlocks);
byte[] segmentBytes = new IPTCParser().writePhotoshopApp13Segment(newData);
JpegRewriter.JFIFPieceSegment newSegment = new JpegRewriter.JFIFPieceSegment(oldSegment.marker, segmentBytes);
newPieces.add(oldPieces.indexOf(oldSegment), newSegment);
}
writeSegments(os, newPieces);
}
public void writeIPTC(byte[] src, OutputStream os, PhotoshopApp13Data newData) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceArray(src);
writeIPTC(byteSource, os, newData);
}
public void writeIPTC(InputStream src, OutputStream os, PhotoshopApp13Data newData) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceInputStream(src, null);
writeIPTC(byteSource, os, newData);
}
public void writeIPTC(File src, OutputStream os, PhotoshopApp13Data newData) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceFile(src);
writeIPTC(byteSource, os, newData);
}
public void writeIPTC(ByteSource byteSource, OutputStream os, PhotoshopApp13Data newData) throws ImageReadException, IOException, ImageWriteException {
JpegRewriter.JFIFPieces jfifPieces = analyzeJFIF(byteSource);
List oldPieces = jfifPieces.pieces;
List photoshopApp13Segments = findPhotoshopApp13Segments(oldPieces);
if (photoshopApp13Segments.size() > 1)
throw new ImageReadException("Image contains more than one Photoshop App13 segment.");
List newPieces = removePhotoshopApp13Segments(oldPieces);
List newBlocks = newData.getNonIptcBlocks();
byte[] newBlockBytes = new IPTCParser().writeIPTCBlock(newData.getRecords());
int blockType = 1028;
byte[] blockNameBytes = new byte[0];
IPTCBlock newBlock = new IPTCBlock(blockType, blockNameBytes, newBlockBytes);
newBlocks.add(newBlock);
newData = new PhotoshopApp13Data(newData.getRecords(), newBlocks);
byte[] segmentBytes = new IPTCParser().writePhotoshopApp13Segment(newData);
JpegRewriter.JFIFPieceSegment newSegment = new JpegRewriter.JFIFPieceSegment(65517, segmentBytes);
newPieces = insertAfterLastAppSegments(newPieces, Arrays.asList(new JpegRewriter.JFIFPieceSegment[] { newSegment }));
writeSegments(os, newPieces);
}
}

View file

@ -0,0 +1,33 @@
package org.apache.sanselan.formats.jpeg.iptc;
import java.util.ArrayList;
import java.util.List;
public class PhotoshopApp13Data implements IPTCConstants {
private final List records;
private final List rawBlocks;
public PhotoshopApp13Data(List records, List rawBlocks) {
this.rawBlocks = rawBlocks;
this.records = records;
}
public List getRecords() {
return new ArrayList(this.records);
}
public List getRawBlocks() {
return new ArrayList(this.rawBlocks);
}
public List getNonIptcBlocks() {
List result = new ArrayList();
for (int i = 0; i < this.rawBlocks.size(); i++) {
IPTCBlock block = (IPTCBlock)this.rawBlocks.get(i);
if (!block.isIPTCBlock())
result.add(block);
}
return result;
}
}

View file

@ -0,0 +1,15 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
public class APPNSegment extends GenericSegment {
public APPNSegment(int marker, int marker_length, InputStream is) throws ImageReadException, IOException {
super(marker, marker_length, is);
}
public String getDescription() {
return "APPN (APP" + (this.marker - 65504) + ") (" + getSegmentType() + ")";
}
}

View file

@ -0,0 +1,33 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
import org.apache.sanselan.formats.jpeg.iptc.IPTCParser;
import org.apache.sanselan.formats.jpeg.iptc.PhotoshopApp13Data;
public class App13Segment extends APPNSegment {
protected final JpegImageParser parser;
public App13Segment(JpegImageParser parser, int marker, byte[] segmentData) throws ImageReadException, IOException {
this(parser, marker, segmentData.length, new ByteArrayInputStream(segmentData));
}
public App13Segment(JpegImageParser parser, int marker, int marker_length, InputStream is) throws ImageReadException, IOException {
super(marker, marker_length, is);
this.parser = parser;
}
public boolean isPhotoshopJpegSegment() throws ImageReadException, IOException {
return new IPTCParser().isPhotoshopJpegSegment(this.bytes);
}
public PhotoshopApp13Data parsePhotoshopSegment(Map params) throws ImageReadException, IOException {
if (!new IPTCParser().isPhotoshopJpegSegment(this.bytes))
return null;
return new IPTCParser().parsePhotoshopSegment(this.bytes, params);
}
}

View file

@ -0,0 +1,41 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
public class App2Segment extends APPNSegment implements Comparable {
public final byte[] icc_bytes;
public final int cur_marker;
public final int num_markers;
public App2Segment(int marker, byte[] segmentData) throws ImageReadException, IOException {
this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
}
public App2Segment(int marker, int marker_length, InputStream is2) throws ImageReadException, IOException {
super(marker, marker_length, is2);
if (startsWith(this.bytes, JpegImageParser.icc_profile_label)) {
InputStream is = new ByteArrayInputStream(this.bytes);
readAndVerifyBytes(is, JpegImageParser.icc_profile_label, "Not a Valid App2 Segment: missing ICC Profile label");
this.cur_marker = readByte("cur_marker", is, "Not a valid App2 Marker");
this.num_markers = readByte("num_markers", is, "Not a valid App2 Marker");
marker_length -= JpegImageParser.icc_profile_label.length;
marker_length -= 2;
this.icc_bytes = readByteArray("App2 Data", marker_length, is, "Invalid App2 Segment: insufficient data");
} else {
this.cur_marker = -1;
this.num_markers = -1;
this.icc_bytes = null;
}
}
public int compareTo(Object o) {
App2Segment other = (App2Segment)o;
return this.cur_marker - other.cur_marker;
}
}

View file

@ -0,0 +1,29 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import org.apache.sanselan.ImageReadException;
public abstract class GenericSegment extends Segment {
public final byte[] bytes;
public GenericSegment(int marker, int marker_length, InputStream is) throws ImageReadException, IOException {
super(marker, marker_length);
this.bytes = readByteArray("Segment Data", marker_length, is, "Invalid Segment: insufficient data");
}
public GenericSegment(int marker, byte[] bytes) throws ImageReadException, IOException {
super(marker, bytes.length);
this.bytes = bytes;
}
public void dump(PrintWriter pw) {
dump(pw, 0);
}
public void dump(PrintWriter pw, int start) {
for (int i = 0; i < 50 && i + start < this.bytes.length; i++)
debugNumber(pw, "\t" + (i + start), this.bytes[i + start]);
}
}

View file

@ -0,0 +1,52 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.formats.jpeg.JpegConstants;
public class JFIFSegment extends Segment implements JpegConstants {
public final int jfifMajorVersion;
public final int jfifMinorVersion;
public final int densityUnits;
public final int xDensity;
public final int yDensity;
public final int xThumbnail;
public final int yThumbnail;
public final int thumbnailSize;
public String getDescription() {
return "JFIF (" + getSegmentType() + ")";
}
public JFIFSegment(int marker, byte[] segmentData) throws ImageReadException, IOException {
this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
}
public JFIFSegment(int marker, int marker_length, InputStream is) throws ImageReadException, IOException {
super(marker, marker_length);
byte[] signature = readBytes(is, JFIF0_SIGNATURE.length);
if (!compareByteArrays(signature, JFIF0_SIGNATURE) && !compareByteArrays(signature, JFIF0_SIGNATURE_ALTERNATIVE))
throw new ImageReadException("Not a Valid JPEG File: missing JFIF string");
this.jfifMajorVersion = readByte("JFIF_major_version", is, "Not a Valid JPEG File");
this.jfifMinorVersion = readByte("JFIF_minor_version", is, "Not a Valid JPEG File");
this.densityUnits = readByte("density_units", is, "Not a Valid JPEG File");
this.xDensity = read2Bytes("x_density", is, "Not a Valid JPEG File");
this.yDensity = read2Bytes("y_density", is, "Not a Valid JPEG File");
this.xThumbnail = readByte("x_thumbnail", is, "Not a Valid JPEG File");
this.yThumbnail = readByte("y_thumbnail", is, "Not a Valid JPEG File");
this.thumbnailSize = this.xThumbnail * this.yThumbnail;
if (this.thumbnailSize > 0)
skipBytes(is, this.thumbnailSize, "Not a Valid JPEG File: missing thumbnail");
if (getDebug())
System.out.println("");
}
}

View file

@ -0,0 +1,37 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
public class SOFNSegment extends Segment {
public final int width;
public final int height;
public final int numberOfComponents;
public final int precision;
public SOFNSegment(int marker, byte[] segmentData) throws ImageReadException, IOException {
this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
}
public SOFNSegment(int marker, int marker_length, InputStream is) throws ImageReadException, IOException {
super(marker, marker_length);
if (getDebug())
System.out.println("SOF0Segment marker_length: " + marker_length);
this.precision = readByte("Data_precision", is, "Not a Valid JPEG File");
this.height = read2Bytes("Image_height", is, "Not a Valid JPEG File");
this.width = read2Bytes("Image_Width", is, "Not a Valid JPEG File");
this.numberOfComponents = readByte("Number_of_components", is, "Not a Valid JPEG File");
skipBytes(is, marker_length - 6, "Not a Valid JPEG File: SOF0 Segment");
if (getDebug())
System.out.println("");
}
public String getDescription() {
return "SOFN (SOF" + (this.marker - 65472) + ") (" + getSegmentType() + ")";
}
}

View file

@ -0,0 +1,35 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.util.Debug;
public class SOSSegment extends Segment {
public SOSSegment(int marker, int marker_length, InputStream is) throws ImageReadException, IOException {
super(marker, marker_length);
if (getDebug())
System.out.println("SOSSegment marker_length: " + marker_length);
Debug.debug("SOS", marker_length);
int number_of_components_in_scan = readByte("number_of_components_in_scan", is, "Not a Valid JPEG File");
Debug.debug("number_of_components_in_scan", number_of_components_in_scan);
for (int i = 0; i < number_of_components_in_scan; i++) {
int scan_component_selector = readByte("scan_component_selector", is, "Not a Valid JPEG File");
Debug.debug("scan_component_selector", scan_component_selector);
int ac_dc_entrooy_coding_table_selector = readByte("ac_dc_entrooy_coding_table_selector", is, "Not a Valid JPEG File");
Debug.debug("ac_dc_entrooy_coding_table_selector", ac_dc_entrooy_coding_table_selector);
}
int start_of_spectral_selection = readByte("start_of_spectral_selection", is, "Not a Valid JPEG File");
Debug.debug("start_of_spectral_selection", start_of_spectral_selection);
int end_of_spectral_selection = readByte("end_of_spectral_selection", is, "Not a Valid JPEG File");
Debug.debug("end_of_spectral_selection", end_of_spectral_selection);
int successive_approximation_bit_position = readByte("successive_approximation_bit_position", is, "Not a Valid JPEG File");
Debug.debug("successive_approximation_bit_position", successive_approximation_bit_position);
if (getDebug())
System.out.println("");
}
public String getDescription() {
return "SOS (" + getSegmentType() + ")";
}
}

View file

@ -0,0 +1,103 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.PrintWriter;
import org.apache.sanselan.common.BinaryFileParser;
public abstract class Segment extends BinaryFileParser {
public final int marker;
public final int length;
public Segment(int marker, int length) {
this.marker = marker;
this.length = length;
}
public void dump(PrintWriter pw) {}
public abstract String getDescription();
public String toString() {
return "[Segment: " + getDescription() + "]";
}
public String getSegmentType() {
switch (this.marker) {
case 65472:
return "Start Of Frame, Baseline DCT, Huffman coding";
case 65473:
return "Start Of Frame, Extended sequential DCT, Huffman coding";
case 65474:
return "Start Of Frame, Progressive DCT, Huffman coding";
case 65475:
return "Start Of Frame, Lossless (sequential), Huffman coding";
case 65477:
return "Start Of Frame, Differential sequential DCT, Huffman coding";
case 65478:
return "Start Of Frame, Differential progressive DCT, Huffman coding";
case 65479:
return "Start Of Frame, Differential lossless (sequential), Huffman coding";
case 65480:
return "Start Of Frame, Reserved for JPEG extensions, arithmetic coding";
case 65481:
return "Start Of Frame, Extended sequential DCT, arithmetic coding";
case 65482:
return "Start Of Frame, Progressive DCT, arithmetic coding";
case 65483:
return "Start Of Frame, Lossless (sequential), arithmetic coding";
case 65485:
return "Start Of Frame, Differential sequential DCT, arithmetic coding";
case 65486:
return "Start Of Frame, Differential progressive DCT, arithmetic coding";
case 65487:
return "Start Of Frame, Differential lossless (sequential), arithmetic coding";
case 65476:
return "Define Huffman table(s)";
case 65484:
return "Define arithmetic coding conditioning(s)";
case 65488:
return "Restart with modulo 8 count 0";
case 65489:
return "Restart with modulo 8 count 1";
case 65490:
return "Restart with modulo 8 count 2";
case 65491:
return "Restart with modulo 8 count 3";
case 65492:
return "Restart with modulo 8 count 4";
case 65493:
return "Restart with modulo 8 count 5";
case 65494:
return "Restart with modulo 8 count 6";
case 65495:
return "Restart with modulo 8 count 7";
case 65496:
return "Start of image";
case 65497:
return "End of image";
case 65498:
return "Start of scan";
case 65499:
return "Define quantization table(s)";
case 65500:
return "Define number of lines";
case 65501:
return "Define restart interval";
case 65502:
return "Define hierarchical progression";
case 65503:
return "Expand reference component(s)";
case 65534:
return "Comment";
case 65281:
return "For temporary private use in arithmetic coding";
}
if (this.marker >= 65282 && this.marker <= 65471)
return "Reserved";
if (this.marker >= 65504 && this.marker <= 65519)
return "APP" + (this.marker - 65504);
if (this.marker >= 65520 && this.marker <= 65533)
return "JPG" + (this.marker - 65504);
return "Unknown";
}
}

View file

@ -0,0 +1,19 @@
package org.apache.sanselan.formats.jpeg.segments;
import java.io.IOException;
import java.io.InputStream;
import org.apache.sanselan.ImageReadException;
public class UnknownSegment extends GenericSegment {
public UnknownSegment(int marker, int marker_length, InputStream is) throws ImageReadException, IOException {
super(marker, marker_length, is);
}
public UnknownSegment(int marker, byte[] bytes) throws ImageReadException, IOException {
super(marker, bytes);
}
public String getDescription() {
return "Unknown (" + getSegmentType() + ")";
}
}

View file

@ -0,0 +1,260 @@
package org.apache.sanselan.formats.jpeg.xmp;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.jpeg.JpegConstants;
import org.apache.sanselan.formats.jpeg.JpegUtils;
import org.apache.sanselan.formats.jpeg.iptc.IPTCParser;
public class JpegRewriter extends BinaryFileParser implements JpegConstants {
private static final int JPEG_BYTE_ORDER = 77;
public JpegRewriter() {
setByteOrder(77);
}
protected static class JFIFPieces {
public final List pieces;
public final List segmentPieces;
public JFIFPieces(List pieces, List segmentPieces) {
this.pieces = pieces;
this.segmentPieces = segmentPieces;
}
}
protected static abstract class JFIFPiece {
protected abstract void write(OutputStream param1OutputStream) throws IOException;
public String toString() {
return "[" + getClass().getName() + "]";
}
}
protected static class JFIFPieceSegment extends JFIFPiece {
public final int marker;
public final byte[] markerBytes;
public final byte[] segmentLengthBytes;
public final byte[] segmentData;
public JFIFPieceSegment(int marker, byte[] segmentData) {
this(marker, JpegRewriter.int2ToByteArray(marker, 77), JpegRewriter.int2ToByteArray(segmentData.length + 2, 77), segmentData);
}
public JFIFPieceSegment(int marker, byte[] markerBytes, byte[] segmentLengthBytes, byte[] segmentData) {
this.marker = marker;
this.markerBytes = markerBytes;
this.segmentLengthBytes = segmentLengthBytes;
this.segmentData = segmentData;
}
public String toString() {
return "[" + getClass().getName() + " (0x" + Integer.toHexString(this.marker) + ")]";
}
protected void write(OutputStream os) throws IOException {
os.write(this.markerBytes);
os.write(this.segmentLengthBytes);
os.write(this.segmentData);
}
public boolean isApp1Segment() {
return (this.marker == 65505);
}
public boolean isAppSegment() {
return (this.marker >= 65504 && this.marker <= 65519);
}
public boolean isExifSegment() {
if (this.marker != 65505)
return false;
if (!BinaryFileParser.byteArrayHasPrefix(this.segmentData, JpegConstants.EXIF_IDENTIFIER_CODE))
return false;
return true;
}
public boolean isPhotoshopApp13Segment() {
if (this.marker != 65517)
return false;
if (!new IPTCParser().isPhotoshopJpegSegment(this.segmentData))
return false;
return true;
}
public boolean isXmpSegment() {
if (this.marker != 65505)
return false;
if (!BinaryFileParser.byteArrayHasPrefix(this.segmentData, JpegConstants.XMP_IDENTIFIER))
return false;
return true;
}
}
protected static class JFIFPieceImageData extends JFIFPiece {
public final byte[] markerBytes;
public final byte[] imageData;
public JFIFPieceImageData(byte[] markerBytes, byte[] imageData) {
this.markerBytes = markerBytes;
this.imageData = imageData;
}
protected void write(OutputStream os) throws IOException {
os.write(this.markerBytes);
os.write(this.imageData);
}
}
protected JFIFPieces analyzeJFIF(ByteSource byteSource) throws ImageReadException, IOException {
final ArrayList pieces = new ArrayList();
final List segmentPieces = new ArrayList();
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
public boolean beginSOS() {
return true;
}
public void visitSOS(int marker, byte[] markerBytes, byte[] imageData) {
pieces.add(new JpegRewriter.JFIFPieceImageData(markerBytes, imageData));
}
public boolean visitSegment(int marker, byte[] markerBytes, int segmentLength, byte[] segmentLengthBytes, byte[] segmentData) throws ImageReadException, IOException {
JpegRewriter.JFIFPiece piece = new JpegRewriter.JFIFPieceSegment(marker, markerBytes, segmentLengthBytes, segmentData);
pieces.add(piece);
segmentPieces.add(piece);
return true;
}
};
new JpegUtils().traverseJFIF(byteSource, visitor);
return new JFIFPieces(pieces, segmentPieces);
}
private static final SegmentFilter EXIF_SEGMENT_FILTER = new SegmentFilter() {
public boolean filter(JpegRewriter.JFIFPieceSegment segment) {
return segment.isExifSegment();
}
};
private static final SegmentFilter XMP_SEGMENT_FILTER = new SegmentFilter() {
public boolean filter(JpegRewriter.JFIFPieceSegment segment) {
return segment.isXmpSegment();
}
};
private static final SegmentFilter PHOTOSHOP_APP13_SEGMENT_FILTER = new SegmentFilter() {
public boolean filter(JpegRewriter.JFIFPieceSegment segment) {
return segment.isPhotoshopApp13Segment();
}
};
protected List removeXmpSegments(List segments) {
return filterSegments(segments, XMP_SEGMENT_FILTER);
}
protected List removePhotoshopApp13Segments(List segments) {
return filterSegments(segments, PHOTOSHOP_APP13_SEGMENT_FILTER);
}
protected List findPhotoshopApp13Segments(List segments) {
return filterSegments(segments, PHOTOSHOP_APP13_SEGMENT_FILTER, true);
}
protected List removeExifSegments(List segments) {
return filterSegments(segments, EXIF_SEGMENT_FILTER);
}
protected List filterSegments(List segments, SegmentFilter filter) {
return filterSegments(segments, filter, false);
}
protected List filterSegments(List segments, SegmentFilter filter, boolean reverse) {
List result = new ArrayList();
for (int i = 0; i < segments.size(); i++) {
JFIFPiece piece = (JFIFPiece)segments.get(i);
if (piece instanceof JFIFPieceSegment) {
if (filter.filter((JFIFPieceSegment)piece) ^ (!reverse))
result.add(piece);
} else if (!reverse) {
result.add(piece);
}
}
return result;
}
protected List insertBeforeFirstAppSegments(List segments, List newSegments) throws ImageWriteException {
int firstAppIndex = -1;
for (int i = 0; i < segments.size(); i++) {
JFIFPiece piece = (JFIFPiece)segments.get(i);
if (piece instanceof JFIFPieceSegment) {
JFIFPieceSegment segment = (JFIFPieceSegment)piece;
if (segment.isAppSegment())
if (firstAppIndex == -1)
firstAppIndex = i;
}
}
List result = new ArrayList(segments);
if (firstAppIndex == -1)
throw new ImageWriteException("JPEG file has no APP segments.");
result.addAll(firstAppIndex, newSegments);
return result;
}
protected List insertAfterLastAppSegments(List segments, List newSegments) throws ImageWriteException {
int lastAppIndex = -1;
for (int i = 0; i < segments.size(); i++) {
JFIFPiece piece = (JFIFPiece)segments.get(i);
if (piece instanceof JFIFPieceSegment) {
JFIFPieceSegment segment = (JFIFPieceSegment)piece;
if (segment.isAppSegment())
lastAppIndex = i;
}
}
List result = new ArrayList(segments);
if (lastAppIndex == -1) {
if (segments.size() < 1)
throw new ImageWriteException("JPEG file has no APP segments.");
result.addAll(1, newSegments);
} else {
result.addAll(lastAppIndex + 1, newSegments);
}
return result;
}
protected void writeSegments(OutputStream os, List segments) throws ImageWriteException, IOException {
try {
os.write(SOI);
for (int i = 0; i < segments.size(); i++) {
JFIFPiece piece = (JFIFPiece)segments.get(i);
piece.write(os);
}
os.close();
os = null;
} finally {
try {
if (os != null)
os.close();
} catch (Exception e) {}
}
}
public static class JpegSegmentOverflowException extends ImageWriteException {
public JpegSegmentOverflowException(String s) {
super(s);
}
}
private static interface SegmentFilter {
boolean filter(JpegRewriter.JFIFPieceSegment param1JFIFPieceSegment);
}
}

View file

@ -0,0 +1,39 @@
package org.apache.sanselan.formats.jpeg.xmp;
import java.io.UnsupportedEncodingException;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.formats.jpeg.JpegConstants;
public class JpegXmpParser extends BinaryFileParser implements JpegConstants {
public JpegXmpParser() {
setByteOrder(77);
}
public boolean isXmpJpegSegment(byte[] segmentData) {
int index = 0;
if (segmentData.length < XMP_IDENTIFIER.length)
return false;
for (; index < XMP_IDENTIFIER.length; index++) {
if (segmentData[index] < XMP_IDENTIFIER[index])
return false;
}
return true;
}
public String parseXmpJpegSegment(byte[] segmentData) throws ImageReadException {
int index = 0;
if (segmentData.length < XMP_IDENTIFIER.length)
throw new ImageReadException("Invalid JPEG XMP Segment.");
for (; index < XMP_IDENTIFIER.length; index++) {
if (segmentData[index] < XMP_IDENTIFIER[index])
throw new ImageReadException("Invalid JPEG XMP Segment.");
}
try {
String xml = new String(segmentData, index, segmentData.length - index, "utf-8");
return xml;
} catch (UnsupportedEncodingException e) {
throw new ImageReadException("Invalid JPEG XMP Segment.");
}
}
}

View file

@ -0,0 +1,78 @@
package org.apache.sanselan.formats.jpeg.xmp;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceArray;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
public class JpegXmpRewriter extends JpegRewriter {
public void removeXmpXml(File src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceFile(src);
removeXmpXml(byteSource, os);
}
public void removeXmpXml(byte[] src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceArray(src);
removeXmpXml(byteSource, os);
}
public void removeXmpXml(InputStream src, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceInputStream(src, null);
removeXmpXml(byteSource, os);
}
public void removeXmpXml(ByteSource byteSource, OutputStream os) throws ImageReadException, IOException, ImageWriteException {
JpegRewriter.JFIFPieces jfifPieces = analyzeJFIF(byteSource);
List pieces = jfifPieces.pieces;
pieces = removeXmpSegments(pieces);
writeSegments(os, pieces);
}
public void updateXmpXml(byte[] src, OutputStream os, String xmpXml) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceArray(src);
updateXmpXml(byteSource, os, xmpXml);
}
public void updateXmpXml(InputStream src, OutputStream os, String xmpXml) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceInputStream(src, null);
updateXmpXml(byteSource, os, xmpXml);
}
public void updateXmpXml(File src, OutputStream os, String xmpXml) throws ImageReadException, IOException, ImageWriteException {
ByteSource byteSource = new ByteSourceFile(src);
updateXmpXml(byteSource, os, xmpXml);
}
public void updateXmpXml(ByteSource byteSource, OutputStream os, String xmpXml) throws ImageReadException, IOException, ImageWriteException {
JpegRewriter.JFIFPieces jfifPieces = analyzeJFIF(byteSource);
List pieces = jfifPieces.pieces;
pieces = removeXmpSegments(pieces);
List newPieces = new ArrayList();
byte[] xmpXmlBytes = xmpXml.getBytes("utf-8");
int index = 0;
while (index < xmpXmlBytes.length) {
int segmentSize = Math.min(xmpXmlBytes.length, 65535);
byte[] segmentData = writeXmpSegment(xmpXmlBytes, index, segmentSize);
newPieces.add(new JpegRewriter.JFIFPieceSegment(65505, segmentData));
index += segmentSize;
}
pieces = insertAfterLastAppSegments(pieces, newPieces);
writeSegments(os, pieces);
}
private byte[] writeXmpSegment(byte[] xmpXmlData, int start, int length) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(XMP_IDENTIFIER);
os.write(xmpXmlData, start, length);
return os.toByteArray();
}
}

View file

@ -0,0 +1,47 @@
package org.apache.sanselan.formats.png;
import java.io.IOException;
import org.apache.sanselan.ImageReadException;
public class BitParser {
private final byte[] bytes;
private final int bitsPerPixel;
private final int bitDepth;
public BitParser(byte[] bytes, int bitsPerPixel, int bitDepth) {
this.bytes = bytes;
this.bitsPerPixel = bitsPerPixel;
this.bitDepth = bitDepth;
}
public int getSample(int pixelIndexInScanline, int sampleIndex) throws ImageReadException, IOException {
int pixelIndexBits = this.bitsPerPixel * pixelIndexInScanline;
int sampleIndexBits = pixelIndexBits + sampleIndex * this.bitDepth;
int sampleIndexBytes = sampleIndexBits >> 3;
if (this.bitDepth == 8)
return 0xFF & this.bytes[sampleIndexBytes];
if (this.bitDepth < 8) {
int b = 0xFF & this.bytes[sampleIndexBytes];
int bitsToShift = 8 - ((pixelIndexBits & 0x7) + this.bitDepth);
b >>= bitsToShift;
int bitmask = (1 << this.bitDepth) - 1;
return b & bitmask;
}
if (this.bitDepth == 16)
return (0xFF & this.bytes[sampleIndexBytes]) << 8 | 0xFF & this.bytes[sampleIndexBytes + 1];
throw new ImageReadException("PNG: bad BitDepth: " + this.bitDepth);
}
public int getSampleAsByte(int pixelIndexInScanline, int sampleIndex) throws ImageReadException, IOException {
int sample = getSample(pixelIndexInScanline, sampleIndex);
int rot = 8 - this.bitDepth;
if (rot > 0) {
sample <<= rot;
} else if (rot < 0) {
sample >>= -rot;
}
return 0xFF & sample;
}
}

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