Added missing Acme classes
This commit is contained in:
parent
1b98b7379f
commit
541dc1e58d
29 changed files with 7281 additions and 0 deletions
613
src/Acme/Fmt.java
Normal file
613
src/Acme/Fmt.java
Normal file
|
@ -0,0 +1,613 @@
|
||||||
|
// Fmt - some simple single-arg sprintf-like routines
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme;
|
||||||
|
|
||||||
|
/// Some simple single-arg sprintf-like routines.
|
||||||
|
// <P>
|
||||||
|
// It is apparently impossible to declare a Java method that accepts
|
||||||
|
// variable numbers of any type of argument. You can declare it to take
|
||||||
|
// Objects, but numeric variables and constants are not in fact Objects.
|
||||||
|
// <P>
|
||||||
|
// However, using the built-in string concatenation, it's almost as
|
||||||
|
// convenient to make a series of single-argument formatting routines.
|
||||||
|
// <P>
|
||||||
|
// Fmt can format the following types:
|
||||||
|
// <BLOCKQUOTE><CODE>
|
||||||
|
// byte short int long float double char String Object
|
||||||
|
// </CODE></BLOCKQUOTE>
|
||||||
|
// For each type there is a set of overloaded methods, each returning
|
||||||
|
// a formatted String. There's the plain formatting version:
|
||||||
|
// <BLOCKQUOTE><PRE>
|
||||||
|
// Fmt.fmt( x )
|
||||||
|
// </PRE></BLOCKQUOTE>
|
||||||
|
// There's a version specifying a minimum field width:
|
||||||
|
// <BLOCKQUOTE><PRE>
|
||||||
|
// Fmt.fmt( x, minWidth )
|
||||||
|
// </PRE></BLOCKQUOTE>
|
||||||
|
// And there's a version that takes flags:
|
||||||
|
// <BLOCKQUOTE><PRE>
|
||||||
|
// Fmt.fmt( x, minWidth, flags )
|
||||||
|
// </PRE></BLOCKQUOTE>
|
||||||
|
// Currently available flags are:
|
||||||
|
// <BLOCKQUOTE><PRE>
|
||||||
|
// Fmt.ZF - zero-fill
|
||||||
|
// Fmt.LJ - left justify
|
||||||
|
// Fmt.HX - hexadecimal
|
||||||
|
// Fmt.OC - octal
|
||||||
|
// </PRE></BLOCKQUOTE>
|
||||||
|
// The HX and OC flags imply unsigned output.
|
||||||
|
// <P>
|
||||||
|
// For doubles and floats, there's a significant-figures parameter before
|
||||||
|
// the flags:
|
||||||
|
// <BLOCKQUOTE><PRE>
|
||||||
|
// Fmt.fmt( d )
|
||||||
|
// Fmt.fmt( d, minWidth )
|
||||||
|
// Fmt.fmt( d, minWidth, sigFigs )
|
||||||
|
// Fmt.fmt( d, minWidth, sigFigs, flags )
|
||||||
|
// </PRE></BLOCKQUOTE>
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/Fmt.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <HR>
|
||||||
|
// Similar classes:
|
||||||
|
// <UL>
|
||||||
|
// <LI> Andrew Scherpbier's <A HREF="http://www.sdsu.edu/doc/java-SDSU/sdsu.FormatString.html">FormatString</A>
|
||||||
|
// Tries to allow variable numbers of arguments by
|
||||||
|
// supplying overloaded routines with different combinations of parameters,
|
||||||
|
// but doesn't actually supply that many. The floating point conversion
|
||||||
|
// is described as "very incomplete".
|
||||||
|
// <LI> Core Java's <A HREF="http://www.apl.jhu.edu/~hall/java/CoreJava-Format.html">Format</A>.
|
||||||
|
// The design seems a little weird. They want you to create an instance,
|
||||||
|
// passing the format string to the constructor, and then call an instance
|
||||||
|
// method with your data to do the actual formatting. The extra steps are
|
||||||
|
// pointless; better to just use static methods.
|
||||||
|
// </UL>
|
||||||
|
|
||||||
|
public class Fmt
|
||||||
|
{
|
||||||
|
|
||||||
|
// Flags.
|
||||||
|
/// Zero-fill.
|
||||||
|
public static final int ZF = 1;
|
||||||
|
/// Left justify.
|
||||||
|
public static final int LJ = 2;
|
||||||
|
/// Hexadecimal.
|
||||||
|
public static final int HX = 4;
|
||||||
|
/// Octal.
|
||||||
|
public static final int OC = 8;
|
||||||
|
// Was a number - internal use.
|
||||||
|
private static final int WN = 16;
|
||||||
|
|
||||||
|
// byte
|
||||||
|
public static String fmt( byte b )
|
||||||
|
{
|
||||||
|
return fmt( b, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( byte b, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( b, minWidth, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( byte b, int minWidth, int flags )
|
||||||
|
{
|
||||||
|
boolean hexadecimal = ( ( flags & HX ) != 0 );
|
||||||
|
boolean octal = ( ( flags & OC ) != 0 );
|
||||||
|
if ( hexadecimal )
|
||||||
|
return fmt( Integer.toString( b & 0xff, 16 ), minWidth, flags|WN );
|
||||||
|
else if ( octal )
|
||||||
|
return fmt( Integer.toString( b & 0xff, 8 ), minWidth, flags|WN );
|
||||||
|
else
|
||||||
|
return fmt( Integer.toString( b & 0xff ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
|
||||||
|
// short
|
||||||
|
public static String fmt( short s )
|
||||||
|
{
|
||||||
|
return fmt( s, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( short s, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( s, minWidth, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( short s, int minWidth, int flags )
|
||||||
|
{
|
||||||
|
boolean hexadecimal = ( ( flags & HX ) != 0 );
|
||||||
|
boolean octal = ( ( flags & OC ) != 0 );
|
||||||
|
if ( hexadecimal )
|
||||||
|
return fmt(
|
||||||
|
Integer.toString( s & 0xffff, 16 ), minWidth, flags|WN );
|
||||||
|
else if ( octal )
|
||||||
|
return fmt(
|
||||||
|
Integer.toString( s & 0xffff, 8 ), minWidth, flags|WN );
|
||||||
|
else
|
||||||
|
return fmt( Integer.toString( s ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
|
||||||
|
// int
|
||||||
|
public static String fmt( int i )
|
||||||
|
{
|
||||||
|
return fmt( i, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( int i, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( i, minWidth, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( int i, int minWidth, int flags )
|
||||||
|
{
|
||||||
|
boolean hexadecimal = ( ( flags & HX ) != 0 );
|
||||||
|
boolean octal = ( ( flags & OC ) != 0 );
|
||||||
|
if ( hexadecimal )
|
||||||
|
return fmt(
|
||||||
|
Long.toString( i & 0xffffffffL, 16 ), minWidth, flags|WN );
|
||||||
|
else if ( octal )
|
||||||
|
return fmt(
|
||||||
|
Long.toString( i & 0xffffffffL, 8 ), minWidth, flags|WN );
|
||||||
|
else
|
||||||
|
return fmt( Integer.toString( i ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
|
||||||
|
// long
|
||||||
|
public static String fmt( long l )
|
||||||
|
{
|
||||||
|
return fmt( l, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( long l, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( l, minWidth, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( long l, int minWidth, int flags )
|
||||||
|
{
|
||||||
|
boolean hexadecimal = ( ( flags & HX ) != 0 );
|
||||||
|
boolean octal = ( ( flags & OC ) != 0 );
|
||||||
|
if ( hexadecimal )
|
||||||
|
{
|
||||||
|
if ( ( l & 0xf000000000000000L ) != 0 )
|
||||||
|
return fmt(
|
||||||
|
Long.toString( l >>> 60, 16 ) +
|
||||||
|
fmt( l & 0x0fffffffffffffffL, 15, HX|ZF ),
|
||||||
|
minWidth, flags|WN );
|
||||||
|
else
|
||||||
|
return fmt( Long.toString( l, 16 ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
else if ( octal )
|
||||||
|
{
|
||||||
|
if ( ( l & 0x8000000000000000L ) != 0 )
|
||||||
|
return fmt(
|
||||||
|
Long.toString( l >>> 63, 8 ) +
|
||||||
|
fmt( l & 0x7fffffffffffffffL, 21, OC|ZF ),
|
||||||
|
minWidth, flags|WN );
|
||||||
|
else
|
||||||
|
return fmt( Long.toString( l, 8 ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return fmt( Long.toString( l ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
|
||||||
|
// float
|
||||||
|
public static String fmt( float f )
|
||||||
|
{
|
||||||
|
return fmt( f, 0, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( float f, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( f, minWidth, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( float f, int minWidth, int sigFigs )
|
||||||
|
{
|
||||||
|
return fmt( f, minWidth, sigFigs, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( float f, int minWidth, int sigFigs, int flags )
|
||||||
|
{
|
||||||
|
if ( sigFigs != 0 )
|
||||||
|
return fmt(
|
||||||
|
sigFigFix( Float.toString( f ), sigFigs ), minWidth,
|
||||||
|
flags|WN );
|
||||||
|
else
|
||||||
|
return fmt( Float.toString( f ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
|
||||||
|
// double
|
||||||
|
public static String fmt( double d )
|
||||||
|
{
|
||||||
|
return fmt( d, 0, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( double d, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( d, minWidth, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( double d, int minWidth, int sigFigs )
|
||||||
|
{
|
||||||
|
return fmt( d, minWidth, sigFigs, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( double d, int minWidth, int sigFigs, int flags )
|
||||||
|
{
|
||||||
|
if ( sigFigs != 0 )
|
||||||
|
return fmt(
|
||||||
|
sigFigFix( doubleToString( d ), sigFigs ), minWidth,
|
||||||
|
flags|WN );
|
||||||
|
else
|
||||||
|
return fmt( doubleToString( d ), minWidth, flags|WN );
|
||||||
|
}
|
||||||
|
|
||||||
|
// char
|
||||||
|
public static String fmt( char c )
|
||||||
|
{
|
||||||
|
return fmt( c, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( char c, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( c, minWidth, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( char c, int minWidth, int flags )
|
||||||
|
{
|
||||||
|
// return fmt( Character.toString( c ), minWidth, flags );
|
||||||
|
// Character currently lacks a static toString method. Workaround
|
||||||
|
// is to make a temporary instance and use the instance toString.
|
||||||
|
return fmt( new Character( c ).toString(), minWidth, flags );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object
|
||||||
|
public static String fmt( Object o )
|
||||||
|
{
|
||||||
|
return fmt( o, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( Object o, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( o, minWidth, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( Object o, int minWidth, int flags )
|
||||||
|
{
|
||||||
|
return fmt( o.toString(), minWidth, flags );
|
||||||
|
}
|
||||||
|
|
||||||
|
// String
|
||||||
|
public static String fmt( String s )
|
||||||
|
{
|
||||||
|
return fmt( s, 0, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( String s, int minWidth )
|
||||||
|
{
|
||||||
|
return fmt( s, minWidth, 0 );
|
||||||
|
}
|
||||||
|
public static String fmt( String s, int minWidth, int flags )
|
||||||
|
{
|
||||||
|
int len = s.length();
|
||||||
|
boolean zeroFill = ( ( flags & ZF ) != 0 );
|
||||||
|
boolean leftJustify = ( ( flags & LJ ) != 0 );
|
||||||
|
boolean hexadecimal = ( ( flags & HX ) != 0 );
|
||||||
|
boolean octal = ( ( flags & OC ) != 0 );
|
||||||
|
boolean wasNumber = ( ( flags & WN ) != 0 );
|
||||||
|
if ( ( hexadecimal || octal || zeroFill ) && ! wasNumber )
|
||||||
|
throw new InternalError( "Acme.Fmt: number flag on a non-number" );
|
||||||
|
if ( zeroFill && leftJustify )
|
||||||
|
throw new InternalError( "Acme.Fmt: zero-fill left-justify is silly" );
|
||||||
|
if ( hexadecimal && octal )
|
||||||
|
throw new InternalError( "Acme.Fmt: can't do both hex and octal" );
|
||||||
|
if ( len >= minWidth )
|
||||||
|
return s;
|
||||||
|
int fillWidth = minWidth - len;
|
||||||
|
StringBuffer fill = new StringBuffer( fillWidth );
|
||||||
|
for ( int i = 0; i < fillWidth; ++i )
|
||||||
|
if ( zeroFill )
|
||||||
|
fill.append( '0' );
|
||||||
|
else
|
||||||
|
fill.append( ' ' );
|
||||||
|
if ( leftJustify )
|
||||||
|
return s + fill;
|
||||||
|
else if ( zeroFill && s.startsWith( "-" ) )
|
||||||
|
return "-" + fill + s.substring( 1 );
|
||||||
|
else
|
||||||
|
return fill + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Internal routines.
|
||||||
|
|
||||||
|
private static String sigFigFix( String s, int sigFigs )
|
||||||
|
{
|
||||||
|
// First dissect the floating-point number string into sign,
|
||||||
|
// integer part, fraction part, and exponent.
|
||||||
|
String sign;
|
||||||
|
String unsigned;
|
||||||
|
if ( s.startsWith( "-" ) || s.startsWith( "+" ) )
|
||||||
|
{
|
||||||
|
sign = s.substring( 0, 1 );
|
||||||
|
unsigned = s.substring( 1 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sign = "";
|
||||||
|
unsigned = s;
|
||||||
|
}
|
||||||
|
String mantissa;
|
||||||
|
String exponent;
|
||||||
|
int eInd = unsigned.indexOf( 'e' );
|
||||||
|
if ( eInd == -1 ) // it may be 'e' or 'E'
|
||||||
|
eInd = unsigned.indexOf( 'E' );
|
||||||
|
if ( eInd == -1 )
|
||||||
|
{
|
||||||
|
mantissa = unsigned;
|
||||||
|
exponent = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mantissa = unsigned.substring( 0, eInd );
|
||||||
|
exponent = unsigned.substring( eInd );
|
||||||
|
}
|
||||||
|
StringBuffer number, fraction;
|
||||||
|
int dotInd = mantissa.indexOf( '.' );
|
||||||
|
if ( dotInd == -1 )
|
||||||
|
{
|
||||||
|
number = new StringBuffer( mantissa );
|
||||||
|
fraction = new StringBuffer( "" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
number = new StringBuffer( mantissa.substring( 0, dotInd ) );
|
||||||
|
fraction = new StringBuffer( mantissa.substring( dotInd + 1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
int numFigs = number.length();
|
||||||
|
int fracFigs = fraction.length();
|
||||||
|
if ( ( numFigs == 0 || number.equals( "0" ) ) && fracFigs > 0 )
|
||||||
|
{
|
||||||
|
// Don't count leading zeros in the fraction.
|
||||||
|
numFigs = 0;
|
||||||
|
for ( int i = 0; i < fraction.length(); ++i )
|
||||||
|
{
|
||||||
|
if ( fraction.charAt( i ) != '0' )
|
||||||
|
break;
|
||||||
|
--fracFigs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int mantFigs = numFigs + fracFigs;
|
||||||
|
if ( sigFigs > mantFigs )
|
||||||
|
{
|
||||||
|
// We want more figures; just append zeros to the fraction.
|
||||||
|
for ( int i = mantFigs; i < sigFigs; ++i )
|
||||||
|
fraction.append( '0' );
|
||||||
|
}
|
||||||
|
else if ( sigFigs < mantFigs && sigFigs >= numFigs )
|
||||||
|
{
|
||||||
|
// Want fewer figures in the fraction; chop.
|
||||||
|
fraction.setLength(
|
||||||
|
fraction.length() - ( fracFigs - ( sigFigs - numFigs ) ) );
|
||||||
|
// Round?
|
||||||
|
}
|
||||||
|
else if ( sigFigs < numFigs )
|
||||||
|
{
|
||||||
|
// Want fewer figures in the number; turn them to zeros.
|
||||||
|
fraction.setLength( 0 ); // should already be zero, but make sure
|
||||||
|
for ( int i = sigFigs; i < numFigs; ++i )
|
||||||
|
number.setCharAt( i, '0' );
|
||||||
|
// Round?
|
||||||
|
}
|
||||||
|
// Else sigFigs == mantFigs, which is fine.
|
||||||
|
|
||||||
|
if ( fraction.length() == 0 )
|
||||||
|
return sign + number + exponent;
|
||||||
|
else
|
||||||
|
return sign + number + "." + fraction + exponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Improved version of Double.toString(), returns more decimal places.
|
||||||
|
// <P>
|
||||||
|
// The JDK 1.0.2 version of Double.toString() returns only six decimal
|
||||||
|
// places on some systems. In JDK 1.1 full precision is returned on
|
||||||
|
// all platforms.
|
||||||
|
// @deprecated
|
||||||
|
// @see java.lang.Double#toString
|
||||||
|
public static String doubleToString( double d )
|
||||||
|
{
|
||||||
|
// Handle special numbers first, to avoid complications.
|
||||||
|
if ( Double.isNaN( d ) )
|
||||||
|
return "NaN";
|
||||||
|
if ( d == Double.NEGATIVE_INFINITY )
|
||||||
|
return "-Inf";
|
||||||
|
if ( d == Double.POSITIVE_INFINITY )
|
||||||
|
return "Inf";
|
||||||
|
|
||||||
|
// Grab the sign, and then make the number positive for simplicity.
|
||||||
|
boolean negative = false;
|
||||||
|
if ( d < 0.0D )
|
||||||
|
{
|
||||||
|
negative = true;
|
||||||
|
d = -d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the native version of the unsigned value, as a template.
|
||||||
|
String unsStr = Double.toString( d );
|
||||||
|
|
||||||
|
// Dissect out the exponent.
|
||||||
|
String mantStr, expStr;
|
||||||
|
int exp;
|
||||||
|
int eInd = unsStr.indexOf( 'e' );
|
||||||
|
if ( eInd == -1 ) // it may be 'e' or 'E'
|
||||||
|
eInd = unsStr.indexOf( 'E' );
|
||||||
|
if ( eInd == -1 )
|
||||||
|
{
|
||||||
|
mantStr = unsStr;
|
||||||
|
expStr = "";
|
||||||
|
exp = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mantStr = unsStr.substring( 0, eInd );
|
||||||
|
expStr = unsStr.substring( eInd + 1 );
|
||||||
|
if ( expStr.startsWith( "+" ) )
|
||||||
|
exp = Integer.parseInt( expStr.substring( 1 ) );
|
||||||
|
else
|
||||||
|
exp = Integer.parseInt( expStr );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dissect out the number part.
|
||||||
|
String numStr;
|
||||||
|
int dotInd = mantStr.indexOf( '.' );
|
||||||
|
if ( dotInd == -1 )
|
||||||
|
numStr = mantStr;
|
||||||
|
else
|
||||||
|
numStr = mantStr.substring( 0, dotInd );
|
||||||
|
long num;
|
||||||
|
if ( numStr.length() == 0 )
|
||||||
|
num = 0;
|
||||||
|
else
|
||||||
|
num = Integer.parseInt( numStr );
|
||||||
|
|
||||||
|
// Build the new mantissa.
|
||||||
|
StringBuffer newMantBuf = new StringBuffer( numStr + "." );
|
||||||
|
double p = Math.pow( 10, exp );
|
||||||
|
double frac = d - num * p;
|
||||||
|
String digits = "0123456789";
|
||||||
|
int nDigits = 16 - numStr.length(); // about 16 digits in a double
|
||||||
|
for ( int i = 0; i < nDigits; ++i )
|
||||||
|
{
|
||||||
|
p /= 10.0D;
|
||||||
|
int dig = (int) ( frac / p );
|
||||||
|
if ( dig < 0 ) dig = 0;
|
||||||
|
if ( dig > 9 ) dig = 9;
|
||||||
|
newMantBuf.append( digits.charAt( dig ) );
|
||||||
|
frac -= dig * p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (int) ( frac / p + 0.5D ) == 1 )
|
||||||
|
{
|
||||||
|
// Round up.
|
||||||
|
boolean roundMore = true;
|
||||||
|
for ( int i = newMantBuf.length() - 1; i >= 0; --i )
|
||||||
|
{
|
||||||
|
int dig = digits.indexOf( newMantBuf.charAt( i ) );
|
||||||
|
if ( dig == -1 )
|
||||||
|
continue;
|
||||||
|
++dig;
|
||||||
|
if ( dig == 10 )
|
||||||
|
{
|
||||||
|
newMantBuf.setCharAt( i, '0' );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newMantBuf.setCharAt( i, digits.charAt( dig ) );
|
||||||
|
roundMore = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( roundMore )
|
||||||
|
{
|
||||||
|
// If this happens, we need to prepend a 1. But I haven't
|
||||||
|
// found a test case yet, so I'm leaving it out for now.
|
||||||
|
// But if you get this message, please let me know!
|
||||||
|
newMantBuf.append( "ROUNDMORE" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chop any trailing zeros.
|
||||||
|
int len = newMantBuf.length();
|
||||||
|
while ( newMantBuf.charAt( len - 1 ) == '0' )
|
||||||
|
newMantBuf.setLength( --len );
|
||||||
|
// And chop a trailing dot, if any.
|
||||||
|
if ( newMantBuf.charAt( len - 1 ) == '.' )
|
||||||
|
newMantBuf.setLength( --len );
|
||||||
|
|
||||||
|
// Done.
|
||||||
|
return ( negative ? "-" : "" ) +
|
||||||
|
newMantBuf +
|
||||||
|
( expStr.length() != 0 ? ( "e" + expStr ) : "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
/// Test program.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
System.out.println( "Starting tests." );
|
||||||
|
show( Fmt.fmt( "Hello there." ) );
|
||||||
|
show( Fmt.fmt( 123 ) );
|
||||||
|
show( Fmt.fmt( 123, 10 ) );
|
||||||
|
show( Fmt.fmt( 123, 10, Fmt.ZF ) );
|
||||||
|
show( Fmt.fmt( 123, 10, Fmt.LJ ) );
|
||||||
|
show( Fmt.fmt( -123 ) );
|
||||||
|
show( Fmt.fmt( -123, 10 ) );
|
||||||
|
show( Fmt.fmt( -123, 10, Fmt.ZF ) );
|
||||||
|
show( Fmt.fmt( -123, 10, Fmt.LJ ) );
|
||||||
|
show( Fmt.fmt( (byte) 0xbe, 22, Fmt.OC ) );
|
||||||
|
show( Fmt.fmt( (short) 0xbabe, 22, Fmt.OC ) );
|
||||||
|
show( Fmt.fmt( 0xcafebabe, 22, Fmt.OC ) );
|
||||||
|
show( Fmt.fmt( 0xdeadbeefcafebabeL, 22, Fmt.OC ) );
|
||||||
|
show( Fmt.fmt( 0x8000000000000000L, 22, Fmt.OC ) );
|
||||||
|
show( Fmt.fmt( (byte) 0xbe, 16, Fmt.HX ) );
|
||||||
|
show( Fmt.fmt( (short) 0xbabe, 16, Fmt.HX ) );
|
||||||
|
show( Fmt.fmt( 0xcafebabe, 16, Fmt.HX ) );
|
||||||
|
show( Fmt.fmt( 0xdeadbeefcafebabeL, 16, Fmt.HX ) );
|
||||||
|
show( Fmt.fmt( 0x8000000000000000L, 16, Fmt.HX ) );
|
||||||
|
show( Fmt.fmt( 'c' ) );
|
||||||
|
show( Fmt.fmt( new java.util.Date() ) );
|
||||||
|
show( Fmt.fmt( 123.456F ) );
|
||||||
|
show( Fmt.fmt( 123456000000000000.0F ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 8 ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 7 ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 6 ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 5 ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 4 ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 3 ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 2 ) );
|
||||||
|
show( Fmt.fmt( 123.456F, 0, 1 ) );
|
||||||
|
show( Fmt.fmt( 123456000000000000.0F, 0, 4 ) );
|
||||||
|
show( Fmt.fmt( -123.456F, 0, 4 ) );
|
||||||
|
show( Fmt.fmt( -123456000000000000.0F, 0, 4 ) );
|
||||||
|
show( Fmt.fmt( 123.0F ) );
|
||||||
|
show( Fmt.fmt( 123.0D ) );
|
||||||
|
show( Fmt.fmt( 1.234567890123456789F ) );
|
||||||
|
show( Fmt.fmt( 1.234567890123456789D ) );
|
||||||
|
show( Fmt.fmt( 1234567890123456789F ) );
|
||||||
|
show( Fmt.fmt( 1234567890123456789D ) );
|
||||||
|
show( Fmt.fmt( 0.000000000000000000001234567890123456789F ) );
|
||||||
|
show( Fmt.fmt( 0.000000000000000000001234567890123456789D ) );
|
||||||
|
show( Fmt.fmt( 12300.0F ) );
|
||||||
|
show( Fmt.fmt( 12300.0D ) );
|
||||||
|
show( Fmt.fmt( 123000.0F ) );
|
||||||
|
show( Fmt.fmt( 123000.0D ) );
|
||||||
|
show( Fmt.fmt( 1230000.0F ) );
|
||||||
|
show( Fmt.fmt( 1230000.0D ) );
|
||||||
|
show( Fmt.fmt( 12300000.0F ) );
|
||||||
|
show( Fmt.fmt( 12300000.0D ) );
|
||||||
|
show( Fmt.fmt( Float.NaN ) );
|
||||||
|
show( Fmt.fmt( Float.POSITIVE_INFINITY ) );
|
||||||
|
show( Fmt.fmt( Float.NEGATIVE_INFINITY ) );
|
||||||
|
show( Fmt.fmt( Double.NaN ) );
|
||||||
|
show( Fmt.fmt( Double.POSITIVE_INFINITY ) );
|
||||||
|
show( Fmt.fmt( Double.NEGATIVE_INFINITY ) );
|
||||||
|
show( Fmt.fmt( 1.0F / 8.0F ) );
|
||||||
|
show( Fmt.fmt( 1.0D / 8.0D ) );
|
||||||
|
System.out.println( "Done with tests." );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void show( String str )
|
||||||
|
{
|
||||||
|
System.out.println( "#" + str + "#" );
|
||||||
|
}
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
}
|
314
src/Acme/JPM/Decoders/ImageDecoder.java
Normal file
314
src/Acme/JPM/Decoders/ImageDecoder.java
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
// ImageDecoder - abstract class for reading in an image
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Decoders;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Abstract class for reading in an image.
|
||||||
|
// <P>
|
||||||
|
// A framework for classes that read in and decode an image in
|
||||||
|
// a particular file format.
|
||||||
|
// <P>
|
||||||
|
// This provides a very simplified rendition of the ImageProducer interface.
|
||||||
|
// It requires the decoder to read the image a row at a time. It requires
|
||||||
|
// use of the RGBdefault color model.
|
||||||
|
// If you want more flexibility you can always implement ImageProducer
|
||||||
|
// directly.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Decoders/ImageDecoder.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see PpmDecoder
|
||||||
|
// @see Acme.JPM.Encoders.ImageEncoder
|
||||||
|
|
||||||
|
public abstract class ImageDecoder implements ImageProducer
|
||||||
|
{
|
||||||
|
|
||||||
|
private InputStream in;
|
||||||
|
private int width, height;
|
||||||
|
private boolean[] rowsRead;
|
||||||
|
private int[][] rgbPixels;
|
||||||
|
private boolean startedRead = false;
|
||||||
|
private boolean gotSize = false;
|
||||||
|
private boolean err = false;
|
||||||
|
private boolean producing = false;
|
||||||
|
private Vector consumers = new Vector();
|
||||||
|
private static final ColorModel model = ColorModel.getRGBdefault();
|
||||||
|
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param in The stream to read the bytes from.
|
||||||
|
public ImageDecoder( InputStream in )
|
||||||
|
{
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Methods that subclasses implement.
|
||||||
|
|
||||||
|
/// Subclasses implement this to read in enough of the image stream
|
||||||
|
// to figure out the width and height.
|
||||||
|
abstract void readHeader( InputStream in ) throws IOException;
|
||||||
|
|
||||||
|
/// Subclasses implement this to return the width, or -1 if not known.
|
||||||
|
abstract int getWidth();
|
||||||
|
|
||||||
|
/// Subclasses implement this to return the height, or -1 if not known.
|
||||||
|
abstract int getHeight();
|
||||||
|
|
||||||
|
/// Subclasses implement this to read pixel data into the rgbRow
|
||||||
|
// array, an int[width]. One int per pixel, no offsets or padding,
|
||||||
|
// RGBdefault (AARRGGBB) color model.
|
||||||
|
abstract void readRow( InputStream in, int row, int[] rgbRow ) throws IOException;
|
||||||
|
|
||||||
|
|
||||||
|
// Our own methods.
|
||||||
|
|
||||||
|
void readImage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
readHeader( in );
|
||||||
|
width = getWidth();
|
||||||
|
height = getHeight();
|
||||||
|
if ( width == -1 || height == -1 )
|
||||||
|
err = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rowsRead = new boolean[height];
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
rowsRead[row] = false;
|
||||||
|
gotSize = true;
|
||||||
|
notifyThem();
|
||||||
|
rgbPixels = new int[height][width];
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
{
|
||||||
|
readRow( in, row, rgbPixels[row] );
|
||||||
|
rowsRead[row] = true;
|
||||||
|
notifyThem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
err = true;
|
||||||
|
width = -1;
|
||||||
|
height = -1;
|
||||||
|
rowsRead = null;
|
||||||
|
rgbPixels = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void notifyThem()
|
||||||
|
{
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendImage()
|
||||||
|
{
|
||||||
|
// Grab the list of consumers, in case it changes while we're sending.
|
||||||
|
ImageConsumer[] c = new ImageConsumer[consumers.size()];
|
||||||
|
int i;
|
||||||
|
for ( i = 0; i < c.length; ++i )
|
||||||
|
c[i] = (ImageConsumer) consumers.elementAt( i );
|
||||||
|
// Try to be as parallel as possible.
|
||||||
|
waitForSize();
|
||||||
|
for ( i = 0; i < c.length; ++i )
|
||||||
|
sendHead( c[i] );
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
for ( i = 0; i < c.length; ++i )
|
||||||
|
sendPixelRow( c[i], row );
|
||||||
|
for ( i = 0; i < c.length; ++i )
|
||||||
|
sendTail( c[i] );
|
||||||
|
producing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void waitForSize()
|
||||||
|
{
|
||||||
|
while ( ( ! err ) && ( ! gotSize ))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
catch ( InterruptedException ignore ) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void waitForRow( int row )
|
||||||
|
{
|
||||||
|
while ( ( ! err ) && ( ! rowsRead[row] ) )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
catch ( InterruptedException ignore ) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendHead( ImageConsumer ic )
|
||||||
|
{
|
||||||
|
if ( err )
|
||||||
|
return;
|
||||||
|
ic.setDimensions( width, height );
|
||||||
|
ic.setColorModel( model );
|
||||||
|
ic.setHints(
|
||||||
|
ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
|
||||||
|
ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPixelRow( ImageConsumer ic, int row )
|
||||||
|
{
|
||||||
|
if ( err )
|
||||||
|
return;
|
||||||
|
waitForRow( row );
|
||||||
|
if ( err )
|
||||||
|
return;
|
||||||
|
ic.setPixels( 0, row, width, 1, model, rgbPixels[row], 0, width );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendTail( ImageConsumer ic )
|
||||||
|
{
|
||||||
|
if ( err )
|
||||||
|
ic.imageComplete( ImageConsumer.IMAGEERROR );
|
||||||
|
else
|
||||||
|
ic.imageComplete( ImageConsumer.STATICIMAGEDONE );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Methods from ImageProducer.
|
||||||
|
|
||||||
|
/// This method is used to register an ImageConsumer with the
|
||||||
|
// ImageProducer for access to the image data during a later
|
||||||
|
// reconstruction of the Image. The ImageProducer may, at its
|
||||||
|
// discretion, start delivering the image data to the consumer
|
||||||
|
// using the ImageConsumer interface immediately, or when the
|
||||||
|
// next available image reconstruction is triggered by a call
|
||||||
|
// to the startProduction method.
|
||||||
|
// @see #startProduction
|
||||||
|
public void addConsumer( ImageConsumer ic )
|
||||||
|
{
|
||||||
|
if ( ic != null && ! isConsumer( ic ) )
|
||||||
|
consumers.addElement( ic );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method determines if a given ImageConsumer object
|
||||||
|
// is currently registered with this ImageProducer as one
|
||||||
|
// of its consumers.
|
||||||
|
public boolean isConsumer( ImageConsumer ic )
|
||||||
|
{
|
||||||
|
return consumers.contains( ic );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method removes the given ImageConsumer object
|
||||||
|
// from the list of consumers currently registered to
|
||||||
|
// receive image data. It is not considered an error
|
||||||
|
// to remove a consumer that is not currently registered.
|
||||||
|
// The ImageProducer should stop sending data to this
|
||||||
|
// consumer as soon as is feasible.
|
||||||
|
public void removeConsumer( ImageConsumer ic )
|
||||||
|
{
|
||||||
|
consumers.removeElement( ic );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method both registers the given ImageConsumer object
|
||||||
|
// as a consumer and starts an immediate reconstruction of
|
||||||
|
// the image data which will then be delivered to this
|
||||||
|
// consumer and any other consumer which may have already
|
||||||
|
// been registered with the producer. This method differs
|
||||||
|
// from the addConsumer method in that a reproduction of
|
||||||
|
// the image data should be triggered as soon as possible.
|
||||||
|
// @see #addConsumer
|
||||||
|
public void startProduction( ImageConsumer ic )
|
||||||
|
{
|
||||||
|
addConsumer( ic );
|
||||||
|
if ( ! startedRead )
|
||||||
|
{
|
||||||
|
startedRead = true;
|
||||||
|
new ImageDecoderRead( this );
|
||||||
|
}
|
||||||
|
if ( ! producing )
|
||||||
|
{
|
||||||
|
producing = true;
|
||||||
|
sendImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method is used by an ImageConsumer to request that
|
||||||
|
// the ImageProducer attempt to resend the image data one
|
||||||
|
// more time in TOPDOWNLEFTRIGHT order so that higher
|
||||||
|
// quality conversion algorithms which depend on receiving
|
||||||
|
// pixels in order can be used to produce a better output
|
||||||
|
// version of the image. The ImageProducer is free to
|
||||||
|
// ignore this call if it cannot resend the data in that
|
||||||
|
// order. If the data can be resent, then the ImageProducer
|
||||||
|
// should respond by executing the following minimum set of
|
||||||
|
// ImageConsumer method calls:
|
||||||
|
// <PRE>
|
||||||
|
// ic.setHints( TOPDOWNLEFTRIGHT | [otherhints] );
|
||||||
|
// ic.setPixels( [...] ); // as many times as needed
|
||||||
|
// ic.imageComplete( [status] );
|
||||||
|
// </PRE>
|
||||||
|
// @see ImageConsumer#setHints
|
||||||
|
public void requestTopDownLeftRightResend( ImageConsumer ic )
|
||||||
|
{
|
||||||
|
addConsumer( ic );
|
||||||
|
waitForSize();
|
||||||
|
sendHead( ic );
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
sendPixelRow( ic, row );
|
||||||
|
sendTail( ic );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ImageDecoderRead extends Thread
|
||||||
|
{
|
||||||
|
|
||||||
|
private ImageDecoder parent;
|
||||||
|
|
||||||
|
public ImageDecoderRead( ImageDecoder parent )
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods from Runnable.
|
||||||
|
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
parent.readImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
267
src/Acme/JPM/Decoders/PpmDecoder.java
Normal file
267
src/Acme/JPM/Decoders/PpmDecoder.java
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
// PpmDecoder - read in a PPM image
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Decoders;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Read in a PPM image.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Decoders/PpmDecoder.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see Acme.JPM.Encoders.PpmEncoder
|
||||||
|
|
||||||
|
public class PpmDecoder extends ImageDecoder
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param in The stream to read the bytes from.
|
||||||
|
public PpmDecoder( InputStream in )
|
||||||
|
{
|
||||||
|
super( in );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int type;
|
||||||
|
private static final int PBM_ASCII = 1;
|
||||||
|
private static final int PGM_ASCII = 2;
|
||||||
|
private static final int PPM_ASCII = 3;
|
||||||
|
private static final int PBM_RAW = 4;
|
||||||
|
private static final int PGM_RAW = 5;
|
||||||
|
private static final int PPM_RAW = 6;
|
||||||
|
|
||||||
|
private int width = -1, height = -1;
|
||||||
|
private int maxval;
|
||||||
|
|
||||||
|
/// Subclasses implement this to read in enough of the image stream
|
||||||
|
// to figure out the width and height.
|
||||||
|
void readHeader( InputStream in ) throws IOException
|
||||||
|
{
|
||||||
|
char c1, c2;
|
||||||
|
|
||||||
|
c1 = (char) readByte( in );
|
||||||
|
c2 = (char) readByte( in );
|
||||||
|
|
||||||
|
if ( c1 != 'P' )
|
||||||
|
throw new IOException( "not a PBM/PGM/PPM file" );
|
||||||
|
switch ( c2 )
|
||||||
|
{
|
||||||
|
case '1':
|
||||||
|
type = PBM_ASCII;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
type = PGM_ASCII;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
type = PPM_ASCII;
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
type = PBM_RAW;
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
type = PGM_RAW;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
type = PPM_RAW;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IOException( "not a standard PBM/PGM/PPM file" );
|
||||||
|
}
|
||||||
|
width = readInt( in );
|
||||||
|
height = readInt( in );
|
||||||
|
if ( type != PBM_ASCII && type != PBM_RAW )
|
||||||
|
maxval = readInt( in );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subclasses implement this to return the width, or -1 if not known.
|
||||||
|
int getWidth()
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subclasses implement this to return the height, or -1 if not known.
|
||||||
|
int getHeight()
|
||||||
|
{
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subclasses implement this to read pixel data into the rgbRow
|
||||||
|
// array, an int[width]. One int per pixel, no offsets or padding,
|
||||||
|
// RGBdefault (AARRGGBB) color model
|
||||||
|
void readRow( InputStream in, int row, int[] rgbRow ) throws IOException
|
||||||
|
{
|
||||||
|
int col, r, g, b;
|
||||||
|
int rgb = 0;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
for ( col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
switch ( type )
|
||||||
|
{
|
||||||
|
case PBM_ASCII:
|
||||||
|
c = readChar( in );
|
||||||
|
if ( c == '1' )
|
||||||
|
rgb = 0xff000000;
|
||||||
|
else if ( c == '0' )
|
||||||
|
rgb = 0xffffffff;
|
||||||
|
else
|
||||||
|
throw new IOException( "illegal PBM bit" );
|
||||||
|
break;
|
||||||
|
case PGM_ASCII:
|
||||||
|
g = readInt( in );
|
||||||
|
rgb = makeRgb( g, g, g );
|
||||||
|
break;
|
||||||
|
case PPM_ASCII:
|
||||||
|
r = readInt( in );
|
||||||
|
g = readInt( in );
|
||||||
|
b = readInt( in );
|
||||||
|
rgb = makeRgb( r, g, b );
|
||||||
|
break;
|
||||||
|
case PBM_RAW:
|
||||||
|
if ( readBit( in ) )
|
||||||
|
rgb = 0xff000000;
|
||||||
|
else
|
||||||
|
rgb = 0xffffffff;
|
||||||
|
break;
|
||||||
|
case PGM_RAW:
|
||||||
|
g = readByte( in );
|
||||||
|
if ( maxval != 255 )
|
||||||
|
g = fixDepth( g );
|
||||||
|
rgb = makeRgb( g, g, g );
|
||||||
|
break;
|
||||||
|
case PPM_RAW:
|
||||||
|
r = readByte( in );
|
||||||
|
g = readByte( in );
|
||||||
|
b = readByte( in );
|
||||||
|
if ( maxval != 255 )
|
||||||
|
{
|
||||||
|
r = fixDepth( r );
|
||||||
|
g = fixDepth( g );
|
||||||
|
b = fixDepth( b );
|
||||||
|
}
|
||||||
|
rgb = makeRgb( r, g, b );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rgbRow[col] = rgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility routine to read a byte. Instead of returning -1 on
|
||||||
|
// EOF, it throws an exception.
|
||||||
|
private static int readByte( InputStream in ) throws IOException
|
||||||
|
{
|
||||||
|
int b = in.read();
|
||||||
|
if ( b == -1 )
|
||||||
|
throw new EOFException();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int bitshift = -1;
|
||||||
|
private int bits;
|
||||||
|
|
||||||
|
/// Utility routine to read a bit, packed eight to a byte, big-endian.
|
||||||
|
private boolean readBit( InputStream in ) throws IOException
|
||||||
|
{
|
||||||
|
if ( bitshift == -1 )
|
||||||
|
{
|
||||||
|
bits = readByte( in );
|
||||||
|
bitshift = 7;
|
||||||
|
}
|
||||||
|
boolean bit = ( ( ( bits >> bitshift ) & 1 ) != 0 );
|
||||||
|
--bitshift;
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility routine to read a character, ignoring comments.
|
||||||
|
private static char readChar( InputStream in ) throws IOException
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
|
||||||
|
c = (char) readByte( in );
|
||||||
|
if ( c == '#' )
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
c = (char) readByte( in );
|
||||||
|
}
|
||||||
|
while ( c != '\n' && c != '\r' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility routine to read the first non-whitespace character.
|
||||||
|
private static char readNonwhiteChar( InputStream in ) throws IOException
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
c = readChar( in );
|
||||||
|
}
|
||||||
|
while ( c == ' ' || c == '\t' || c == '\n' || c == '\r' );
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility routine to read an ASCII integer, ignoring comments.
|
||||||
|
private static int readInt( InputStream in ) throws IOException
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
c = readNonwhiteChar( in );
|
||||||
|
if ( c < '0' || c > '9' )
|
||||||
|
throw new IOException( "junk in file where integer should be" );
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
i = i * 10 + c - '0';
|
||||||
|
c = readChar( in );
|
||||||
|
}
|
||||||
|
while ( c >= '0' && c <= '9' );
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility routine to rescale a pixel value from a non-eight-bit maxval.
|
||||||
|
private int fixDepth( int p )
|
||||||
|
{
|
||||||
|
return ( p * 255 + maxval / 2 ) / maxval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility routine make an RGBdefault pixel from three color values.
|
||||||
|
private static int makeRgb( int r, int g, int b )
|
||||||
|
{
|
||||||
|
return 0xff000000 | ( r << 16 ) | ( g << 8 ) | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
691
src/Acme/JPM/Encoders/GifEncoder.java
Normal file
691
src/Acme/JPM/Encoders/GifEncoder.java
Normal file
|
@ -0,0 +1,691 @@
|
||||||
|
// GifEncoder - write out an image as a GIF
|
||||||
|
//
|
||||||
|
// Transparency handling and variable bit size courtesy of Jack Palevich.
|
||||||
|
//
|
||||||
|
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Encoders;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Write out an image as a GIF.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Encoders/GifEncoder.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see ToGif
|
||||||
|
|
||||||
|
public class GifEncoder extends ImageEncoder
|
||||||
|
{
|
||||||
|
|
||||||
|
private boolean interlace = false;
|
||||||
|
|
||||||
|
/// Constructor from Image.
|
||||||
|
// @param img The image to encode.
|
||||||
|
// @param out The stream to write the GIF to.
|
||||||
|
public GifEncoder( Image img, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
super( img, out );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor from Image with interlace setting.
|
||||||
|
// @param img The image to encode.
|
||||||
|
// @param out The stream to write the GIF to.
|
||||||
|
// @param interlace Whether to interlace.
|
||||||
|
public GifEncoder( Image img, OutputStream out, boolean interlace ) throws IOException
|
||||||
|
{
|
||||||
|
super( img, out );
|
||||||
|
this.interlace = interlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor from ImageProducer.
|
||||||
|
// @param prod The ImageProducer to encode.
|
||||||
|
// @param out The stream to write the GIF to.
|
||||||
|
public GifEncoder( ImageProducer prod, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
super( prod, out );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor from ImageProducer with interlace setting.
|
||||||
|
// @param prod The ImageProducer to encode.
|
||||||
|
// @param out The stream to write the GIF to.
|
||||||
|
public GifEncoder( ImageProducer prod, OutputStream out, boolean interlace ) throws IOException
|
||||||
|
{
|
||||||
|
super( prod, out );
|
||||||
|
this.interlace = interlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
int[][] rgbPixels;
|
||||||
|
|
||||||
|
void encodeStart( int width, int height ) throws IOException
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
rgbPixels = new int[height][width];
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodePixels(
|
||||||
|
int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// Save the pixels.
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
System.arraycopy(
|
||||||
|
rgbPixels, row * scansize + off,
|
||||||
|
this.rgbPixels[y + row], x, w );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Acme.IntHashtable colorHash;
|
||||||
|
|
||||||
|
void encodeDone() throws IOException
|
||||||
|
{
|
||||||
|
int transparentIndex = -1;
|
||||||
|
int transparentRgb = -1;
|
||||||
|
// Put all the pixels into a hash table.
|
||||||
|
colorHash = new Acme.IntHashtable();
|
||||||
|
int index = 0;
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
{
|
||||||
|
int rowOffset = row * width;
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
int rgb = rgbPixels[row][col];
|
||||||
|
boolean isTransparent = ( ( rgb >>> 24 ) < 0x80 );
|
||||||
|
if ( isTransparent )
|
||||||
|
{
|
||||||
|
if ( transparentIndex < 0 )
|
||||||
|
{
|
||||||
|
// First transparent color; remember it.
|
||||||
|
transparentIndex = index;
|
||||||
|
transparentRgb = rgb;
|
||||||
|
}
|
||||||
|
else if ( rgb != transparentRgb )
|
||||||
|
{
|
||||||
|
// A second transparent color; replace it with
|
||||||
|
// the first one.
|
||||||
|
rgbPixels[row][col] = rgb = transparentRgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GifEncoderHashitem item =
|
||||||
|
(GifEncoderHashitem) colorHash.get( rgb );
|
||||||
|
if ( item == null )
|
||||||
|
{
|
||||||
|
if ( index >= 256 )
|
||||||
|
throw new IOException( "too many colors for a GIF" );
|
||||||
|
item = new GifEncoderHashitem(
|
||||||
|
rgb, 1, index, isTransparent );
|
||||||
|
++index;
|
||||||
|
colorHash.put( rgb, item );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++item.count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out how many bits to use.
|
||||||
|
int logColors;
|
||||||
|
if ( index <= 2 )
|
||||||
|
logColors = 1;
|
||||||
|
else if ( index <= 4 )
|
||||||
|
logColors = 2;
|
||||||
|
else if ( index <= 16 )
|
||||||
|
logColors = 4;
|
||||||
|
else
|
||||||
|
logColors = 8;
|
||||||
|
|
||||||
|
// Turn colors into colormap entries.
|
||||||
|
int mapSize = 1 << logColors;
|
||||||
|
byte[] reds = new byte[mapSize];
|
||||||
|
byte[] grns = new byte[mapSize];
|
||||||
|
byte[] blus = new byte[mapSize];
|
||||||
|
for ( Enumeration e = colorHash.elements(); e.hasMoreElements(); )
|
||||||
|
{
|
||||||
|
GifEncoderHashitem item = (GifEncoderHashitem) e.nextElement();
|
||||||
|
reds[item.index] = (byte) ( ( item.rgb >> 16 ) & 0xff );
|
||||||
|
grns[item.index] = (byte) ( ( item.rgb >> 8 ) & 0xff );
|
||||||
|
blus[item.index] = (byte) ( item.rgb & 0xff );
|
||||||
|
}
|
||||||
|
|
||||||
|
GIFEncode(
|
||||||
|
out, width, height, interlace, (byte) 0, transparentIndex,
|
||||||
|
logColors, reds, grns, blus );
|
||||||
|
}
|
||||||
|
|
||||||
|
byte GetPixel( int x, int y ) throws IOException
|
||||||
|
{
|
||||||
|
GifEncoderHashitem item =
|
||||||
|
(GifEncoderHashitem) colorHash.get( rgbPixels[y][x] );
|
||||||
|
if ( item == null )
|
||||||
|
throw new IOException( "color not found" );
|
||||||
|
return (byte) item.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeString( OutputStream out, String str ) throws IOException
|
||||||
|
{
|
||||||
|
byte[] buf = str.getBytes();
|
||||||
|
out.write( buf );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from ppmtogif, which is based on GIFENCOD by David
|
||||||
|
// Rowley <mgardi@watdscu.waterloo.edu>. Lempel-Zim compression
|
||||||
|
// based on "compress".
|
||||||
|
|
||||||
|
int Width, Height;
|
||||||
|
boolean Interlace;
|
||||||
|
int curx, cury;
|
||||||
|
int CountDown;
|
||||||
|
int Pass = 0;
|
||||||
|
|
||||||
|
void GIFEncode(
|
||||||
|
OutputStream outs, int Width, int Height, boolean Interlace, byte Background, int Transparent, int BitsPerPixel, byte[] Red, byte[] Green, byte[] Blue )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
byte B;
|
||||||
|
int LeftOfs, TopOfs;
|
||||||
|
int ColorMapSize;
|
||||||
|
int InitCodeSize;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
this.Width = Width;
|
||||||
|
this.Height = Height;
|
||||||
|
this.Interlace = Interlace;
|
||||||
|
ColorMapSize = 1 << BitsPerPixel;
|
||||||
|
LeftOfs = TopOfs = 0;
|
||||||
|
|
||||||
|
// Calculate number of bits we are expecting
|
||||||
|
CountDown = Width * Height;
|
||||||
|
|
||||||
|
// Indicate which pass we are on (if interlace)
|
||||||
|
Pass = 0;
|
||||||
|
|
||||||
|
// The initial code size
|
||||||
|
if ( BitsPerPixel <= 1 )
|
||||||
|
InitCodeSize = 2;
|
||||||
|
else
|
||||||
|
InitCodeSize = BitsPerPixel;
|
||||||
|
|
||||||
|
// Set up the current x and y position
|
||||||
|
curx = 0;
|
||||||
|
cury = 0;
|
||||||
|
|
||||||
|
// Write the Magic header
|
||||||
|
writeString( outs, "GIF89a" );
|
||||||
|
|
||||||
|
// Write out the screen width and height
|
||||||
|
Putword( Width, outs );
|
||||||
|
Putword( Height, outs );
|
||||||
|
|
||||||
|
// Indicate that there is a global colour map
|
||||||
|
B = (byte) 0x80; // Yes, there is a color map
|
||||||
|
// OR in the resolution
|
||||||
|
B |= (byte) ( ( 8 - 1 ) << 4 );
|
||||||
|
// Not sorted
|
||||||
|
// OR in the Bits per Pixel
|
||||||
|
B |= (byte) ( ( BitsPerPixel - 1 ) );
|
||||||
|
|
||||||
|
// Write it out
|
||||||
|
Putbyte( B, outs );
|
||||||
|
|
||||||
|
// Write out the Background colour
|
||||||
|
Putbyte( Background, outs );
|
||||||
|
|
||||||
|
// Pixel aspect ratio - 1:1.
|
||||||
|
//Putbyte( (byte) 49, outs );
|
||||||
|
// Java's GIF reader currently has a bug, if the aspect ratio byte is
|
||||||
|
// not zero it throws an ImageFormatException. It doesn't know that
|
||||||
|
// 49 means a 1:1 aspect ratio. Well, whatever, zero works with all
|
||||||
|
// the other decoders I've tried so it probably doesn't hurt.
|
||||||
|
Putbyte( (byte) 0, outs );
|
||||||
|
|
||||||
|
// Write out the Global Colour Map
|
||||||
|
for ( i = 0; i < ColorMapSize; ++i )
|
||||||
|
{
|
||||||
|
Putbyte( Red[i], outs );
|
||||||
|
Putbyte( Green[i], outs );
|
||||||
|
Putbyte( Blue[i], outs );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out extension for transparent colour index, if necessary.
|
||||||
|
if ( Transparent != -1 )
|
||||||
|
{
|
||||||
|
Putbyte( (byte) '!', outs );
|
||||||
|
Putbyte( (byte) 0xf9, outs );
|
||||||
|
Putbyte( (byte) 4, outs );
|
||||||
|
Putbyte( (byte) 1, outs );
|
||||||
|
Putbyte( (byte) 0, outs );
|
||||||
|
Putbyte( (byte) 0, outs );
|
||||||
|
Putbyte( (byte) Transparent, outs );
|
||||||
|
Putbyte( (byte) 0, outs );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an Image separator
|
||||||
|
Putbyte( (byte) ',', outs );
|
||||||
|
|
||||||
|
// Write the Image header
|
||||||
|
Putword( LeftOfs, outs );
|
||||||
|
Putword( TopOfs, outs );
|
||||||
|
Putword( Width, outs );
|
||||||
|
Putword( Height, outs );
|
||||||
|
|
||||||
|
// Write out whether or not the image is interlaced
|
||||||
|
if ( Interlace )
|
||||||
|
Putbyte( (byte) 0x40, outs );
|
||||||
|
else
|
||||||
|
Putbyte( (byte) 0x00, outs );
|
||||||
|
|
||||||
|
// Write out the initial code size
|
||||||
|
Putbyte( (byte) InitCodeSize, outs );
|
||||||
|
|
||||||
|
// Go and actually compress the data
|
||||||
|
compress( InitCodeSize+1, outs );
|
||||||
|
|
||||||
|
// Write out a Zero-length packet (to end the series)
|
||||||
|
Putbyte( (byte) 0, outs );
|
||||||
|
|
||||||
|
// Write the GIF file terminator
|
||||||
|
Putbyte( (byte) ';', outs );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bump the 'curx' and 'cury' to point to the next pixel
|
||||||
|
void BumpPixel()
|
||||||
|
{
|
||||||
|
// Bump the current X position
|
||||||
|
++curx;
|
||||||
|
|
||||||
|
// If we are at the end of a scan line, set curx back to the beginning
|
||||||
|
// If we are interlaced, bump the cury to the appropriate spot,
|
||||||
|
// otherwise, just increment it.
|
||||||
|
if ( curx == Width )
|
||||||
|
{
|
||||||
|
curx = 0;
|
||||||
|
|
||||||
|
if ( ! Interlace )
|
||||||
|
++cury;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch( Pass )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
cury += 8;
|
||||||
|
if ( cury >= Height )
|
||||||
|
{
|
||||||
|
++Pass;
|
||||||
|
cury = 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
cury += 8;
|
||||||
|
if ( cury >= Height )
|
||||||
|
{
|
||||||
|
++Pass;
|
||||||
|
cury = 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
cury += 4;
|
||||||
|
if ( cury >= Height )
|
||||||
|
{
|
||||||
|
++Pass;
|
||||||
|
cury = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
cury += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int EOF = -1;
|
||||||
|
|
||||||
|
// Return the next pixel from the image
|
||||||
|
int GIFNextPixel() throws IOException
|
||||||
|
{
|
||||||
|
byte r;
|
||||||
|
|
||||||
|
if ( CountDown == 0 )
|
||||||
|
return EOF;
|
||||||
|
|
||||||
|
--CountDown;
|
||||||
|
|
||||||
|
r = GetPixel( curx, cury );
|
||||||
|
|
||||||
|
BumpPixel();
|
||||||
|
|
||||||
|
return r & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out a word to the GIF file
|
||||||
|
void Putword( int w, OutputStream outs ) throws IOException
|
||||||
|
{
|
||||||
|
Putbyte( (byte) ( w & 0xff ), outs );
|
||||||
|
Putbyte( (byte) ( ( w >> 8 ) & 0xff ), outs );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out a byte to the GIF file
|
||||||
|
void Putbyte( byte b, OutputStream outs ) throws IOException
|
||||||
|
{
|
||||||
|
outs.write( b );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// GIFCOMPR.C - GIF Image compression routines
|
||||||
|
//
|
||||||
|
// Lempel-Ziv compression based on 'compress'. GIF modifications by
|
||||||
|
// David Rowley (mgardi@watdcsu.waterloo.edu)
|
||||||
|
|
||||||
|
// General DEFINEs
|
||||||
|
|
||||||
|
static final int BITS = 12;
|
||||||
|
|
||||||
|
static final int HSIZE = 5003; // 80% occupancy
|
||||||
|
|
||||||
|
// GIF Image compression - modified 'compress'
|
||||||
|
//
|
||||||
|
// Based on: compress.c - File compression ala IEEE Computer, June 1984.
|
||||||
|
//
|
||||||
|
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
|
||||||
|
// Jim McKie (decvax!mcvax!jim)
|
||||||
|
// Steve Davies (decvax!vax135!petsd!peora!srd)
|
||||||
|
// Ken Turkowski (decvax!decwrl!turtlevax!ken)
|
||||||
|
// James A. Woods (decvax!ihnp4!ames!jaw)
|
||||||
|
// Joe Orost (decvax!vax135!petsd!joe)
|
||||||
|
|
||||||
|
int n_bits; // number of bits/code
|
||||||
|
int maxbits = BITS; // user settable max # bits/code
|
||||||
|
int maxcode; // maximum code, given n_bits
|
||||||
|
int maxmaxcode = 1 << BITS; // should NEVER generate this code
|
||||||
|
|
||||||
|
final int MAXCODE( int n_bits )
|
||||||
|
{
|
||||||
|
return ( 1 << n_bits ) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] htab = new int[HSIZE];
|
||||||
|
int[] codetab = new int[HSIZE];
|
||||||
|
|
||||||
|
int hsize = HSIZE; // for dynamic table sizing
|
||||||
|
|
||||||
|
int free_ent = 0; // first unused entry
|
||||||
|
|
||||||
|
// block compression parameters -- after all codes are used up,
|
||||||
|
// and compression rate changes, start over.
|
||||||
|
boolean clear_flg = false;
|
||||||
|
|
||||||
|
// Algorithm: use open addressing double hashing (no chaining) on the
|
||||||
|
// prefix code / next character combination. We do a variant of Knuth's
|
||||||
|
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
|
||||||
|
// secondary probe. Here, the modular division first probe is gives way
|
||||||
|
// to a faster exclusive-or manipulation. Also do block compression with
|
||||||
|
// an adaptive reset, whereby the code table is cleared when the compression
|
||||||
|
// ratio decreases, but after the table fills. The variable-length output
|
||||||
|
// codes are re-sized at this point, and a special CLEAR code is generated
|
||||||
|
// for the decompressor. Late addition: construct the table according to
|
||||||
|
// file size for noticeable speed improvement on small files. Please direct
|
||||||
|
// questions about this implementation to ames!jaw.
|
||||||
|
|
||||||
|
int g_init_bits;
|
||||||
|
|
||||||
|
int ClearCode;
|
||||||
|
int EOFCode;
|
||||||
|
|
||||||
|
void compress( int init_bits, OutputStream outs ) throws IOException
|
||||||
|
{
|
||||||
|
int fcode;
|
||||||
|
int i /* = 0 */;
|
||||||
|
int c;
|
||||||
|
int ent;
|
||||||
|
int disp;
|
||||||
|
int hsize_reg;
|
||||||
|
int hshift;
|
||||||
|
|
||||||
|
// Set up the globals: g_init_bits - initial number of bits
|
||||||
|
g_init_bits = init_bits;
|
||||||
|
|
||||||
|
// Set up the necessary values
|
||||||
|
clear_flg = false;
|
||||||
|
n_bits = g_init_bits;
|
||||||
|
maxcode = MAXCODE( n_bits );
|
||||||
|
|
||||||
|
ClearCode = 1 << ( init_bits - 1 );
|
||||||
|
EOFCode = ClearCode + 1;
|
||||||
|
free_ent = ClearCode + 2;
|
||||||
|
|
||||||
|
char_init();
|
||||||
|
|
||||||
|
ent = GIFNextPixel();
|
||||||
|
|
||||||
|
hshift = 0;
|
||||||
|
for ( fcode = hsize; fcode < 65536; fcode *= 2 )
|
||||||
|
++hshift;
|
||||||
|
hshift = 8 - hshift; // set hash code range bound
|
||||||
|
|
||||||
|
hsize_reg = hsize;
|
||||||
|
cl_hash( hsize_reg ); // clear hash table
|
||||||
|
|
||||||
|
output( ClearCode, outs );
|
||||||
|
|
||||||
|
outer_loop:
|
||||||
|
while ( (c = GIFNextPixel()) != EOF )
|
||||||
|
{
|
||||||
|
fcode = ( c << maxbits ) + ent;
|
||||||
|
i = ( c << hshift ) ^ ent; // xor hashing
|
||||||
|
|
||||||
|
if ( htab[i] == fcode )
|
||||||
|
{
|
||||||
|
ent = codetab[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if ( htab[i] >= 0 ) // non-empty slot
|
||||||
|
{
|
||||||
|
disp = hsize_reg - i; // secondary hash (after G. Knott)
|
||||||
|
if ( i == 0 )
|
||||||
|
disp = 1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ( (i -= disp) < 0 )
|
||||||
|
i += hsize_reg;
|
||||||
|
|
||||||
|
if ( htab[i] == fcode )
|
||||||
|
{
|
||||||
|
ent = codetab[i];
|
||||||
|
continue outer_loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ( htab[i] >= 0 );
|
||||||
|
}
|
||||||
|
output( ent, outs );
|
||||||
|
ent = c;
|
||||||
|
if ( free_ent < maxmaxcode )
|
||||||
|
{
|
||||||
|
codetab[i] = free_ent++; // code -> hashtable
|
||||||
|
htab[i] = fcode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cl_block( outs );
|
||||||
|
}
|
||||||
|
// Put out the final code.
|
||||||
|
output( ent, outs );
|
||||||
|
output( EOFCode, outs );
|
||||||
|
}
|
||||||
|
|
||||||
|
// output
|
||||||
|
//
|
||||||
|
// Output the given code.
|
||||||
|
// Inputs:
|
||||||
|
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
|
||||||
|
// that n_bits =< wordsize - 1.
|
||||||
|
// Outputs:
|
||||||
|
// Outputs code to the file.
|
||||||
|
// Assumptions:
|
||||||
|
// Chars are 8 bits long.
|
||||||
|
// Algorithm:
|
||||||
|
// Maintain a BITS character long buffer (so that 8 codes will
|
||||||
|
// fit in it exactly). Use the VAX insv instruction to insert each
|
||||||
|
// code in turn. When the buffer fills up empty it and start over.
|
||||||
|
|
||||||
|
int cur_accum = 0;
|
||||||
|
int cur_bits = 0;
|
||||||
|
|
||||||
|
int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
|
||||||
|
0x001F, 0x003F, 0x007F, 0x00FF,
|
||||||
|
0x01FF, 0x03FF, 0x07FF, 0x0FFF,
|
||||||
|
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
|
||||||
|
|
||||||
|
void output( int code, OutputStream outs ) throws IOException
|
||||||
|
{
|
||||||
|
cur_accum &= masks[cur_bits];
|
||||||
|
|
||||||
|
if ( cur_bits > 0 )
|
||||||
|
cur_accum |= ( code << cur_bits );
|
||||||
|
else
|
||||||
|
cur_accum = code;
|
||||||
|
|
||||||
|
cur_bits += n_bits;
|
||||||
|
|
||||||
|
while ( cur_bits >= 8 )
|
||||||
|
{
|
||||||
|
char_out( (byte) ( cur_accum & 0xff ), outs );
|
||||||
|
cur_accum >>= 8;
|
||||||
|
cur_bits -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the next entry is going to be too big for the code size,
|
||||||
|
// then increase it, if possible.
|
||||||
|
if ( free_ent > maxcode || clear_flg )
|
||||||
|
{
|
||||||
|
if ( clear_flg )
|
||||||
|
{
|
||||||
|
maxcode = MAXCODE(n_bits = g_init_bits);
|
||||||
|
clear_flg = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++n_bits;
|
||||||
|
if ( n_bits == maxbits )
|
||||||
|
maxcode = maxmaxcode;
|
||||||
|
else
|
||||||
|
maxcode = MAXCODE(n_bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( code == EOFCode )
|
||||||
|
{
|
||||||
|
// At EOF, write the rest of the buffer.
|
||||||
|
while ( cur_bits > 0 )
|
||||||
|
{
|
||||||
|
char_out( (byte) ( cur_accum & 0xff ), outs );
|
||||||
|
cur_accum >>= 8;
|
||||||
|
cur_bits -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
flush_char( outs );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear out the hash table
|
||||||
|
|
||||||
|
// table clear for block compress
|
||||||
|
void cl_block( OutputStream outs ) throws IOException
|
||||||
|
{
|
||||||
|
cl_hash( hsize );
|
||||||
|
free_ent = ClearCode + 2;
|
||||||
|
clear_flg = true;
|
||||||
|
|
||||||
|
output( ClearCode, outs );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset code table
|
||||||
|
void cl_hash( int hsize )
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < hsize; ++i )
|
||||||
|
htab[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GIF Specific routines
|
||||||
|
|
||||||
|
// Number of characters so far in this 'packet'
|
||||||
|
int a_count;
|
||||||
|
|
||||||
|
// Set up the 'byte output' routine
|
||||||
|
void char_init()
|
||||||
|
{
|
||||||
|
a_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the storage for the packet accumulator
|
||||||
|
byte[] accum = new byte[256];
|
||||||
|
|
||||||
|
// Add a character to the end of the current packet, and if it is 254
|
||||||
|
// characters, flush the packet to disk.
|
||||||
|
void char_out( byte c, OutputStream outs ) throws IOException
|
||||||
|
{
|
||||||
|
accum[a_count++] = c;
|
||||||
|
if ( a_count >= 254 )
|
||||||
|
flush_char( outs );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush the packet to disk, and reset the accumulator
|
||||||
|
void flush_char( OutputStream outs ) throws IOException
|
||||||
|
{
|
||||||
|
if ( a_count > 0 )
|
||||||
|
{
|
||||||
|
outs.write( a_count );
|
||||||
|
outs.write( accum, 0, a_count );
|
||||||
|
a_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class GifEncoderHashitem
|
||||||
|
{
|
||||||
|
|
||||||
|
public int rgb;
|
||||||
|
public int count;
|
||||||
|
public int index;
|
||||||
|
public boolean isTransparent;
|
||||||
|
|
||||||
|
public GifEncoderHashitem( int rgb, int count, int index, boolean isTransparent )
|
||||||
|
{
|
||||||
|
this.rgb = rgb;
|
||||||
|
this.count = count;
|
||||||
|
this.index = index;
|
||||||
|
this.isTransparent = isTransparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
583
src/Acme/JPM/Encoders/GrayJPEG.java
Normal file
583
src/Acme/JPM/Encoders/GrayJPEG.java
Normal file
|
@ -0,0 +1,583 @@
|
||||||
|
//////////////////////// DISCLAIMER \\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
// \\
|
||||||
|
// This software is provided by Sean Patrick Breslin for \\
|
||||||
|
// your use or abuse, free of charge and royalties. This \\
|
||||||
|
// software is provide 'AS IS' and as such there are no \\
|
||||||
|
// warrenties expressed or implied. This software is by \\
|
||||||
|
// no means robust, optimized, efficient, tight or any of \\
|
||||||
|
// a number of adjectives used to describe software. This \\
|
||||||
|
// in mind, this software is not to be used for nuclear \\
|
||||||
|
// applications, world domination schemes or discount \\
|
||||||
|
// remote laser surgery. You may copy or modify this \\
|
||||||
|
// code as you see fit. This code works on my PC other \\
|
||||||
|
// than that I cannot help you with this code in any way. \\
|
||||||
|
// I have not decided whether or not I will support this \\
|
||||||
|
// code with bug fixes or enhancements but I'll think \\
|
||||||
|
// about it. \\
|
||||||
|
// \\
|
||||||
|
// USAGE: instanciate the class: \\
|
||||||
|
// GrayJPEG jpg = new GrayJPEG(); \\
|
||||||
|
// \\
|
||||||
|
// and call the method: \\
|
||||||
|
// jpg.compress(Image i, OutputStream os) \\
|
||||||
|
// \\
|
||||||
|
// Where 'i' is a standard Java Image object and \\
|
||||||
|
// 'os' is an output stream where you want the \\
|
||||||
|
// JPEG file written. \\
|
||||||
|
// \\
|
||||||
|
// NOTE: This code will only compress a grayscale image \\
|
||||||
|
// i.e. Black and White, though a color image will \\
|
||||||
|
// not crash the code the results are unknown. \\
|
||||||
|
// \\
|
||||||
|
// PS: I have compiled this with Java 1.0.2 and 1.1.3 \\
|
||||||
|
// and it works under both versions using windows \\
|
||||||
|
// 95 operating system, so knock yourself out and \\
|
||||||
|
// have fun with my code. \\
|
||||||
|
// \\
|
||||||
|
///////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
|
||||||
|
package Acme.JPM.Encoders;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
public class GrayJPEG {
|
||||||
|
// quantization table in zigzag form for output file
|
||||||
|
private byte[] QNT = {-1, -37, 0, 67, 0,
|
||||||
|
13,9,10,11,10,8,13,11,
|
||||||
|
10,11,14,14,13,15,19,32,
|
||||||
|
21,19,18,18,19,39,28,30,
|
||||||
|
23,32,46,41,49,48,46,41,
|
||||||
|
45,44,51,58,74,62,51,54,
|
||||||
|
70,55,44,45,64,87,65,70,
|
||||||
|
76,78,82,83,82,50,62,90,
|
||||||
|
97,90,80,96,74,81,82,79};
|
||||||
|
|
||||||
|
// table for doing quantization
|
||||||
|
private float[][] QT = {{13, 9, 8,13,19,32,41,49},
|
||||||
|
{10,10,11,15,21,46,48,44},
|
||||||
|
{11,10,13,19,32,46,55,45},
|
||||||
|
{11,14,18,23,41,70,64,50},
|
||||||
|
{14,18,30,45,54,87,82,62},
|
||||||
|
{19,28,44,51,65,83,90,74},
|
||||||
|
{39,51,62,70,82,97,96,81},
|
||||||
|
{58,74,76,78,90,80,82,79}};
|
||||||
|
|
||||||
|
// Zig Zag array
|
||||||
|
private int[][] ZZ = {{ 0, 1, 5, 6,14,15,27,28},
|
||||||
|
{ 2, 4, 7,13,16,26,29,42},
|
||||||
|
{ 3, 8,12,17,25,30,41,43},
|
||||||
|
{ 9,11,18,24,31,40,44,53},
|
||||||
|
{10,19,23,32,39,45,52,54},
|
||||||
|
{20,22,33,38,46,51,55,60},
|
||||||
|
{21,34,37,47,50,56,59,61},
|
||||||
|
{35,36,48,49,57,58,62,63}};
|
||||||
|
|
||||||
|
// AC Huffman table
|
||||||
|
private byte[] Bits = {0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,125};
|
||||||
|
private byte[] Huffval = {
|
||||||
|
1,2,3,0,4,17,5,18,33,49,65,6,19,81,97,7,34,113,20,
|
||||||
|
50,-127,-111,-95,8,35,66,-79,-63,21,82,-47,-16,36,
|
||||||
|
51,98,114,-126,9,10,22,23,24,25,26,37,38,39,40,41,
|
||||||
|
42,52,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,
|
||||||
|
84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,
|
||||||
|
115,116,117,118,119,120,121,122,-125,-124,-123,-122,
|
||||||
|
-121,-120,-119,-118,-110,-109,-108,-107,-106,-105,
|
||||||
|
-104,-103,-102,-94,-93,-92,-91,-90,-89,-88,-87,-86,
|
||||||
|
-78,-77,-76,-75,-74,-73,-72,-71,-70,-62,-61,-60,-59,
|
||||||
|
-58,-57,-56,-55,-54,-46,-45,-44,-43,-42,-41,-40,-39,
|
||||||
|
-38,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-15,-14,
|
||||||
|
-13,-12,-11,-10,-9,-8,-7,-6};
|
||||||
|
|
||||||
|
// Header data
|
||||||
|
private String str = "JFIF Breslin Engineering JPEG Image Compression";
|
||||||
|
private byte[] BE, APP0;
|
||||||
|
private byte[] SOI = {-1,-40};
|
||||||
|
private byte[] EOI = {-1,-39};
|
||||||
|
private byte[] SOF = {-1,-64,0,11,8,0,0,0,0,1,1,17,0};
|
||||||
|
private byte[] HuffDC = {-1,-60,0,31,0,0,1,5,1,1,1,1,1,1,0,0,
|
||||||
|
0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11};
|
||||||
|
private byte[] HuffACHeader = {-1,-60,0,-75,16};
|
||||||
|
private byte[] SOS = {-1,-38,0,8,1,1,0,0,63,0};
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
private int ln, X, Y, LASTK, I, J;
|
||||||
|
private int BitCnt, K, R, CODE, SI, SSSS;
|
||||||
|
private int[] BITS = new int[17];
|
||||||
|
private int[] HUFFVAL = new int[162];
|
||||||
|
private int[] HUFFCODE = new int[257];
|
||||||
|
private int[] HUFFSIZE = new int[257];
|
||||||
|
private int[] EHUFCO = new int[257];
|
||||||
|
private int[] EHUFSI = new int[257];
|
||||||
|
private OutputStream fos;
|
||||||
|
private Image image;
|
||||||
|
private long DATA=0;
|
||||||
|
|
||||||
|
public GrayJPEG() {
|
||||||
|
int x, y, ln = str.length();
|
||||||
|
BE = new byte[ln];
|
||||||
|
str.getBytes(0, ln, BE, 0);
|
||||||
|
ln += 2;
|
||||||
|
byte[] ap = {-1, -32, 0, (byte)ln};
|
||||||
|
APP0 = ap;
|
||||||
|
|
||||||
|
// Generate AC Huffman tables
|
||||||
|
// Get BITS data
|
||||||
|
BITS[0]=0;
|
||||||
|
for(x=1;x<17;x++)
|
||||||
|
BITS[x] = Bits[x-1];
|
||||||
|
|
||||||
|
// Get HUFFVAL data
|
||||||
|
for(x=0;x<162;x++) {
|
||||||
|
HUFFVAL[x] = Huffval[x];
|
||||||
|
if(HUFFVAL[x]<0) HUFFVAL[x] = 256 + HUFFVAL[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
Generate_size_table();
|
||||||
|
Generate_code_table();
|
||||||
|
Order_codes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void compress(Image i, OutputStream os){
|
||||||
|
int bkk, x, y, w, h, a, b, c, d;
|
||||||
|
int[][] blocks;
|
||||||
|
int[][] blk = new int[8][8];
|
||||||
|
|
||||||
|
image = i;
|
||||||
|
fos = os;
|
||||||
|
X = image.getWidth(null);
|
||||||
|
Y = image.getHeight(null);
|
||||||
|
ln = X*Y;
|
||||||
|
int[] data = new int[ln];
|
||||||
|
w = (X>>3)<<3;
|
||||||
|
h = (Y>>3)<<3;
|
||||||
|
getPixels(data);
|
||||||
|
|
||||||
|
// Set Image dimensions in Start of Frame header
|
||||||
|
SOF[5] = (byte)((h & 0x0000ff00)>>8);
|
||||||
|
SOF[6] = (byte)(h & 0x000000ff);
|
||||||
|
SOF[7] = (byte)((w & 0x0000ff00)>>8);
|
||||||
|
SOF[8] = (byte)(w & 0x000000ff);
|
||||||
|
|
||||||
|
// Set number of blocks
|
||||||
|
w = X/8; h = Y/8; ln = w*h;
|
||||||
|
blocks = new int[ln][];
|
||||||
|
bkk=0;
|
||||||
|
|
||||||
|
// break image in to 8x8 blocks
|
||||||
|
for(a=0;a<(h*8);a+=8) {
|
||||||
|
// Get 8 lines of image
|
||||||
|
for(b=0;b<(w*8);b+=8) {
|
||||||
|
// Get block for FDCT
|
||||||
|
for(c=0;c<8;c++)
|
||||||
|
for(d=0;d<8;d++)
|
||||||
|
// mask off 3 upper bytes of image data
|
||||||
|
blk[c][d] = (data[(a+c)*X+b+d] & 0x000000ff)-128;
|
||||||
|
|
||||||
|
//Do FDCT on Block
|
||||||
|
blocks[bkk++] = FDCT(blk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Write out header data
|
||||||
|
writeHeaders();
|
||||||
|
|
||||||
|
// Encode image scan
|
||||||
|
Huffman(blocks);
|
||||||
|
|
||||||
|
// Write out last bits of image scan
|
||||||
|
writeEndData();
|
||||||
|
|
||||||
|
// Write End of Image marker
|
||||||
|
writeEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Huffman(int[][] blocks) {
|
||||||
|
int tmp, lp, DIFF, dat, PRED, bits;
|
||||||
|
int[] blk;
|
||||||
|
byte[] data = new byte[1];
|
||||||
|
int[][] HuffTbl = {{2,0},{3,2},{3,3},{3,4},{3,5},{3,6},{4,14},{5,30},{6,62},{7,126},{8,254},{9,510}};
|
||||||
|
long buffer;
|
||||||
|
|
||||||
|
// Encode data blocks
|
||||||
|
// Initialize predictor for new scan
|
||||||
|
PRED = 0; BitCnt=0;
|
||||||
|
for(lp=0;lp<ln;lp++) {
|
||||||
|
blk = blocks[lp];
|
||||||
|
buffer = 0;
|
||||||
|
bits=0;
|
||||||
|
|
||||||
|
// Encode DC coefficient
|
||||||
|
tmp = DIFF = blk[0]-PRED;
|
||||||
|
if(tmp<0) { tmp=-tmp; DIFF--; }
|
||||||
|
|
||||||
|
// find magnitude category data for DC edcoding
|
||||||
|
SSSS = MagCat(tmp);
|
||||||
|
|
||||||
|
// Total number of bits output
|
||||||
|
bits = HuffTbl[SSSS][0] + SSSS;
|
||||||
|
|
||||||
|
// Code word for this category shifted
|
||||||
|
//to accept magnitude data
|
||||||
|
buffer = HuffTbl[SSSS][1]<<SSSS;
|
||||||
|
|
||||||
|
// append bits to code word
|
||||||
|
buffer+=(DIFF&((1<<SSSS)-1));
|
||||||
|
|
||||||
|
// Write out data if greater then or equal to one byte
|
||||||
|
writeData(buffer, bits);
|
||||||
|
|
||||||
|
// Assign PRED for next DC DIFF
|
||||||
|
PRED = blk[0];
|
||||||
|
|
||||||
|
// Encode AC coefficients
|
||||||
|
Encode_AC_coefficients(blk);
|
||||||
|
|
||||||
|
// free up memory for next compression
|
||||||
|
blocks[lp]=null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Encode_AC_coefficients(int[] ZZ) {
|
||||||
|
boolean done = false;
|
||||||
|
int size, code, dat;
|
||||||
|
K=0; R=0;
|
||||||
|
|
||||||
|
while(!done) {
|
||||||
|
K++; dat = ZZ[K];
|
||||||
|
|
||||||
|
if(dat==0) {
|
||||||
|
if(K==63) {
|
||||||
|
// mark EOB
|
||||||
|
code = EHUFCO[0x00];
|
||||||
|
size = EHUFSI[0x00];
|
||||||
|
writeData(code, size);
|
||||||
|
done=true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
R++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if(R>15) {
|
||||||
|
R-=16;
|
||||||
|
// Mark RLZ in scan
|
||||||
|
code = EHUFCO[0xf0];
|
||||||
|
size = EHUFSI[0xf0];
|
||||||
|
writeData(code, size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Encode_R(ZZ[K]);
|
||||||
|
R=0;
|
||||||
|
if(K==63) done=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Encode_R(int ZZ) {
|
||||||
|
int dat, RS, size, code;
|
||||||
|
dat = ZZ;
|
||||||
|
if(ZZ<0) {
|
||||||
|
dat=-dat;
|
||||||
|
ZZ--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get AC magnitude category
|
||||||
|
SSSS = MagCat(dat);
|
||||||
|
RS = (R<<4) + SSSS;
|
||||||
|
|
||||||
|
// append bits
|
||||||
|
code = EHUFCO[RS];
|
||||||
|
size = EHUFSI[RS];
|
||||||
|
writeData(code, size);
|
||||||
|
|
||||||
|
// Mask off upper bits of ZZ
|
||||||
|
ZZ &= ((1<<SSSS)-1);
|
||||||
|
|
||||||
|
// append SSS low order bits of ZZ(K)
|
||||||
|
writeData(ZZ, SSSS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Generate_size_table() {
|
||||||
|
// Generate HUFFSIZE table Flow Chart C.1
|
||||||
|
K=0; I=1; J=1;
|
||||||
|
while(true) {
|
||||||
|
if(J>BITS[I]) {
|
||||||
|
J=1; I++;
|
||||||
|
if(I > 16) break;
|
||||||
|
} else {
|
||||||
|
HUFFSIZE[K++]=I;
|
||||||
|
J++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HUFFSIZE[K] = 0;
|
||||||
|
LASTK = K;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Generate_code_table() {
|
||||||
|
// Generate Code table Flow Chart C.2
|
||||||
|
K = 0; CODE = 0; SI = HUFFSIZE[0];
|
||||||
|
while(true) {
|
||||||
|
HUFFCODE[K++] = CODE++;
|
||||||
|
if(HUFFSIZE[K] == SI) continue;
|
||||||
|
if(HUFFSIZE[K] == 0) break;
|
||||||
|
while(true){
|
||||||
|
CODE<<=1; SI++;
|
||||||
|
if(HUFFSIZE[K] == SI)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Order_codes() {
|
||||||
|
// Order Codes Flow Chart C.3
|
||||||
|
K=0;
|
||||||
|
while(true) {
|
||||||
|
I = HUFFVAL[K];
|
||||||
|
EHUFCO[I] = HUFFCODE[K];
|
||||||
|
EHUFSI[I] = HUFFSIZE[K++];
|
||||||
|
if(K >= LASTK) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int MagCat(int dat) {
|
||||||
|
int ln;
|
||||||
|
for(;;) {
|
||||||
|
if(dat==0) { ln = 0; break; }
|
||||||
|
if(dat==1) { ln = 1; break; }
|
||||||
|
if(dat<=3) { ln = 2; break; }
|
||||||
|
if(dat<=7) { ln = 3; break; }
|
||||||
|
if(dat<=15) { ln = 4; break; }
|
||||||
|
if(dat<=31) { ln = 5; break; }
|
||||||
|
if(dat<=63) { ln = 6; break; }
|
||||||
|
if(dat<=127) { ln = 7; break; }
|
||||||
|
if(dat<=255) { ln = 8; break; }
|
||||||
|
if(dat<=511) { ln = 9; break; }
|
||||||
|
if(dat<=1023) { ln = 10; break; }
|
||||||
|
if(dat<=2047) { ln = 11; break; }
|
||||||
|
}
|
||||||
|
return ln;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeHeaders() {
|
||||||
|
try {
|
||||||
|
fos.write(SOI); // Start of Image marker
|
||||||
|
fos.write(APP0); // Application header
|
||||||
|
fos.write(BE); // JFIF BE jpg compression
|
||||||
|
fos.write(QNT); // Quantization table
|
||||||
|
fos.write(SOF); // Start of Frame marker
|
||||||
|
fos.write(HuffDC); // DC Huffman Table
|
||||||
|
fos.write(HuffACHeader); // AC Huffman Table Header
|
||||||
|
fos.write(Bits); // AC Huffman Table
|
||||||
|
fos.write(Huffval); // AC Huffman Table
|
||||||
|
fos.write(SOS); // Start of Scan header
|
||||||
|
} catch(IOException ioe) { System.err.println("IOException: " + ioe); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeEnd() {
|
||||||
|
try {
|
||||||
|
fos.write(EOI); // End of Image/File
|
||||||
|
fos.close();
|
||||||
|
} catch(IOException ioe) { System.err.println("IOException: " + ioe); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getPixels(int[] data) {
|
||||||
|
PixelGrabber pg = new PixelGrabber(image.getSource(), 0, 0, X, Y, data, 0, X);
|
||||||
|
try { if(pg.grabPixels() != true) {
|
||||||
|
try {
|
||||||
|
throw new AWTException("Grabber returned false: " + pg.status());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println("System Failed to get Pixels! - " + ex);
|
||||||
|
System.exit(0);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ent) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeData(long dat, int bits) {
|
||||||
|
int tmp;
|
||||||
|
byte[] stuff = {0};
|
||||||
|
byte[] d = new byte[1];
|
||||||
|
|
||||||
|
if(bits>0) {
|
||||||
|
DATA<<=bits;
|
||||||
|
DATA+=dat;
|
||||||
|
BitCnt+=bits;
|
||||||
|
}else
|
||||||
|
return;
|
||||||
|
|
||||||
|
// output bytes untill cnt is less then 8
|
||||||
|
while(BitCnt>7) {
|
||||||
|
BitCnt-=8;
|
||||||
|
tmp=(int)(DATA>>BitCnt);
|
||||||
|
d[0] = (byte)tmp;
|
||||||
|
|
||||||
|
// mask off 8 msb of DATA
|
||||||
|
DATA &= ((1<<BitCnt)-1);
|
||||||
|
|
||||||
|
// write out data
|
||||||
|
try {
|
||||||
|
fos.write(d); // End of Image/File
|
||||||
|
if(d[0]==-1)
|
||||||
|
// if 0xFF data stuff 0x00 byte
|
||||||
|
fos.write(stuff);
|
||||||
|
} catch(IOException ioe) { System.err.println("IOException: " + ioe); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeEndData() {
|
||||||
|
byte[] d = new byte[1];
|
||||||
|
// finish off scan data to byte boundry
|
||||||
|
if(BitCnt>0) {
|
||||||
|
DATA<<=(8-BitCnt);
|
||||||
|
DATA+=((1<<(8-BitCnt))-1);
|
||||||
|
d[0] = (byte)DATA;
|
||||||
|
try {
|
||||||
|
fos.write(d);
|
||||||
|
} catch(IOException ioe) { System.err.println("IOException: " + ioe); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] FDCT(int[][] block) {
|
||||||
|
int j1, i, j;
|
||||||
|
int[] blk = new int[64];
|
||||||
|
float[] b = new float[8];
|
||||||
|
float temp;
|
||||||
|
float[] b1 = new float[8];
|
||||||
|
float[][] d = new float[8][8];
|
||||||
|
float f0=(float)0.7071068,f1=(float)0.4903926;
|
||||||
|
float f2=(float)0.4619398,f3=(float)0.4157348;
|
||||||
|
float f4=(float)0.3535534,f5=(float)0.2777851;
|
||||||
|
float f6=(float)0.1913417,f7=(float)0.0975452;
|
||||||
|
|
||||||
|
float df7f1=(float)-0.3928475, sf7f1=(float)0.5879378 ;
|
||||||
|
float df3f5=(float)0.1379497, sf3f5=(float)0.6935199 ;
|
||||||
|
float df6f2=(float)-0.27059805, sf6f2=(float).6532815 ;
|
||||||
|
|
||||||
|
for(i = 0; i < 8; i++) {
|
||||||
|
for(j = 0; j < 8; j++) {
|
||||||
|
b[j] = block[i][j];
|
||||||
|
}
|
||||||
|
// Horizontal transform
|
||||||
|
for(j = 0; j < 4; j++) {
|
||||||
|
j1 = 7 - j;
|
||||||
|
b1[j] = b[j] + b[j1];
|
||||||
|
b1[j1] = b[j] - b[j1];
|
||||||
|
}
|
||||||
|
b[0] = b1[0] + b1[3];
|
||||||
|
b[1] = b1[1] + b1[2];
|
||||||
|
b[2] = b1[1] - b1[2];
|
||||||
|
b[3] = b1[0] - b1[3];
|
||||||
|
b[4] = b1[4];
|
||||||
|
b[5] = (b1[6] - b1[5]) * f0;
|
||||||
|
b[6] = (b1[6] + b1[5]) * f0;
|
||||||
|
b[7] = b1[7];
|
||||||
|
d[i][0] = (b[0] + b[1]) * f4;
|
||||||
|
d[i][4] = (b[0] - b[1]) * f4;
|
||||||
|
|
||||||
|
temp = (b[3]+b[2])*f6 ;
|
||||||
|
d[i][2] = temp - b[3]*df6f2 ;
|
||||||
|
d[i][6] = temp - b[2]*sf6f2 ;
|
||||||
|
|
||||||
|
b1[4] = b[4] + b[5];
|
||||||
|
b1[7] = b[7] + b[6];
|
||||||
|
b1[5] = b[4] - b[5];
|
||||||
|
b1[6] = b[7] - b[6];
|
||||||
|
|
||||||
|
temp = (b1[7]+b1[4])*f7;
|
||||||
|
d[i][1] = temp - b1[7]*df7f1 ;
|
||||||
|
d[i][7] = temp - b1[4]*sf7f1 ;
|
||||||
|
|
||||||
|
temp = (b1[6]+b1[5])*f3;
|
||||||
|
d[i][5] = temp - b1[6]*df3f5 ;
|
||||||
|
d[i][3] = temp - b1[5]*sf3f5 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical transform
|
||||||
|
for(i = 0; i < 8; i++) {
|
||||||
|
for(j = 0; j < 4; j++) {
|
||||||
|
j1 = 7 - j;
|
||||||
|
b1[j] = d[j][i] + d[j1][i];
|
||||||
|
b1[j1] = d[j][i] - d[j1][i];
|
||||||
|
}
|
||||||
|
b[0] = b1[0] + b1[3];
|
||||||
|
b[1] = b1[1] + b1[2];
|
||||||
|
b[2] = b1[1] - b1[2];
|
||||||
|
b[3] = b1[0] - b1[3];
|
||||||
|
b[4] = b1[4];
|
||||||
|
b[5] = (b1[6] - b1[5]) * f0;
|
||||||
|
b[6] = (b1[6] + b1[5]) * f0;
|
||||||
|
b[7] = b1[7];
|
||||||
|
d[0][i] = (b[0] + b[1]) * f4;
|
||||||
|
d[4][i] = (b[0] - b[1]) * f4;
|
||||||
|
|
||||||
|
temp = (b[3]+b[2])*f6 ;
|
||||||
|
d[2][i] = temp - b[3]*df6f2 ;
|
||||||
|
d[6][i] = temp - b[2]*sf6f2 ;
|
||||||
|
|
||||||
|
b1[4] = b[4] + b[5];
|
||||||
|
b1[7] = b[7] + b[6];
|
||||||
|
b1[5] = b[4] - b[5];
|
||||||
|
b1[6] = b[7] - b[6];
|
||||||
|
|
||||||
|
temp = (b1[7]+b1[4])*f7;
|
||||||
|
d[1][i] = temp - b1[7]*df7f1 ;
|
||||||
|
d[7][i] = temp - b1[4]*sf7f1 ;
|
||||||
|
|
||||||
|
temp = (b1[6]+b1[5])*f3;
|
||||||
|
d[5][i] = temp - b1[6]*df3f5 ;
|
||||||
|
d[3][i] = temp - b1[5]*sf3f5 ;
|
||||||
|
|
||||||
|
}
|
||||||
|
for(i = 0; i < 8; i++) {
|
||||||
|
for(j = 0; j < 8; j++) {
|
||||||
|
// Quantize and ZigZag data block
|
||||||
|
blk[ZZ[i][j]] = (int)(d[i][j]/QT[i][j]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(args[0]);
|
||||||
|
URLConnection uc = url.openConnection();
|
||||||
|
uc.connect();
|
||||||
|
|
||||||
|
// Got to open a stream to get around Java NullPointerException bug.
|
||||||
|
InputStream f = uc.getInputStream();
|
||||||
|
|
||||||
|
String mimeType = uc.getContentType();
|
||||||
|
|
||||||
|
// Java prints (not throws!) a ClassNotFoundException if you try
|
||||||
|
// a getContent on text.html or audio/basic (or no doubt many
|
||||||
|
// other types).
|
||||||
|
if ( ! mimeType.startsWith( "image/" ) )
|
||||||
|
System.err.println( args[0] + " is not an image" );
|
||||||
|
else {
|
||||||
|
Object content = uc.getContent();
|
||||||
|
if ( ! ( content instanceof ImageProducer ) )
|
||||||
|
System.err.println( args[0] + " is not a known image type" );
|
||||||
|
else {
|
||||||
|
ImageProducer prod = (ImageProducer) content;
|
||||||
|
Toolkit tk = new Acme.JPM.StubToolkit();
|
||||||
|
Image img = tk.createImage(prod);
|
||||||
|
GrayJPEG jpg = new GrayJPEG();
|
||||||
|
jpg.compress(img, System.out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done with unnecessary stream.
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
System.err.println( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
271
src/Acme/JPM/Encoders/ImageEncoder.java
Normal file
271
src/Acme/JPM/Encoders/ImageEncoder.java
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
// ImageEncoder - abstract class for writing out an image
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Encoders;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Abstract class for writing out an image.
|
||||||
|
// <P>
|
||||||
|
// A framework for classes that encode and write out an image in
|
||||||
|
// a particular file format.
|
||||||
|
// <P>
|
||||||
|
// This provides a simplified rendition of the ImageConsumer interface.
|
||||||
|
// It always delivers the pixels as ints in the RGBdefault color model.
|
||||||
|
// It always provides them in top-down left-right order.
|
||||||
|
// If you want more flexibility you can always implement ImageConsumer
|
||||||
|
// directly.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Encoders/ImageEncoder.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see GifEncoder
|
||||||
|
// @see PpmEncoder
|
||||||
|
// @see Acme.JPM.Decoders.ImageDecoder
|
||||||
|
|
||||||
|
public abstract class ImageEncoder implements ImageConsumer
|
||||||
|
{
|
||||||
|
|
||||||
|
protected OutputStream out;
|
||||||
|
|
||||||
|
private ImageProducer producer;
|
||||||
|
private int width = -1;
|
||||||
|
private int height = -1;
|
||||||
|
private int hintflags = 0;
|
||||||
|
private boolean started = false;
|
||||||
|
private boolean encoding;
|
||||||
|
private IOException iox;
|
||||||
|
private static final ColorModel rgbModel = ColorModel.getRGBdefault();
|
||||||
|
private Hashtable props = null;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param img The image to encode.
|
||||||
|
// @param out The stream to write the bytes to.
|
||||||
|
public ImageEncoder( Image img, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
this( img.getSource(), out );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param producer The ImageProducer to encode.
|
||||||
|
// @param out The stream to write the bytes to.
|
||||||
|
public ImageEncoder( ImageProducer producer, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
this.producer = producer;
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Methods that subclasses implement.
|
||||||
|
|
||||||
|
/// Subclasses implement this to initialize an encoding.
|
||||||
|
abstract void encodeStart( int w, int h ) throws IOException;
|
||||||
|
|
||||||
|
/// Subclasses implement this to actually write out some bits. They
|
||||||
|
// are guaranteed to be delivered in top-down-left-right order.
|
||||||
|
// One int per pixel, index is row * scansize + off + col,
|
||||||
|
// RGBdefault (AARRGGBB) color model.
|
||||||
|
abstract void encodePixels(
|
||||||
|
int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
|
/// Subclasses implement this to finish an encoding.
|
||||||
|
abstract void encodeDone() throws IOException;
|
||||||
|
|
||||||
|
|
||||||
|
// Our own methods.
|
||||||
|
|
||||||
|
/// Call this after initialization to get things going.
|
||||||
|
public synchronized void encode() throws IOException
|
||||||
|
{
|
||||||
|
encoding = true;
|
||||||
|
iox = null;
|
||||||
|
producer.startProduction( this );
|
||||||
|
while ( encoding )
|
||||||
|
try
|
||||||
|
{
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
catch ( InterruptedException e ) {}
|
||||||
|
if ( iox != null )
|
||||||
|
throw iox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean accumulate = false;
|
||||||
|
private int[] accumulator;
|
||||||
|
|
||||||
|
private void encodePixelsWrapper(
|
||||||
|
int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if ( ! started )
|
||||||
|
{
|
||||||
|
started = true;
|
||||||
|
encodeStart( width, height );
|
||||||
|
if ( ( hintflags & TOPDOWNLEFTRIGHT ) == 0 )
|
||||||
|
{
|
||||||
|
accumulate = true;
|
||||||
|
accumulator = new int[width * height];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( accumulate )
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
System.arraycopy(
|
||||||
|
rgbPixels, row * scansize + off,
|
||||||
|
accumulator, ( y + row ) * width + x,
|
||||||
|
w );
|
||||||
|
else
|
||||||
|
encodePixels( x, y, w, h, rgbPixels, off, scansize );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encodeFinish() throws IOException
|
||||||
|
{
|
||||||
|
if ( accumulate )
|
||||||
|
{
|
||||||
|
encodePixels( 0, 0, width, height, accumulator, 0, width );
|
||||||
|
accumulator = null;
|
||||||
|
accumulate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void stop()
|
||||||
|
{
|
||||||
|
encoding = false;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Methods from ImageConsumer.
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperties( Hashtable props )
|
||||||
|
{
|
||||||
|
this.props = props;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColorModel( ColorModel model )
|
||||||
|
{
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHints( int hintflags )
|
||||||
|
{
|
||||||
|
this.hintflags = hintflags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels(
|
||||||
|
int x, int y, int w, int h, ColorModel model, byte[] pixels,
|
||||||
|
int off, int scansize )
|
||||||
|
{
|
||||||
|
int[] rgbPixels = new int[w];
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
int rowOff = off + row * scansize;
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
rgbPixels[col] = model.getRGB( pixels[rowOff + col] & 0xff );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
encodePixelsWrapper( x, y + row, w, 1, rgbPixels, 0, w );
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
iox = e;
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels(
|
||||||
|
int x, int y, int w, int h, ColorModel model, int[] pixels,
|
||||||
|
int off, int scansize )
|
||||||
|
{
|
||||||
|
if ( model == rgbModel )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
encodePixelsWrapper( x, y, w, h, pixels, off, scansize );
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
iox = e;
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int[] rgbPixels = new int[w];
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
int rowOff = off + row * scansize;
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
rgbPixels[col] = model.getRGB( pixels[rowOff + col] );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
encodePixelsWrapper( x, y + row, w, 1, rgbPixels, 0, w );
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
iox = e;
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void imageComplete( int status )
|
||||||
|
{
|
||||||
|
producer.removeConsumer( this );
|
||||||
|
if ( status == ImageConsumer.IMAGEABORTED )
|
||||||
|
iox = new IOException( "image aborted" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
encodeFinish();
|
||||||
|
encodeDone();
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
iox = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
448
src/Acme/JPM/Encoders/JpegEncoder.java
Normal file
448
src/Acme/JPM/Encoders/JpegEncoder.java
Normal file
|
@ -0,0 +1,448 @@
|
||||||
|
// JpegEncoder - write out an image as a JPEG
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Encoders;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Write out an image as a JPEG.
|
||||||
|
// DOESN'T WORK YET.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Encoders/JpegEncoder.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see ToJpeg
|
||||||
|
|
||||||
|
public class JpegEncoder extends ImageEncoder
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param img The image to encode.
|
||||||
|
// @param out The stream to write the JPEG to.
|
||||||
|
public JpegEncoder( Image img, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
super( img, out );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param prod The ImageProducer to encode.
|
||||||
|
// @param out The stream to write the JPEG to.
|
||||||
|
public JpegEncoder( ImageProducer prod, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
super( prod, out );
|
||||||
|
}
|
||||||
|
|
||||||
|
int qfactor = 100;
|
||||||
|
|
||||||
|
/// Set the Q-factor.
|
||||||
|
public void setQfactor( int qfactor )
|
||||||
|
{
|
||||||
|
this.qfactor = qfactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
int[][] rgbPixels;
|
||||||
|
|
||||||
|
void encodeStart( int width, int height ) throws IOException
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
rgbPixels = new int[height][width];
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodePixels(
|
||||||
|
int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// Save the pixels.
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
System.arraycopy(
|
||||||
|
rgbPixels, row * scansize + off,
|
||||||
|
this.rgbPixels[y + row], x, w );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodeDone() throws IOException
|
||||||
|
{
|
||||||
|
writeJfifHuffHeader();
|
||||||
|
// !!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Some of the following code is derived from the Berkeley Continuous
|
||||||
|
// Media Toolkit (http://bmrc.berkeley.edu/projects/cmt/), which is
|
||||||
|
// Copyright (c) 1996 The Regents of the University of California.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||||
|
// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE
|
||||||
|
// UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
// DAMAGE.
|
||||||
|
//
|
||||||
|
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
||||||
|
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
// ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION
|
||||||
|
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||||
|
|
||||||
|
|
||||||
|
// This array represents the default JFIF header for quality = 100 and
|
||||||
|
// size = 640x480, with Huffman tables. The values are adjusted when a
|
||||||
|
// file is generated.
|
||||||
|
private static byte[] jfifHuff100Header = {
|
||||||
|
// SOI
|
||||||
|
(byte) 0xFF, (byte) 0xD8,
|
||||||
|
|
||||||
|
// JFIF header
|
||||||
|
(byte) 0xFF, (byte) 0xE0, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x10, // Length = 16 bytes
|
||||||
|
(byte) 0x4A, (byte) 0x46, (byte) 0x49, (byte) 0x46, // "JFIF"
|
||||||
|
(byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
|
||||||
|
|
||||||
|
// Start of frame (section B.2.2)
|
||||||
|
(byte) 0xFF, (byte) 0xC0, // Baseline DCT
|
||||||
|
(byte) 0x00, (byte) 0x11, // Length = 17 bytes
|
||||||
|
(byte) 0x08, // Sample precision
|
||||||
|
(byte) 0x01, (byte) 0xE0, // Height
|
||||||
|
(byte) 0x02, (byte) 0x80, // Width
|
||||||
|
(byte) 0x03, // Number of components = 3
|
||||||
|
// Scan 1: 2:1 horiz, (byte) 1:1 vertical, (byte) use QT 0
|
||||||
|
(byte) 0x01, (byte) 0x21, (byte) 0x00,
|
||||||
|
// Scan 2: 1:1 horiz, (byte) 1:1 vertical, (byte) use QT 1
|
||||||
|
(byte) 0x02, (byte) 0x11, (byte) 0x01,
|
||||||
|
// Scan 3: 1:1 horiz, (byte) 1:1 vertical, (byte) use QT 1
|
||||||
|
(byte) 0x03, (byte) 0x11, (byte) 0x01,
|
||||||
|
|
||||||
|
// Define Quant table (section B.2.4.1)
|
||||||
|
(byte) 0xFF, (byte) 0xDB, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x84, // Length (both tables)
|
||||||
|
(byte) 0x00, // 8 bit values, (byte) table 0
|
||||||
|
(byte) 0x10, (byte) 0x0B, (byte) 0x0C, (byte) 0x0E, (byte) 0x0C,
|
||||||
|
(byte) 0x0A, (byte) 0x10, (byte) 0x0E, (byte) 0x0D, (byte) 0x0E,
|
||||||
|
(byte) 0x12, (byte) 0x11, (byte) 0x10, (byte) 0x13, (byte) 0x18,
|
||||||
|
(byte) 0x28, (byte) 0x1A, (byte) 0x18, (byte) 0x16, (byte) 0x16,
|
||||||
|
(byte) 0x18, (byte) 0x31, (byte) 0x23, (byte) 0x25, (byte) 0x1D,
|
||||||
|
(byte) 0x28, (byte) 0x3A, (byte) 0x33, (byte) 0x3D, (byte) 0x3C,
|
||||||
|
(byte) 0x39, (byte) 0x33, (byte) 0x38, (byte) 0x37, (byte) 0x40,
|
||||||
|
(byte) 0x48, (byte) 0x5C, (byte) 0x4E, (byte) 0x40, (byte) 0x44,
|
||||||
|
(byte) 0x57, (byte) 0x45, (byte) 0x37, (byte) 0x38, (byte) 0x50,
|
||||||
|
(byte) 0x6D, (byte) 0x51, (byte) 0x57, (byte) 0x5F, (byte) 0x62,
|
||||||
|
(byte) 0x67, (byte) 0x68, (byte) 0x67, (byte) 0x3E, (byte) 0x4D,
|
||||||
|
(byte) 0x71, (byte) 0x79, (byte) 0x70, (byte) 0x64, (byte) 0x78,
|
||||||
|
(byte) 0x5C, (byte) 0x65, (byte) 0x67, (byte) 0x63,
|
||||||
|
|
||||||
|
(byte) 0x01, // 8 bit values, (byte) table 1
|
||||||
|
(byte) 0x11, (byte) 0x12, (byte) 0x12, (byte) 0x18, (byte) 0x15,
|
||||||
|
(byte) 0x18, (byte) 0x2F, (byte) 0x1A, (byte) 0x1A, (byte) 0x2F,
|
||||||
|
(byte) 0x63, (byte) 0x42, (byte) 0x38, (byte) 0x42, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
|
||||||
|
// Define huffman table (section B.2.4.1)
|
||||||
|
(byte) 0xFF, (byte) 0xC4, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x1F, // Length (31 bytes)
|
||||||
|
(byte) 0x00, // DC, (byte) table 0
|
||||||
|
(byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x01, (byte) 0x01,
|
||||||
|
(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
|
||||||
|
(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08,
|
||||||
|
(byte) 0x09, (byte) 0x0A, (byte) 0x0B,
|
||||||
|
|
||||||
|
// Define huffman table (section B.2.4.1)
|
||||||
|
(byte) 0xFF, (byte) 0xC4, // Marker
|
||||||
|
(byte) 0x00, (byte) 0xB5, // Length (181 bytes)
|
||||||
|
(byte) 0x10, // AC, (byte) table 0
|
||||||
|
(byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x03, (byte) 0x03,
|
||||||
|
(byte) 0x02, (byte) 0x04, (byte) 0x03, (byte) 0x05, (byte) 0x05,
|
||||||
|
(byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x01,
|
||||||
|
(byte) 0x7D, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x00,
|
||||||
|
(byte) 0x04, (byte) 0x11, (byte) 0x05, (byte) 0x12, (byte) 0x21,
|
||||||
|
(byte) 0x31, (byte) 0x41, (byte) 0x06, (byte) 0x13, (byte) 0x51,
|
||||||
|
(byte) 0x61, (byte) 0x07, (byte) 0x22, (byte) 0x71, (byte) 0x14,
|
||||||
|
(byte) 0x32, (byte) 0x81, (byte) 0x91, (byte) 0xA1, (byte) 0x08,
|
||||||
|
(byte) 0x23, (byte) 0x42, (byte) 0xB1, (byte) 0xC1, (byte) 0x15,
|
||||||
|
(byte) 0x52, (byte) 0xD1, (byte) 0xF0, (byte) 0x24, (byte) 0x33,
|
||||||
|
(byte) 0x62, (byte) 0x72, (byte) 0x82, (byte) 0x09, (byte) 0x0A,
|
||||||
|
(byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1A,
|
||||||
|
(byte) 0x25, (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29,
|
||||||
|
(byte) 0x2A, (byte) 0x34, (byte) 0x35, (byte) 0x36, (byte) 0x37,
|
||||||
|
(byte) 0x38, (byte) 0x39, (byte) 0x3A, (byte) 0x43, (byte) 0x44,
|
||||||
|
(byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49,
|
||||||
|
(byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56,
|
||||||
|
(byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x5A, (byte) 0x63,
|
||||||
|
(byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68,
|
||||||
|
(byte) 0x69, (byte) 0x6A, (byte) 0x73, (byte) 0x74, (byte) 0x75,
|
||||||
|
(byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x7A,
|
||||||
|
(byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87,
|
||||||
|
(byte) 0x88, (byte) 0x89, (byte) 0x8A, (byte) 0x92, (byte) 0x93,
|
||||||
|
(byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98,
|
||||||
|
(byte) 0x99, (byte) 0x9A, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4,
|
||||||
|
(byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9,
|
||||||
|
(byte) 0xAA, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5,
|
||||||
|
(byte) 0xB6, (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA,
|
||||||
|
(byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6,
|
||||||
|
(byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xD2,
|
||||||
|
(byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7,
|
||||||
|
(byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xE1, (byte) 0xE2,
|
||||||
|
(byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
|
||||||
|
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xF1, (byte) 0xF2,
|
||||||
|
(byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
|
||||||
|
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA,
|
||||||
|
|
||||||
|
// Define huffman table (section B.2.4.1)
|
||||||
|
(byte) 0xFF, (byte) 0xC4, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x1F, // Length (31 bytes)
|
||||||
|
(byte) 0x01, // DC, (byte) table 1
|
||||||
|
(byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01,
|
||||||
|
(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
|
||||||
|
(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
|
||||||
|
(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08,
|
||||||
|
(byte) 0x09, (byte) 0x0A, (byte) 0x0B,
|
||||||
|
|
||||||
|
// Define huffman table (section B.2.4.1)
|
||||||
|
(byte) 0xFF, (byte) 0xC4, // Marker
|
||||||
|
(byte) 0x00, (byte) 0xB5, // Length (181 bytes)
|
||||||
|
(byte) 0x11, // AC, (byte) table 1
|
||||||
|
(byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x02, (byte) 0x04,
|
||||||
|
(byte) 0x04, (byte) 0x03, (byte) 0x04, (byte) 0x07, (byte) 0x05,
|
||||||
|
(byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x01, (byte) 0x02,
|
||||||
|
(byte) 0x77, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
|
||||||
|
(byte) 0x11, (byte) 0x04, (byte) 0x05, (byte) 0x21, (byte) 0x31,
|
||||||
|
(byte) 0x06, (byte) 0x12, (byte) 0x41, (byte) 0x51, (byte) 0x07,
|
||||||
|
(byte) 0x61, (byte) 0x71, (byte) 0x13, (byte) 0x22, (byte) 0x32,
|
||||||
|
(byte) 0x81, (byte) 0x08, (byte) 0x14, (byte) 0x42, (byte) 0x91,
|
||||||
|
(byte) 0xA1, (byte) 0xB1, (byte) 0xC1, (byte) 0x09, (byte) 0x23,
|
||||||
|
(byte) 0x33, (byte) 0x52, (byte) 0xF0, (byte) 0x15, (byte) 0x62,
|
||||||
|
(byte) 0x72, (byte) 0xD1, (byte) 0x0A, (byte) 0x16, (byte) 0x24,
|
||||||
|
(byte) 0x34, (byte) 0xE1, (byte) 0x25, (byte) 0xF1, (byte) 0x17,
|
||||||
|
(byte) 0x18, (byte) 0x19, (byte) 0x1A, (byte) 0x26, (byte) 0x27,
|
||||||
|
(byte) 0x28, (byte) 0x29, (byte) 0x2A, (byte) 0x35, (byte) 0x36,
|
||||||
|
(byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x3A, (byte) 0x43,
|
||||||
|
(byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48,
|
||||||
|
(byte) 0x49, (byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55,
|
||||||
|
(byte) 0x56, (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x5A,
|
||||||
|
(byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
|
||||||
|
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x73, (byte) 0x74,
|
||||||
|
(byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79,
|
||||||
|
(byte) 0x7A, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85,
|
||||||
|
(byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x8A,
|
||||||
|
(byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96,
|
||||||
|
(byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0x9A, (byte) 0xA2,
|
||||||
|
(byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7,
|
||||||
|
(byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xB2, (byte) 0xB3,
|
||||||
|
(byte) 0xB4, (byte) 0xB5, (byte) 0xB6, (byte) 0xB7, (byte) 0xB8,
|
||||||
|
(byte) 0xB9, (byte) 0xBA, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4,
|
||||||
|
(byte) 0xC5, (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9,
|
||||||
|
(byte) 0xCA, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5,
|
||||||
|
(byte) 0xD6, (byte) 0xD7, (byte) 0xD8, (byte) 0xD9, (byte) 0xDA,
|
||||||
|
(byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6,
|
||||||
|
(byte) 0xE7, (byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xF2,
|
||||||
|
(byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
|
||||||
|
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA,
|
||||||
|
|
||||||
|
// Start of Scan (section B.2.3)
|
||||||
|
(byte) 0xFF, (byte) 0xDA, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x0C, // Length of header
|
||||||
|
(byte) 0x03, // Number of image components
|
||||||
|
// Scan 1: use DC/AC huff tables 0/0
|
||||||
|
(byte) 0x01, (byte) 0x00,
|
||||||
|
// Scan 2: use DC/AC huff tables 1/1
|
||||||
|
(byte) 0x02, (byte) 0x11,
|
||||||
|
// Scan 3: use DC/AC huff tables 1/1
|
||||||
|
(byte) 0x03, (byte) 0x11,
|
||||||
|
(byte) 0x00, (byte) 0x3F, (byte) 0x00 // Not used
|
||||||
|
};
|
||||||
|
|
||||||
|
// This array represents the default JFIF header for quality = 100 and
|
||||||
|
// size = 640x480, without Huffman tables. The values are adjusted when a
|
||||||
|
// file is generated.
|
||||||
|
private static byte[] jfifNoHuff100Header = {
|
||||||
|
// SOI
|
||||||
|
(byte) 0xFF, (byte) 0xD8,
|
||||||
|
|
||||||
|
// JFIF header
|
||||||
|
(byte) 0xFF, (byte) 0xE0, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x10, // Length = 16 bytes
|
||||||
|
(byte) 0x4A, (byte) 0x46, (byte) 0x49, (byte) 0x46, // "JFIF"
|
||||||
|
(byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
|
||||||
|
|
||||||
|
// Start of frame (section B.2.2)
|
||||||
|
(byte) 0xFF, (byte) 0xC0, // Baseline DCT
|
||||||
|
(byte) 0x00, (byte) 0x11, // Length = 17 bytes
|
||||||
|
(byte) 0x08, // Sample precision
|
||||||
|
(byte) 0x01, (byte) 0xE0, // Height
|
||||||
|
(byte) 0x02, (byte) 0x80, // Width
|
||||||
|
(byte) 0x03, // Number of components = 3
|
||||||
|
// Scan 1: 2:1 horiz, (byte) 1:1 vertical, (byte) use QT 0
|
||||||
|
(byte) 0x01, (byte) 0x21, (byte) 0x00,
|
||||||
|
// Scan 2: 1:1 horiz, (byte) 1:1 vertical, (byte) use QT 1
|
||||||
|
(byte) 0x02, (byte) 0x11, (byte) 0x01,
|
||||||
|
// Scan 3: 1:1 horiz, (byte) 1:1 vertical, (byte) use QT 1
|
||||||
|
(byte) 0x03, (byte) 0x11, (byte) 0x01,
|
||||||
|
|
||||||
|
// Define Quant table (section B.2.4.1)
|
||||||
|
(byte) 0xFF, (byte) 0xDB, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x84, // Length (both tables)
|
||||||
|
(byte) 0x00, // 8 bit values, (byte) table 0
|
||||||
|
(byte) 0x10, (byte) 0x0B, (byte) 0x0C, (byte) 0x0E, (byte) 0x0C,
|
||||||
|
(byte) 0x0A, (byte) 0x10, (byte) 0x0E, (byte) 0x0D, (byte) 0x0E,
|
||||||
|
(byte) 0x12, (byte) 0x11, (byte) 0x10, (byte) 0x13, (byte) 0x18,
|
||||||
|
(byte) 0x28, (byte) 0x1A, (byte) 0x18, (byte) 0x16, (byte) 0x16,
|
||||||
|
(byte) 0x18, (byte) 0x31, (byte) 0x23, (byte) 0x25, (byte) 0x1D,
|
||||||
|
(byte) 0x28, (byte) 0x3A, (byte) 0x33, (byte) 0x3D, (byte) 0x3C,
|
||||||
|
(byte) 0x39, (byte) 0x33, (byte) 0x38, (byte) 0x37, (byte) 0x40,
|
||||||
|
(byte) 0x48, (byte) 0x5C, (byte) 0x4E, (byte) 0x40, (byte) 0x44,
|
||||||
|
(byte) 0x57, (byte) 0x45, (byte) 0x37, (byte) 0x38, (byte) 0x50,
|
||||||
|
(byte) 0x6D, (byte) 0x51, (byte) 0x57, (byte) 0x5F, (byte) 0x62,
|
||||||
|
(byte) 0x67, (byte) 0x68, (byte) 0x67, (byte) 0x3E, (byte) 0x4D,
|
||||||
|
(byte) 0x71, (byte) 0x79, (byte) 0x70, (byte) 0x64, (byte) 0x78,
|
||||||
|
(byte) 0x5C, (byte) 0x65, (byte) 0x67, (byte) 0x63,
|
||||||
|
|
||||||
|
(byte) 0x01, // 8 bit values, (byte) table 1
|
||||||
|
(byte) 0x11, (byte) 0x12, (byte) 0x12, (byte) 0x18, (byte) 0x15,
|
||||||
|
(byte) 0x18, (byte) 0x2F, (byte) 0x1A, (byte) 0x1A, (byte) 0x2F,
|
||||||
|
(byte) 0x63, (byte) 0x42, (byte) 0x38, (byte) 0x42, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
(byte) 0x63, (byte) 0x63, (byte) 0x63, (byte) 0x63,
|
||||||
|
|
||||||
|
// Start of Scan (section B.2.3)
|
||||||
|
(byte) 0xFF, (byte) 0xDA, // Marker
|
||||||
|
(byte) 0x00, (byte) 0x0C, // Length of header
|
||||||
|
(byte) 0x03, // Number of image components
|
||||||
|
// Scan 1: use DC/AC huff tables 0/0
|
||||||
|
(byte) 0x01, (byte) 0x00,
|
||||||
|
// Scan 2: use DC/AC huff tables 1/1
|
||||||
|
(byte) 0x02, (byte) 0x11,
|
||||||
|
// Scan 3: use DC/AC huff tables 1/1
|
||||||
|
(byte) 0x03, (byte) 0x11,
|
||||||
|
(byte) 0x00, (byte) 0x3F, (byte) 0x00 // Not used
|
||||||
|
};
|
||||||
|
|
||||||
|
private void writeJfifHuffHeader() throws IOException
|
||||||
|
{
|
||||||
|
byte[] newHeader = new byte[jfifHuff100Header.length];
|
||||||
|
|
||||||
|
System.arraycopy(
|
||||||
|
jfifHuff100Header, 0, newHeader, 0, jfifHuff100Header.length );
|
||||||
|
|
||||||
|
// Set image width in JFIF header.
|
||||||
|
newHeader[27] = (byte) ( ( width >>> 8 ) & 0xff );
|
||||||
|
newHeader[28] = (byte) ( width & 0xff );
|
||||||
|
|
||||||
|
// Set image height in JFIF header.
|
||||||
|
newHeader[25] = (byte) ( ( height >>> 8 ) & 0xff );
|
||||||
|
newHeader[26] = (byte) ( height & 0xff );
|
||||||
|
|
||||||
|
// Adjust the quality factor.
|
||||||
|
//
|
||||||
|
// The default quality factor is 100, therefore if
|
||||||
|
// our quality factor does not equal 100 we must
|
||||||
|
// scale the quantization matrices in the JFIF header.
|
||||||
|
// Note that values are clipped to a max of 255.
|
||||||
|
if ( qfactor != 100 )
|
||||||
|
{
|
||||||
|
for ( int i = 44; i < 108; ++i )
|
||||||
|
{
|
||||||
|
int t = ( newHeader[i] * qfactor ) / 100;
|
||||||
|
newHeader[i] = (byte) Math.max( t, 0xff );
|
||||||
|
}
|
||||||
|
for ( int i = 109; i < 173; ++i )
|
||||||
|
{
|
||||||
|
int t = ( newHeader[i] * qfactor ) / 100;
|
||||||
|
newHeader[i] = (byte) Math.max( t, 0xff );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out buffer.
|
||||||
|
out.write( newHeader );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeJfifNoHuffHeader() throws IOException
|
||||||
|
{
|
||||||
|
byte[] newHeader = new byte[jfifNoHuff100Header.length];
|
||||||
|
|
||||||
|
System.arraycopy(
|
||||||
|
jfifNoHuff100Header, 0, newHeader, 0, jfifNoHuff100Header.length );
|
||||||
|
|
||||||
|
// Set image width in JFIF header.
|
||||||
|
newHeader[27] = (byte) ( ( width >>> 8 ) & 0xff );
|
||||||
|
newHeader[28] = (byte) ( width & 0xff );
|
||||||
|
|
||||||
|
// Set image height in JFIF header.
|
||||||
|
newHeader[25] = (byte) ( ( height >>> 8 ) & 0xff );
|
||||||
|
newHeader[26] = (byte) ( height & 0xff );
|
||||||
|
|
||||||
|
// Adjust the quality factor.
|
||||||
|
//
|
||||||
|
// The default quality factor is 100, therefore if
|
||||||
|
// our quality factor does not equal 100 we must
|
||||||
|
// scale the quantization matrices in the JFIF header.
|
||||||
|
// Note that values are clipped to a max of 255.
|
||||||
|
if ( qfactor != 100 )
|
||||||
|
{
|
||||||
|
for ( int i = 44; i < 108; ++i )
|
||||||
|
{
|
||||||
|
int t = ( newHeader[i] * qfactor ) / 100;
|
||||||
|
newHeader[i] = (byte) Math.max( t, 0xff );
|
||||||
|
}
|
||||||
|
for ( int i = 109; i < 173; ++i )
|
||||||
|
{
|
||||||
|
int t = ( newHeader[i] * qfactor ) / 100;
|
||||||
|
newHeader[i] = (byte) Math.max( t, 0xff );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out buffer.
|
||||||
|
out.write( newHeader );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
103
src/Acme/JPM/Encoders/PpmEncoder.java
Normal file
103
src/Acme/JPM/Encoders/PpmEncoder.java
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// PpmEncoder - write out an image as a PPM
|
||||||
|
//
|
||||||
|
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Encoders;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Write out an image as a PPM.
|
||||||
|
// <P>
|
||||||
|
// Writes an image onto a specified OutputStream in the PPM file format.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Encoders/PpmEncoder.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see ToPpm
|
||||||
|
|
||||||
|
public class PpmEncoder extends ImageEncoder
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param img The image to encode.
|
||||||
|
// @param out The stream to write the PPM to.
|
||||||
|
public PpmEncoder( Image img, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
super( img, out );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param prod The ImageProducer to encode.
|
||||||
|
// @param out The stream to write the PPM to.
|
||||||
|
public PpmEncoder( ImageProducer prod, OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
super( prod, out );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void encodeStart( int width, int height ) throws IOException
|
||||||
|
{
|
||||||
|
writeString( out, "P6\n" );
|
||||||
|
writeString( out, width + " " + height + "\n" );
|
||||||
|
writeString( out, "255\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeString( OutputStream out, String str ) throws IOException
|
||||||
|
{
|
||||||
|
byte[] buf = str.getBytes();
|
||||||
|
out.write( buf );
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodePixels(
|
||||||
|
int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
byte[] ppmPixels = new byte[w * 3];
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
int rowOff = off + row * scansize;
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
{
|
||||||
|
int i = rowOff + col;
|
||||||
|
int j = col * 3;
|
||||||
|
ppmPixels[j ] = (byte) ( ( rgbPixels[i] & 0xff0000 ) >> 16 );
|
||||||
|
ppmPixels[j + 1] = (byte) ( ( rgbPixels[i] & 0x00ff00 ) >> 8 );
|
||||||
|
ppmPixels[j + 2] = (byte) ( rgbPixels[i] & 0x0000ff );
|
||||||
|
}
|
||||||
|
out.write( ppmPixels );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodeDone() throws IOException
|
||||||
|
{
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
106
src/Acme/JPM/Filters/CompositeFilter.java
Normal file
106
src/Acme/JPM/Filters/CompositeFilter.java
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// CompositeFilter - compose two filters into one
|
||||||
|
//
|
||||||
|
// Copyright (C) 1997 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/// Compose two filters into one.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/CompositeFilter.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class CompositeFilter extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
private ImageFilterPlus filterOne, filterTwo;
|
||||||
|
private ImageFilter instanceOne, instanceTwo;
|
||||||
|
|
||||||
|
/// Constructor. Builds a filter chain with a FilteredImageSource as
|
||||||
|
// the glue.
|
||||||
|
public CompositeFilter( ImageProducer producer, ImageFilterPlus filterOne, ImageFilterPlus filterTwo )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.filterOne = filterOne;
|
||||||
|
this.filterTwo = filterTwo;
|
||||||
|
|
||||||
|
filterOne.setSource( producer );
|
||||||
|
ImageProducer producerOne =
|
||||||
|
new FilteredImageSource( producer, filterOne );
|
||||||
|
filterTwo.setSource( producerOne );
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageFilter getFilterInstance( ImageConsumer consumer )
|
||||||
|
{
|
||||||
|
CompositeFilter instance = (CompositeFilter) clone();
|
||||||
|
instance.instanceTwo = filterTwo.getFilterInstance( consumer );
|
||||||
|
instance.instanceOne = filterOne.getFilterInstance( instanceTwo );
|
||||||
|
return (ImageFilter) instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The rest of the methods just delegate to instanceOne.
|
||||||
|
|
||||||
|
public void setColorModel( ColorModel model )
|
||||||
|
{
|
||||||
|
instanceOne.setColorModel( model );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
instanceOne.setDimensions( width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHints( int hintflags )
|
||||||
|
{
|
||||||
|
instanceOne.setHints( hintflags );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperties( Hashtable props )
|
||||||
|
{
|
||||||
|
instanceOne.setProperties( props );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
instanceOne.setPixels( x, y, w, h, model, pixels, off, scansize );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
instanceOne.setPixels( x, y, w, h, model, pixels, off, scansize );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void imageComplete( int status )
|
||||||
|
{
|
||||||
|
//super.imageComplete( status );
|
||||||
|
instanceOne.imageComplete( status );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
169
src/Acme/JPM/Filters/EdgeDetect.java
Normal file
169
src/Acme/JPM/Filters/EdgeDetect.java
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// EdgeDetect - edge-detection filter
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import Acme.JPM.Decoders.*;
|
||||||
|
import Acme.JPM.Encoders.*;
|
||||||
|
|
||||||
|
/// Edge-detection filter.
|
||||||
|
// <IMG ALIGN=RIGHT WIDTH=202 HEIGHT=200 SRC="Earth-EdgeDetect.jpg">
|
||||||
|
// <IMG ALIGN=RIGHT WIDTH=202 HEIGHT=200 SRC="Earth.jpg">
|
||||||
|
// <P>
|
||||||
|
// Outlines the edges of an image.
|
||||||
|
// The edge detection technique used is to take the Pythagorean sum of
|
||||||
|
// two Sobel gradient operators at 90 degrees to each other, separately
|
||||||
|
// for each color component.
|
||||||
|
// For more details see "Digital Image Processing" by Gonzalez and Wintz,
|
||||||
|
// chapter 7.
|
||||||
|
// <P>
|
||||||
|
// This filter is slow.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/EdgeDetect.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class EdgeDetect extends RGBAllFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
// Constructor.
|
||||||
|
public EdgeDetect( ImageProducer producer )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final double SCALE = 1.8D;
|
||||||
|
|
||||||
|
public void filterRGBAll( int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
int[][] newPixels = new int[height][width];
|
||||||
|
long sum1, sum2;
|
||||||
|
double sum;
|
||||||
|
int r, g, b;
|
||||||
|
|
||||||
|
// First and last rows are black.
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
newPixels[0][col] = 0xff000000;
|
||||||
|
newPixels[height - 1][col] = 0xff000000;
|
||||||
|
}
|
||||||
|
for ( int row = 1; row < height - 1; ++row )
|
||||||
|
{
|
||||||
|
// First and last columns are black too.
|
||||||
|
newPixels[row][0] = 0xff000000;
|
||||||
|
newPixels[row][width - 1] = 0xff000000;
|
||||||
|
// The real pixels.
|
||||||
|
for ( int col = 1; col < width - 1; ++col )
|
||||||
|
{
|
||||||
|
sum1 =
|
||||||
|
rgbModel.getRed( rgbPixels[row - 1][col + 1] ) -
|
||||||
|
rgbModel.getRed( rgbPixels[row - 1][col - 1] ) +
|
||||||
|
2 * (
|
||||||
|
rgbModel.getRed( rgbPixels[row][col + 1] ) -
|
||||||
|
rgbModel.getRed( rgbPixels[row][col - 1] ) ) +
|
||||||
|
rgbModel.getRed( rgbPixels[row + 1][col + 1] ) -
|
||||||
|
rgbModel.getRed( rgbPixels[row + 1][col - 1] );
|
||||||
|
sum2 = (
|
||||||
|
rgbModel.getRed( rgbPixels[row + 1][col - 1] ) +
|
||||||
|
2 * rgbModel.getRed( rgbPixels[row + 1][col] ) +
|
||||||
|
rgbModel.getRed( rgbPixels[row + 1][col + 1] )
|
||||||
|
) - (
|
||||||
|
rgbModel.getRed( rgbPixels[row - 1][col - 1] ) +
|
||||||
|
2 * rgbModel.getRed( rgbPixels[row - 1][col] ) +
|
||||||
|
rgbModel.getRed( rgbPixels[row - 1][col + 1] )
|
||||||
|
);
|
||||||
|
sum = Math.sqrt( (double) ( sum1*sum1 + sum2*sum2 ) ) / SCALE;
|
||||||
|
r = Math.min( (int) sum, 255 );
|
||||||
|
|
||||||
|
sum1 =
|
||||||
|
rgbModel.getGreen( rgbPixels[row - 1][col + 1] ) -
|
||||||
|
rgbModel.getGreen( rgbPixels[row - 1][col - 1] ) +
|
||||||
|
2 * (
|
||||||
|
rgbModel.getGreen( rgbPixels[row][col + 1] ) -
|
||||||
|
rgbModel.getGreen( rgbPixels[row][col - 1] ) ) +
|
||||||
|
rgbModel.getGreen( rgbPixels[row + 1][col + 1] ) -
|
||||||
|
rgbModel.getGreen( rgbPixels[row + 1][col - 1] );
|
||||||
|
sum2 = (
|
||||||
|
rgbModel.getGreen( rgbPixels[row + 1][col - 1] ) +
|
||||||
|
2 * rgbModel.getGreen( rgbPixels[row + 1][col] ) +
|
||||||
|
rgbModel.getGreen( rgbPixels[row + 1][col + 1] )
|
||||||
|
) - (
|
||||||
|
rgbModel.getGreen( rgbPixels[row - 1][col - 1] ) +
|
||||||
|
2 * rgbModel.getGreen( rgbPixels[row - 1][col] ) +
|
||||||
|
rgbModel.getGreen( rgbPixels[row - 1][col + 1] )
|
||||||
|
);
|
||||||
|
sum = Math.sqrt( (double) ( sum1*sum1 + sum2*sum2 ) ) / SCALE;
|
||||||
|
g = Math.min( (int) sum, 255 );
|
||||||
|
|
||||||
|
sum1 =
|
||||||
|
rgbModel.getBlue( rgbPixels[row - 1][col + 1] ) -
|
||||||
|
rgbModel.getBlue( rgbPixels[row - 1][col - 1] ) +
|
||||||
|
2 * (
|
||||||
|
rgbModel.getBlue( rgbPixels[row][col + 1] ) -
|
||||||
|
rgbModel.getBlue( rgbPixels[row][col - 1] ) ) +
|
||||||
|
rgbModel.getBlue( rgbPixels[row + 1][col + 1] ) -
|
||||||
|
rgbModel.getBlue( rgbPixels[row + 1][col - 1] );
|
||||||
|
sum2 = (
|
||||||
|
rgbModel.getBlue( rgbPixels[row + 1][col - 1] ) +
|
||||||
|
2 * rgbModel.getBlue( rgbPixels[row + 1][col] ) +
|
||||||
|
rgbModel.getBlue( rgbPixels[row + 1][col + 1] )
|
||||||
|
) - (
|
||||||
|
rgbModel.getBlue( rgbPixels[row - 1][col - 1] ) +
|
||||||
|
2 * rgbModel.getBlue( rgbPixels[row - 1][col] ) +
|
||||||
|
rgbModel.getBlue( rgbPixels[row - 1][col + 1] )
|
||||||
|
);
|
||||||
|
sum = Math.sqrt( (double) ( sum1*sum1 + sum2*sum2 ) ) / SCALE;
|
||||||
|
b = Math.min( (int) sum, 255 );
|
||||||
|
|
||||||
|
newPixels[row][col] =
|
||||||
|
0xff000000 | ( r << 16 ) | ( g << 8 ) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPixels( width, height, newPixels );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 0 )
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter = new EdgeDetect( null );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: EdgeDetect" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
140
src/Acme/JPM/Filters/Enlarge.java
Normal file
140
src/Acme/JPM/Filters/Enlarge.java
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
// Enlarge - an ImageFilter that enlarges by pixel replication
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// An ImageFilter that enlarges by pixel replication.
|
||||||
|
// <P>
|
||||||
|
// Enlarges an image an integral factor by replicating pixels.
|
||||||
|
// The output uses the same color model as the input.
|
||||||
|
// This filter is very fast.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Enlarge.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see Shrink
|
||||||
|
// @see ScaleCopy
|
||||||
|
|
||||||
|
public class Enlarge extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
private int multiplier;
|
||||||
|
private int newWidth, newHeight;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Enlarge( ImageProducer producer, int multiplier )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.multiplier = multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
newWidth = width * multiplier;
|
||||||
|
newHeight = height * multiplier;
|
||||||
|
consumer.setDimensions( newWidth, newHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
int newX = Math.min( x * multiplier, newWidth - 1 );
|
||||||
|
int newY = Math.min( y * multiplier, newHeight - 1 );
|
||||||
|
int newW = w * multiplier;
|
||||||
|
if ( newX + newW > newWidth )
|
||||||
|
newW = newWidth - newX;
|
||||||
|
int newH = h * multiplier;
|
||||||
|
if ( newY + newH > newHeight )
|
||||||
|
newH = newHeight - newY;
|
||||||
|
byte[] newPixels = new byte[newW * newH];
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
{
|
||||||
|
byte pixel = pixels[row * scansize + off + col];
|
||||||
|
for ( int i = 0; i < multiplier; ++i )
|
||||||
|
for ( int j = 0; j < multiplier; ++j )
|
||||||
|
{
|
||||||
|
int newRow = row * multiplier + i;
|
||||||
|
int newCol = col * multiplier + j;
|
||||||
|
newPixels[newRow * newW + newCol] = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumer.setPixels( newX, newY, newW, newH, model, newPixels, 0, newW );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
int newX = Math.min( x * multiplier, newWidth - 1 );
|
||||||
|
int newY = Math.min( y * multiplier, newHeight - 1 );
|
||||||
|
int newW = w * multiplier;
|
||||||
|
if ( newX + newW > newWidth )
|
||||||
|
newW = newWidth - newX;
|
||||||
|
int newH = h * multiplier;
|
||||||
|
if ( newY + newH > newHeight )
|
||||||
|
newH = newHeight - newY;
|
||||||
|
int[] newPixels = new int[newW * newH];
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
{
|
||||||
|
int pixel = pixels[row * scansize + off + col];
|
||||||
|
for ( int i = 0; i < multiplier; ++i )
|
||||||
|
for ( int j = 0; j < multiplier; ++j )
|
||||||
|
{
|
||||||
|
int newRow = row * multiplier + i;
|
||||||
|
int newCol = col * multiplier + j;
|
||||||
|
newPixels[newRow * newW + newCol] = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumer.setPixels( newX, newY, newW, newH, model, newPixels, 0, newW );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 1 )
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter =
|
||||||
|
new Enlarge( null, Integer.parseInt( args[0] ) );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Enlarge <multiplier>" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
295
src/Acme/JPM/Filters/Flip.java
Normal file
295
src/Acme/JPM/Filters/Flip.java
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
// Flip - an ImageFilter that flips or rotates the image
|
||||||
|
//
|
||||||
|
// Copyright (C) 1997 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// An ImageFilter that flips or rotates the image.
|
||||||
|
// <P>
|
||||||
|
// Flips the image left-right, top-bottom, rotates clockwise or
|
||||||
|
// counter-clockwise, or otherwise munges the image as specified.
|
||||||
|
// This filter is fast.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Flip.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Flip extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
/// The null transformation.
|
||||||
|
public static final int FLIP_NULL = 0;
|
||||||
|
|
||||||
|
/// Flip left to right.
|
||||||
|
public static final int FLIP_LR = 1;
|
||||||
|
|
||||||
|
/// Flip top to bottom.
|
||||||
|
public static final int FLIP_TB = 2;
|
||||||
|
|
||||||
|
/// Transpose X and Y - reflection along a diagonal.
|
||||||
|
public static final int FLIP_XY = 3;
|
||||||
|
|
||||||
|
/// Rotate clockwise 90 degrees.
|
||||||
|
public static final int FLIP_CW = 4;
|
||||||
|
|
||||||
|
/// Rotate counter-clockwise 90 degrees.
|
||||||
|
public static final int FLIP_CCW = 5;
|
||||||
|
|
||||||
|
/// Rotate 180 degrees.
|
||||||
|
public static final int FLIP_R180 = 6;
|
||||||
|
|
||||||
|
private int flipType;
|
||||||
|
private int width, height;
|
||||||
|
private int newWidth, newHeight;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Flip( ImageProducer producer, int flipType )
|
||||||
|
{
|
||||||
|
super( producer, true );
|
||||||
|
this.flipType = flipType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
switch ( flipType )
|
||||||
|
{
|
||||||
|
case FLIP_NULL:
|
||||||
|
case FLIP_LR:
|
||||||
|
case FLIP_TB:
|
||||||
|
case FLIP_R180:
|
||||||
|
newWidth = width;
|
||||||
|
newHeight = height;
|
||||||
|
break;
|
||||||
|
case FLIP_XY:
|
||||||
|
case FLIP_CW:
|
||||||
|
case FLIP_CCW:
|
||||||
|
newWidth = height;
|
||||||
|
newHeight = width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
consumer.setDimensions( newWidth, newHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
int newX = x;
|
||||||
|
int newY = y;
|
||||||
|
int newW = w;
|
||||||
|
int newH = h;
|
||||||
|
switch ( flipType )
|
||||||
|
{
|
||||||
|
case FLIP_NULL:
|
||||||
|
break;
|
||||||
|
case FLIP_LR:
|
||||||
|
newX = width - ( x + w );
|
||||||
|
break;
|
||||||
|
case FLIP_TB:
|
||||||
|
newY = height - ( y + h );
|
||||||
|
break;
|
||||||
|
case FLIP_XY:
|
||||||
|
newW = h;
|
||||||
|
newH = w;
|
||||||
|
newX = y;
|
||||||
|
newY = x;
|
||||||
|
break;
|
||||||
|
case FLIP_CW:
|
||||||
|
newW = h;
|
||||||
|
newH = w;
|
||||||
|
newX = height - ( y + h );
|
||||||
|
newY = x;
|
||||||
|
break;
|
||||||
|
case FLIP_CCW:
|
||||||
|
newW = h;
|
||||||
|
newH = w;
|
||||||
|
newX = y;
|
||||||
|
newY = width - ( x + w );
|
||||||
|
break;
|
||||||
|
case FLIP_R180:
|
||||||
|
newX = width - ( x + w );
|
||||||
|
newY = height - ( y + h );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byte[] newPixels = new byte[newW * newH];
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
{
|
||||||
|
int index = row * scansize + off + col;
|
||||||
|
int newRow = row;
|
||||||
|
int newCol = col;
|
||||||
|
switch ( flipType )
|
||||||
|
{
|
||||||
|
case FLIP_NULL:
|
||||||
|
break;
|
||||||
|
case FLIP_LR:
|
||||||
|
newCol = w - col - 1;
|
||||||
|
break;
|
||||||
|
case FLIP_TB:
|
||||||
|
newRow = h - row - 1;
|
||||||
|
break;
|
||||||
|
case FLIP_XY:
|
||||||
|
newRow = col;
|
||||||
|
newCol = row;
|
||||||
|
break;
|
||||||
|
case FLIP_CW:
|
||||||
|
newRow = col;
|
||||||
|
newCol = h - row - 1;;
|
||||||
|
break;
|
||||||
|
case FLIP_CCW:
|
||||||
|
newRow = w - col - 1;
|
||||||
|
newCol = row;
|
||||||
|
break;
|
||||||
|
case FLIP_R180:
|
||||||
|
newRow = h - row - 1;
|
||||||
|
newCol = w - col - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int newIndex = newRow * newW + newCol;
|
||||||
|
newPixels[newIndex] = pixels[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumer.setPixels( newX, newY, newW, newH, model, newPixels, 0, newW );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
int newX = x;
|
||||||
|
int newY = y;
|
||||||
|
int newW = w;
|
||||||
|
int newH = h;
|
||||||
|
switch ( flipType )
|
||||||
|
{
|
||||||
|
case FLIP_NULL:
|
||||||
|
break;
|
||||||
|
case FLIP_LR:
|
||||||
|
newX = width - ( x + w );
|
||||||
|
break;
|
||||||
|
case FLIP_TB:
|
||||||
|
newY = height - ( y + h );
|
||||||
|
break;
|
||||||
|
case FLIP_XY:
|
||||||
|
newW = h;
|
||||||
|
newH = w;
|
||||||
|
newX = y;
|
||||||
|
newY = x;
|
||||||
|
break;
|
||||||
|
case FLIP_CW:
|
||||||
|
newW = h;
|
||||||
|
newH = w;
|
||||||
|
newX = height - ( y + h );
|
||||||
|
newY = x;
|
||||||
|
break;
|
||||||
|
case FLIP_CCW:
|
||||||
|
newW = h;
|
||||||
|
newH = w;
|
||||||
|
newX = y;
|
||||||
|
newY = width - ( x + w );
|
||||||
|
break;
|
||||||
|
case FLIP_R180:
|
||||||
|
newX = width - ( x + w );
|
||||||
|
newY = height - ( y + h );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int[] newPixels = new int[newW * newH];
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
{
|
||||||
|
int index = row * scansize + off + col;
|
||||||
|
int newRow = row;
|
||||||
|
int newCol = col;
|
||||||
|
switch ( flipType )
|
||||||
|
{
|
||||||
|
case FLIP_NULL:
|
||||||
|
break;
|
||||||
|
case FLIP_LR:
|
||||||
|
newCol = w - col - 1;
|
||||||
|
break;
|
||||||
|
case FLIP_TB:
|
||||||
|
newRow = h - row - 1;
|
||||||
|
break;
|
||||||
|
case FLIP_XY:
|
||||||
|
newRow = col;
|
||||||
|
newCol = row;
|
||||||
|
break;
|
||||||
|
case FLIP_CW:
|
||||||
|
newRow = col;
|
||||||
|
newCol = h - row - 1;;
|
||||||
|
break;
|
||||||
|
case FLIP_CCW:
|
||||||
|
newRow = w - col - 1;
|
||||||
|
newCol = row;
|
||||||
|
break;
|
||||||
|
case FLIP_R180:
|
||||||
|
newRow = h - row - 1;
|
||||||
|
newCol = w - col - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int newIndex = newRow * newW + newCol;
|
||||||
|
newPixels[newIndex] = pixels[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumer.setPixels( newX, newY, newW, newH, model, newPixels, 0, newW );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 1 )
|
||||||
|
usage();
|
||||||
|
int flipType = FLIP_NULL;
|
||||||
|
if ( args[0].equalsIgnoreCase( "-lr" ) )
|
||||||
|
flipType = FLIP_LR;
|
||||||
|
else if ( args[0].equalsIgnoreCase( "-tb" ) )
|
||||||
|
flipType = FLIP_TB;
|
||||||
|
else if ( args[0].equalsIgnoreCase( "-xy" ) )
|
||||||
|
flipType = FLIP_XY;
|
||||||
|
else if ( args[0].equalsIgnoreCase( "-cw" ) )
|
||||||
|
flipType = FLIP_CW;
|
||||||
|
else if ( args[0].equalsIgnoreCase( "-ccw" ) )
|
||||||
|
flipType = FLIP_CCW;
|
||||||
|
else if ( args[0].equalsIgnoreCase( "-r180" ) )
|
||||||
|
flipType = FLIP_R180;
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter = new Flip( null, flipType );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Flip -lr|-tb|-xy|-cw|-ccw|-r180" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
148
src/Acme/JPM/Filters/Gamma.java
Normal file
148
src/Acme/JPM/Filters/Gamma.java
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
// Gamma - gamma-correction filter
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Gamma-correction filter.
|
||||||
|
// <P>
|
||||||
|
// Gamma correction fixes a form of color distortion common to many monitors.
|
||||||
|
// Values less than 1.0 darken the image, and greater than 1.0 lighten it.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Gamma.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Gamma extends RGBBlockFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
private double rValue, gValue, bValue;
|
||||||
|
|
||||||
|
/// Constructor, single exponent.
|
||||||
|
public Gamma( ImageProducer producer, double value )
|
||||||
|
{
|
||||||
|
this( producer, value, value, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor, different exponents for R G and B.
|
||||||
|
public Gamma( ImageProducer producer, double rValue, double gValue, double bValue )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.rValue = rValue;
|
||||||
|
this.gValue = gValue;
|
||||||
|
this.bValue = bValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int[] rTable, gTable, bTable;
|
||||||
|
|
||||||
|
public int[][] filterRGBBlock( int x, int y, int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
int rgb = rgbPixels[row][col];
|
||||||
|
int a = ( rgb >> 24 ) & 0xff;
|
||||||
|
int r = ( rgb >> 16 ) & 0xff;
|
||||||
|
int g = ( rgb >> 8 ) & 0xff;
|
||||||
|
int b = rgb & 0xff;
|
||||||
|
r = rTable[r];
|
||||||
|
g = gTable[g];
|
||||||
|
b = bTable[b];
|
||||||
|
rgbPixels[row][col] =
|
||||||
|
( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
|
||||||
|
}
|
||||||
|
return rgbPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean initialized = false;
|
||||||
|
|
||||||
|
private void initialize()
|
||||||
|
{
|
||||||
|
if ( initialized )
|
||||||
|
return;
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
rTable = buildTable( rValue );
|
||||||
|
|
||||||
|
if ( gValue == rValue )
|
||||||
|
gTable = rTable;
|
||||||
|
else
|
||||||
|
gTable = buildTable( gValue );
|
||||||
|
|
||||||
|
if ( bValue == rValue )
|
||||||
|
bTable = rTable;
|
||||||
|
else if ( bValue == gValue )
|
||||||
|
bTable = gTable;
|
||||||
|
else
|
||||||
|
bTable = buildTable( bValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] buildTable( double gamma )
|
||||||
|
{
|
||||||
|
int[] table = new int[256];
|
||||||
|
double oneOverGamma = 1.0D / gamma;
|
||||||
|
for ( int i = 0; i < 256; ++i )
|
||||||
|
{
|
||||||
|
int v = (int) (
|
||||||
|
( 255.0D * Math.pow( i / 255.0D, oneOverGamma ) ) + 0.5D );
|
||||||
|
if ( v > 255 )
|
||||||
|
v = 255;
|
||||||
|
table[i] = v;
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
ImageFilterPlus filter = null;
|
||||||
|
if ( args.length == 1 )
|
||||||
|
filter = new Gamma( null, Double.valueOf( args[0] ).doubleValue() );
|
||||||
|
else if ( args.length == 3 )
|
||||||
|
filter = new Gamma( null,
|
||||||
|
Double.valueOf( args[0] ).doubleValue(),
|
||||||
|
Double.valueOf( args[1] ).doubleValue(),
|
||||||
|
Double.valueOf( args[2] ).doubleValue() );
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Gamma <value>" );
|
||||||
|
System.err.println( "or: Gamma <rValue> <gValue> <bValue>" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
160
src/Acme/JPM/Filters/ImageFilterPlus.java
Normal file
160
src/Acme/JPM/Filters/ImageFilterPlus.java
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
// ImageFilterPlus - an ImageFilter with some extra features and bug fixes
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.*;
|
||||||
|
import Acme.JPM.Decoders.*;
|
||||||
|
import Acme.JPM.Encoders.*;
|
||||||
|
|
||||||
|
/// An ImageFilter with some extra features and bug fixes.
|
||||||
|
// <P>
|
||||||
|
// You can use an image filter to turn one Image into another via
|
||||||
|
// a FilteredImageSource, e.g.:
|
||||||
|
// <BLOCKQUOTE><CODE><PRE>
|
||||||
|
// Image newImage = comp.createImage( new FilteredImageSource(
|
||||||
|
// oldImage.getSource(), new SomeFilter( oldImage.getSource() ) ) );
|
||||||
|
// </PRE></CODE></BLOCKQUOTE>
|
||||||
|
// Or use the convenient utility JPMUtils.filterImage():
|
||||||
|
// <BLOCKQUOTE><CODE><PRE>
|
||||||
|
// Image newImage = JPMUtils.filterImage(
|
||||||
|
// comp, SomeFilter( oldImage.getSource() ) );
|
||||||
|
// </PRE></CODE></BLOCKQUOTE>
|
||||||
|
// <P>
|
||||||
|
// You can also use image filters from the command line, reading PPM
|
||||||
|
// from stdin and writing PPM to stdout, if you add code like the following
|
||||||
|
// to each filter:
|
||||||
|
// <BLOCKQUOTE><CODE><PRE>
|
||||||
|
// System.exit(
|
||||||
|
// ImageFilterPlus.filterStream(
|
||||||
|
// System.in, System.out,
|
||||||
|
// new SomeFilter( null ) ) );
|
||||||
|
// </PRE></CODE></BLOCKQUOTE>
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/ImageFilterPlus.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class ImageFilterPlus extends ImageFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
private ImageProducer producer;
|
||||||
|
private boolean pixelOrderChanges;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
// @param producer The ImageProducer is required, so that we can
|
||||||
|
// remove ourself from its consumers list when we're done.
|
||||||
|
// However, if you don't have the producer available when you want
|
||||||
|
// to create the filter, you can pass in null and set it later
|
||||||
|
// via setSource().
|
||||||
|
public ImageFilterPlus( ImageProducer producer )
|
||||||
|
{
|
||||||
|
this( producer, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor, with pixel order change.
|
||||||
|
// @param producer The ImageProducer is required, so that we can
|
||||||
|
// remove ourself from its consumers list when we're done.
|
||||||
|
// However, if you don't have the producer available when you want
|
||||||
|
// to create the filter, you can pass in null and set it later
|
||||||
|
// via setSource().
|
||||||
|
// @param pixelOrderChanges If the filter may output pixels in a different
|
||||||
|
// order from the one they were delivered in, this flag must be set.
|
||||||
|
public ImageFilterPlus( ImageProducer producer, boolean pixelOrderChanges )
|
||||||
|
{
|
||||||
|
setSource( producer );
|
||||||
|
this.pixelOrderChanges = pixelOrderChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The default color model - useful for comparisons.
|
||||||
|
public static final ColorModel rgbModel = ColorModel.getRGBdefault();
|
||||||
|
|
||||||
|
|
||||||
|
/// Return the ImageProducer for this filter.
|
||||||
|
public ImageProducer getSource()
|
||||||
|
{
|
||||||
|
return producer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the ImageProducer for this filter, if it wasn't set by the
|
||||||
|
// constructor.
|
||||||
|
public void setSource( ImageProducer producer )
|
||||||
|
{
|
||||||
|
this.producer = producer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Set the hint flags. If the pixel order may change, we have to
|
||||||
|
// turn off the TOPDOWNLEFTRIGHT flag; otherwise the flags are passed
|
||||||
|
// through unmodified.
|
||||||
|
public void setHints( int hintflags )
|
||||||
|
{
|
||||||
|
if ( pixelOrderChanges )
|
||||||
|
hintflags &= ~TOPDOWNLEFTRIGHT;
|
||||||
|
consumer.setHints( hintflags );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// This routine fixes a bug in java.awt.image.ImageFilter. All
|
||||||
|
// ImageConsumers are required remove themselves from the producer's
|
||||||
|
// list when they're done reading. If they don't do this then some
|
||||||
|
// producers will generate an error. The standard ImageFilter class
|
||||||
|
// fails to do this, but this one does it.
|
||||||
|
public void imageComplete( int status )
|
||||||
|
{
|
||||||
|
if ( status != ImageConsumer.SINGLEFRAMEDONE )
|
||||||
|
producer.removeConsumer( this );
|
||||||
|
super.imageComplete( status );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Filter a PPM InputStream to a PPM OutputStream.
|
||||||
|
// <P>
|
||||||
|
// Create the filter with a null producer, and this routine will
|
||||||
|
// fill it in for you.
|
||||||
|
// @return a status code suitable for use with System.exit().
|
||||||
|
public static int filterStream( InputStream in, OutputStream out, ImageFilterPlus filter )
|
||||||
|
{
|
||||||
|
ImageDecoder producer = new PpmDecoder( in );
|
||||||
|
filter.setSource( producer );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImageEncoder consumer = new PpmEncoder(
|
||||||
|
new FilteredImageSource( producer, filter ), out );
|
||||||
|
consumer.encode();
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
System.err.println( e.toString() );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
src/Acme/JPM/Filters/Invert.java
Normal file
83
src/Acme/JPM/Filters/Invert.java
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
// Invert - color-inversion filter
|
||||||
|
//
|
||||||
|
// Copyright (C) 1997 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Color-inversion filter.
|
||||||
|
// <P>
|
||||||
|
// Switches black for white, and all the other colors too.
|
||||||
|
// This filter is very fast.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Invert.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Invert extends RGBBlockFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Invert( ImageProducer producer )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int[][] filterRGBBlock( int x, int y, int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
int rgb = rgbPixels[row][col];
|
||||||
|
int alpha = rgb & 0xff000000;
|
||||||
|
int rest = ( ~ rgb ) & 0x00ffffff;
|
||||||
|
rgbPixels[row][col] = alpha | rest;
|
||||||
|
}
|
||||||
|
return rgbPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
ImageFilterPlus filter = null;
|
||||||
|
if ( args.length == 0 )
|
||||||
|
filter = new Invert( null );
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Invert" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
133
src/Acme/JPM/Filters/Margin.java
Normal file
133
src/Acme/JPM/Filters/Margin.java
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Margin - an ImageFilter that adds a margin to an image
|
||||||
|
//
|
||||||
|
// Copyright (C) 1997 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// An ImageFilter that adds a margin to an image.
|
||||||
|
// <P>
|
||||||
|
// Adds a margin of a specified color and width around an image.
|
||||||
|
// The output uses the same color model as the input.
|
||||||
|
// This filter is very fast.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Margin.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Margin extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
private Color color;
|
||||||
|
private int size;
|
||||||
|
private int width, height;
|
||||||
|
private int newWidth;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Margin( ImageProducer producer, Color color, int size )
|
||||||
|
{
|
||||||
|
super( producer, true );
|
||||||
|
this.color = color;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
newWidth = width + size * 2;
|
||||||
|
consumer.setDimensions( newWidth, height + size * 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean started = false;
|
||||||
|
|
||||||
|
private void start()
|
||||||
|
{
|
||||||
|
started = true;
|
||||||
|
int rgb = color.getRGB();
|
||||||
|
|
||||||
|
int[] fullRow = new int[newWidth];
|
||||||
|
for ( int col = 0; col < newWidth; ++col )
|
||||||
|
fullRow[col] = rgb;
|
||||||
|
for ( int row = 0; row < size; ++row )
|
||||||
|
{
|
||||||
|
consumer.setPixels(
|
||||||
|
0, row, newWidth, 1, rgbModel, fullRow, 0, newWidth );
|
||||||
|
consumer.setPixels(
|
||||||
|
0, size + height + row, newWidth, 1, rgbModel, fullRow, 0,
|
||||||
|
newWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] sideRow = new int[size];
|
||||||
|
for ( int col = 0; col < size; ++col )
|
||||||
|
sideRow[col] = rgb;
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
{
|
||||||
|
consumer.setPixels(
|
||||||
|
0, size + row, size, 1, rgbModel, sideRow, 0, size );
|
||||||
|
consumer.setPixels(
|
||||||
|
size + width, size + row, size, 1, rgbModel, sideRow, 0, size );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
if ( ! started )
|
||||||
|
start();
|
||||||
|
consumer.setPixels(
|
||||||
|
x + size, y + size, w, h, model, pixels, off, scansize );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
if ( ! started )
|
||||||
|
start();
|
||||||
|
consumer.setPixels(
|
||||||
|
x + size, y + size, w, h, model, pixels, off, scansize );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 1 )
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter =
|
||||||
|
new Margin( null, Color.black, Integer.parseInt( args[0] ) );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Margin <size>" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
141
src/Acme/JPM/Filters/Oil.java
Normal file
141
src/Acme/JPM/Filters/Oil.java
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// Oil - oil-transfer filter
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Oil-transfer filter.
|
||||||
|
// <IMG ALIGN=RIGHT WIDTH=202 HEIGHT=200 SRC="Earth-Oil.jpg">
|
||||||
|
// <IMG ALIGN=RIGHT WIDTH=202 HEIGHT=200 SRC="Earth.jpg">
|
||||||
|
// <P>
|
||||||
|
// The oil transfer is described in "Beyond Photography" by Holzmann,
|
||||||
|
// chapter 4, photo 7.
|
||||||
|
// It's a sort of localized smearing.
|
||||||
|
// The parameter controls the size of the smeared area, with a default of 3.
|
||||||
|
// <P>
|
||||||
|
// This filter is very slow.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Oil.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Oil extends RGBAllFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
private int n;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Oil( ImageProducer producer, int n )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.n = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor, default value.
|
||||||
|
public Oil( ImageProducer producer )
|
||||||
|
{
|
||||||
|
this( producer, 3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void filterRGBAll( int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
int[][] newPixels = new int[height][width];
|
||||||
|
int[] rHist = new int[256];
|
||||||
|
int[] gHist = new int[256];
|
||||||
|
int[] bHist = new int[256];
|
||||||
|
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
{
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < 256; ++i )
|
||||||
|
rHist[i] = gHist[i] = bHist[i] =0;
|
||||||
|
for ( int drow = row - n; drow <= row + n; ++drow )
|
||||||
|
if ( drow >= 0 && drow < height )
|
||||||
|
for ( int dcol = col - n; dcol <= col + n; ++dcol )
|
||||||
|
if ( dcol >= 0 && dcol < width )
|
||||||
|
{
|
||||||
|
int rgb = rgbPixels[drow][dcol];
|
||||||
|
rHist[( rgb >> 16 ) & 0xff]++;
|
||||||
|
gHist[( rgb >> 8 ) & 0xff]++;
|
||||||
|
bHist[rgb & 0xff]++;
|
||||||
|
}
|
||||||
|
int r = 0, g = 0, b = 0;
|
||||||
|
for ( int i = 1; i < 256; ++i )
|
||||||
|
{
|
||||||
|
if ( rHist[i] > rHist[r] )
|
||||||
|
r = i;
|
||||||
|
if ( gHist[i] > gHist[g] )
|
||||||
|
g = i;
|
||||||
|
if ( bHist[i] > bHist[b] )
|
||||||
|
b = i;
|
||||||
|
}
|
||||||
|
newPixels[row][col] =
|
||||||
|
0xff000000 | ( r << 16 ) | ( g << 8 ) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPixels( width, height, newPixels );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
int n = -1;
|
||||||
|
int argc = args.length;
|
||||||
|
int argn;
|
||||||
|
for ( argn = 0; argn < argc && args[argn].charAt( 0 ) == '-'; ++argn )
|
||||||
|
{
|
||||||
|
if ( args[argn].equals( "-n" ) && argn + 1 < argc )
|
||||||
|
{
|
||||||
|
++argn;
|
||||||
|
n = Integer.parseInt( args[argn] );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
if ( argn != argc )
|
||||||
|
usage();
|
||||||
|
|
||||||
|
ImageFilterPlus filter;
|
||||||
|
if ( n == -1 )
|
||||||
|
filter = new Oil( null );
|
||||||
|
else
|
||||||
|
filter = new Oil( null, n );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Oil [-n N]" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
170
src/Acme/JPM/Filters/RGBAllFilter.java
Normal file
170
src/Acme/JPM/Filters/RGBAllFilter.java
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
// RGBAllFilter - an ImageFilter that grabs the whole image
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// An ImageFilter that grabs the whole image.
|
||||||
|
// <P>
|
||||||
|
// Many image filters need to work on the whole image at once.
|
||||||
|
// This class collects up the image and hands it to a single
|
||||||
|
// routine for processing.
|
||||||
|
// <P>
|
||||||
|
// Also, because Java's image classes allow each setPixels() call to
|
||||||
|
// use a different color model, we have to convert all pixels into
|
||||||
|
// the default RGB model.
|
||||||
|
// <P>
|
||||||
|
// Here's a sample RGBAllFilter that smooths an image by averaging
|
||||||
|
// nine adjacent pixels.
|
||||||
|
// <BLOCKQUOTE><CODE><PRE>
|
||||||
|
// class SmoothFilter extends RGBAllFilter
|
||||||
|
// {
|
||||||
|
// public void filterRGBAll( int width, int height, int[][] rgbPixels )
|
||||||
|
// {
|
||||||
|
// int[][] newPixels = new int[height][width];
|
||||||
|
// for ( int row = 0; row < height; ++row )
|
||||||
|
// for ( int col = 0; col < width; ++col )
|
||||||
|
// {
|
||||||
|
// int a = 0, r = 0, g = 0, b = 0, c = 0;
|
||||||
|
// for ( int subrow = row - 1; subrow <= row + 1; ++subrow )
|
||||||
|
// if ( subrow >= 0 && subrow < height )
|
||||||
|
// for ( int subcol = col - 1; subcol <= col + 1; ++subcol )
|
||||||
|
// if ( subcol >= 0 && subcol < width )
|
||||||
|
// {
|
||||||
|
// int pixel = rgbPixels[subrow][subcol];
|
||||||
|
// a += rgbModel.getAlpha( pixel );
|
||||||
|
// r += rgbModel.getRed( pixel );
|
||||||
|
// g += rgbModel.getGreen( pixel );
|
||||||
|
// b += rgbModel.getBlue( pixel );
|
||||||
|
// ++c;
|
||||||
|
// }
|
||||||
|
// a /= c;
|
||||||
|
// r /= c;
|
||||||
|
// g /= c;
|
||||||
|
// b /= c;
|
||||||
|
// newPixels[row][col] =
|
||||||
|
// ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
|
||||||
|
// }
|
||||||
|
// setPixels( width, height, newPixels );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// </PRE></CODE></BLOCKQUOTE>
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/RGBAllFilter.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public abstract class RGBAllFilter extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
private int width = -1, height = -1;
|
||||||
|
private int[][] rgbPixels = null;
|
||||||
|
|
||||||
|
public RGBAllFilter( ImageProducer producer )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// This is the routine that subclasses must implement.
|
||||||
|
// It gets the entire image as an int[height][width] in the default
|
||||||
|
// RGB color model. It should call setPixels() with a filtered array,
|
||||||
|
// same color model.
|
||||||
|
public abstract void filterRGBAll( int width, int height, int[][] rgbPixels );
|
||||||
|
|
||||||
|
|
||||||
|
/// The version of setPixels() that gets called by the subclass.
|
||||||
|
public void setPixels( int newWidth, int newHeight, int[][] newPixels )
|
||||||
|
{
|
||||||
|
// Send it on to the consumer.
|
||||||
|
consumer.setDimensions( newWidth, newHeight );
|
||||||
|
for ( int row = 0; row < newHeight; ++row )
|
||||||
|
consumer.setPixels(
|
||||||
|
0, row, newWidth, 1, rgbModel, newPixels[row], 0, newWidth );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setColorModel( ColorModel model )
|
||||||
|
{
|
||||||
|
consumer.setColorModel( rgbModel );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
if ( width == this.width && height == this.height )
|
||||||
|
return;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
rgbPixels = new int[height][width];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
int rowOffsetIn = row * scansize + off;
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
rgbPixels[y + row][x + col] =
|
||||||
|
model.getRGB( pixels[rowOffsetIn + col] & 0xff );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
for ( int row = 0; row < h; ++row )
|
||||||
|
{
|
||||||
|
int rowOffsetIn = row * scansize + off;
|
||||||
|
if ( model == rgbModel )
|
||||||
|
System.arraycopy(
|
||||||
|
pixels, rowOffsetIn, rgbPixels[y + row], x, w );
|
||||||
|
else
|
||||||
|
for ( int col = 0; col < w; ++col )
|
||||||
|
rgbPixels[y + row][x + col] =
|
||||||
|
model.getRGB( pixels[rowOffsetIn + col] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void imageComplete( int status )
|
||||||
|
{
|
||||||
|
if ( status == ImageConsumer.IMAGEERROR ||
|
||||||
|
status == ImageConsumer.IMAGEABORTED )
|
||||||
|
{
|
||||||
|
super.imageComplete( status );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the actual work.
|
||||||
|
filterRGBAll( width, height, rgbPixels );
|
||||||
|
|
||||||
|
// And we're done.
|
||||||
|
super.imageComplete( status );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
135
src/Acme/JPM/Filters/RGBBlockFilter.java
Normal file
135
src/Acme/JPM/Filters/RGBBlockFilter.java
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// RGBBlockFilter - more efficient RGB ImageFilter
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// More efficient RGB ImageFilter.
|
||||||
|
// <P>
|
||||||
|
// Similar in concept to java.awt.image.RGBImageFilter, but designed
|
||||||
|
// to run more efficiently when filtering large images.
|
||||||
|
// <P>
|
||||||
|
// As with RGBImageFilter, you only have to implement a single routine
|
||||||
|
// to use the filter. However, RGBImageFilter's routine filters a single
|
||||||
|
// pixel at a time. This means a lot of routine-calling overhead.
|
||||||
|
// RGBBlockFilter filters a block at a time.
|
||||||
|
// <P>
|
||||||
|
// Here's a sample RGBBlockFilter that makes an image translucent
|
||||||
|
// by setting all the alpha values to 0x80:
|
||||||
|
// <BLOCKQUOTE><CODE><PRE>
|
||||||
|
// class TranslucentFilter extends RGBBlockFilter
|
||||||
|
// {
|
||||||
|
// public int[] filterRGBBlock(
|
||||||
|
// int x, int y, int width, int height, int[][] rgbPixels )
|
||||||
|
// {
|
||||||
|
// for ( int row = 0; row < height; ++row )
|
||||||
|
// for ( int col = 0; col < width; ++col )
|
||||||
|
// rgbPixels[row][col] =
|
||||||
|
// ( rgbPixels[row][col] & 0x00ffffff ) | 0x80000000;
|
||||||
|
// return rgbPixels;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// </PRE></CODE></BLOCKQUOTE>
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/RGBBlockFilter.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public abstract class RGBBlockFilter extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
public RGBBlockFilter( ImageProducer producer )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// This is the routine that subclasses must implement.
|
||||||
|
// It gets a block of the image as an int[height][width] in the default
|
||||||
|
// RGB color model. It should return a filtered array, same size
|
||||||
|
// and same model.
|
||||||
|
public abstract int[][] filterRGBBlock(
|
||||||
|
int x, int y, int width, int height, int[][] rgbPixels );
|
||||||
|
|
||||||
|
|
||||||
|
public void setColorModel( ColorModel model )
|
||||||
|
{
|
||||||
|
consumer.setColorModel( rgbModel );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Byte version of setPixels reformats the pixels to RGB and the
|
||||||
|
// array to 2 dimensions.
|
||||||
|
public void setPixels(
|
||||||
|
int x, int y, int width, int height, ColorModel model,
|
||||||
|
byte[] pixels, int offset, int scansize )
|
||||||
|
{
|
||||||
|
int[][] rgbPixels = new int[height][width];
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
{
|
||||||
|
int rowOffsetIn = offset + row * scansize;
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
rgbPixels[row][col] =
|
||||||
|
model.getRGB( pixels[rowOffsetIn + col] & 0xff );
|
||||||
|
}
|
||||||
|
setPixels( x, y, width, height, rgbPixels );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Int version of setPixels reformats the array to 2 dimensions.
|
||||||
|
public void setPixels(
|
||||||
|
int x, int y, int width, int height, ColorModel model,
|
||||||
|
int[] pixels, int offset, int scansize )
|
||||||
|
{
|
||||||
|
int[][] rgbPixels = new int[height][width];
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
{
|
||||||
|
int rowOffsetIn = offset + row * scansize;
|
||||||
|
int rowOffsetOut = row * width;
|
||||||
|
// Convert color models if necessary.
|
||||||
|
if ( model == rgbModel )
|
||||||
|
System.arraycopy(
|
||||||
|
pixels, rowOffsetIn, rgbPixels[row], 0, width );
|
||||||
|
else
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
rgbPixels[row][col] =
|
||||||
|
model.getRGB( pixels[rowOffsetIn + col] );
|
||||||
|
}
|
||||||
|
setPixels( x, y, width, height, rgbPixels );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the filter routine, and send the results to the consumer.
|
||||||
|
private void setPixels( int x, int y, int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
int[][] newPixels = filterRGBBlock( x, y, width, height, rgbPixels );
|
||||||
|
|
||||||
|
// And send it on to the consumer.
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
consumer.setPixels(
|
||||||
|
x, y + row, width, 1, rgbModel, newPixels[row], 0, width );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
src/Acme/JPM/Filters/Rotate.java
Normal file
81
src/Acme/JPM/Filters/Rotate.java
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// Rotate - rotate an image by some angle
|
||||||
|
//
|
||||||
|
// Copyright (C) 1997 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Rotate an image by some angle.
|
||||||
|
// <P>
|
||||||
|
// Rotates an image by the specified angle.
|
||||||
|
// The angle is in degrees measured counter-clockwise.
|
||||||
|
// It can be negative, but it should be between -90 and 90
|
||||||
|
// or the resulting image will be unreasonably large.
|
||||||
|
// Staying between -45 and 45 is best.
|
||||||
|
// <P>
|
||||||
|
// The rotation algorithm is Alan Paeth's three-shear method, described
|
||||||
|
// in "A Fast Algorithm for General Raster Rotation", Graphics Interface
|
||||||
|
// '86, pp. 77-81.
|
||||||
|
// <P>
|
||||||
|
// This filter is slow.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Rotate.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Rotate extends CompositeFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
private double angle;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Rotate( ImageProducer producer, double angle )
|
||||||
|
{
|
||||||
|
super( producer, new Shear( null, angle ), new Flip( null, Flip.FLIP_XY ) );
|
||||||
|
this.angle = angle * Math.PI / 180.0;
|
||||||
|
double xshearfac = Math.tan( angle / 2.0 );
|
||||||
|
double yshearfac = Math.sin( angle );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 1 )
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter = new Rotate( null, Integer.parseInt( args[0] ) );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Rotate <angle>" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
159
src/Acme/JPM/Filters/ScaleCopy.java
Normal file
159
src/Acme/JPM/Filters/ScaleCopy.java
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
// ScaleCopy - an ImageFilter that scales by pixel copying
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// An ImageFilter that scales by pixel copying.
|
||||||
|
// <P>
|
||||||
|
// Scales an image by copying pixels.
|
||||||
|
// If the image is being enlarged, pixels get replicated;
|
||||||
|
// if the image is being shrunk, pixels get dropped.
|
||||||
|
// The output uses the same color model as the input.
|
||||||
|
// For enlarging, this filter is slightly slower than Enlarge due
|
||||||
|
// to the floating-point arithmetic;
|
||||||
|
// for shrinking, it's much faster than Shrink, but
|
||||||
|
// the results aren't as nice.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/ScaleCopy.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see Enlarge
|
||||||
|
// @see Shrink
|
||||||
|
|
||||||
|
public class ScaleCopy extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
private double xScale, yScale;
|
||||||
|
private int newWidth, newHeight;
|
||||||
|
|
||||||
|
/// Constructor, same X and Y scale factor.
|
||||||
|
public ScaleCopy( ImageProducer producer, double scale )
|
||||||
|
{
|
||||||
|
this( producer, scale, scale );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor, different X and Y scale factors.
|
||||||
|
public ScaleCopy( ImageProducer producer, double xScale, double yScale )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.xScale = xScale;
|
||||||
|
this.yScale = yScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
newWidth = (int) ( width * xScale );
|
||||||
|
newHeight = (int) ( height * yScale );
|
||||||
|
consumer.setDimensions( newWidth, newHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
int newX = Math.min( (int) ( x * xScale ), newWidth - 1 );
|
||||||
|
int newY = Math.min( (int) ( y * yScale ), newHeight - 1 );
|
||||||
|
int newW = Math.max( (int) ( w * xScale ), 1 );
|
||||||
|
if ( newX + newW > newWidth )
|
||||||
|
newW = newWidth - newX;
|
||||||
|
int newH = Math.max( (int) ( h * yScale ), 1 );
|
||||||
|
if ( newY + newH > newHeight )
|
||||||
|
newH = newHeight - newY;
|
||||||
|
byte[] newPixels = new byte[newW * newH];
|
||||||
|
for ( int newRow = 0; newRow < newH; ++newRow )
|
||||||
|
{
|
||||||
|
int row = (int) ( newRow / yScale );
|
||||||
|
if ( row >= h )
|
||||||
|
continue;
|
||||||
|
for ( int newCol = 0; newCol < newW; ++newCol )
|
||||||
|
{
|
||||||
|
int col = (int) ( newCol / xScale );
|
||||||
|
if ( col >= w )
|
||||||
|
continue;
|
||||||
|
newPixels[newRow * newW + newCol] =
|
||||||
|
pixels[row * scansize + off + col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumer.setPixels( newX, newY, newW, newH, model, newPixels, 0, newW );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
int newX = Math.min( (int) ( x * xScale ), newWidth - 1 );
|
||||||
|
int newY = Math.min( (int) ( y * yScale ), newHeight - 1 );
|
||||||
|
int newW = Math.max( (int) ( w * xScale ), 1 );
|
||||||
|
if ( newX + newW > newWidth )
|
||||||
|
newW = newWidth - newX;
|
||||||
|
int newH = Math.max( (int) ( h * yScale ), 1 );
|
||||||
|
if ( newY + newH > newHeight )
|
||||||
|
newH = newHeight - newY;
|
||||||
|
int[] newPixels = new int[newW * newH];
|
||||||
|
for ( int newRow = 0; newRow < newH; ++newRow )
|
||||||
|
{
|
||||||
|
int row = (int) ( newRow / yScale );
|
||||||
|
if ( row >= h )
|
||||||
|
continue;
|
||||||
|
for ( int newCol = 0; newCol < newW; ++newCol )
|
||||||
|
{
|
||||||
|
int col = (int) ( newCol / xScale );
|
||||||
|
if ( col >= w )
|
||||||
|
continue;
|
||||||
|
newPixels[newRow * newW + newCol] =
|
||||||
|
pixels[row * scansize + off + col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumer.setPixels( newX, newY, newW, newH, model, newPixels, 0, newW );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
ImageFilterPlus filter = null;
|
||||||
|
if ( args.length == 1 )
|
||||||
|
filter = new ScaleCopy( null,
|
||||||
|
Double.valueOf( args[0] ).doubleValue() );
|
||||||
|
else if ( args.length == 2 )
|
||||||
|
filter = new ScaleCopy( null,
|
||||||
|
Double.valueOf( args[0] ).doubleValue(),
|
||||||
|
Double.valueOf( args[1] ).doubleValue() );
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: ScaleCopy scale" );
|
||||||
|
System.err.println( "or: ScaleCopy xScale yScale" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
144
src/Acme/JPM/Filters/Shear.java
Normal file
144
src/Acme/JPM/Filters/Shear.java
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// Shear - shear an image by some angle
|
||||||
|
//
|
||||||
|
// Copyright (C) 1997 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Shear an image by some angle.
|
||||||
|
// <P>
|
||||||
|
// Shears an image by the specified angle.
|
||||||
|
// The angle is in degrees (floating point), and measures this:
|
||||||
|
// <BLOCKQUOTE><PRE><CODE>
|
||||||
|
// +-------+ +-------+
|
||||||
|
// | | |\ \
|
||||||
|
// | OLD | | \ NEW \
|
||||||
|
// | | |an\ \
|
||||||
|
// +-------+ |gle+-------+
|
||||||
|
// </CODE></PRE></BLOCKQUOTE>
|
||||||
|
// If the angle is negative, it shears the other way:
|
||||||
|
// <BLOCKQUOTE><PRE><CODE>
|
||||||
|
// +-------+ |-an+-------+
|
||||||
|
// | | |gl/ /
|
||||||
|
// | OLD | |e/ NEW /
|
||||||
|
// | | |/ /
|
||||||
|
// +-------+ +-------+
|
||||||
|
// </CODE></PRE></BLOCKQUOTE>
|
||||||
|
// The angle should not get too close to 90 or -90, or the resulting
|
||||||
|
// image will be unreasonably wide. Staying between -45 and 45 is best.
|
||||||
|
// <P>
|
||||||
|
// The shearing is implemented by looping over the source pixels and
|
||||||
|
// distributing fractions to each of the destination pixels.
|
||||||
|
// This has an "anti-aliasing" effect - it avoids jagged edges and similar
|
||||||
|
// artifacts.
|
||||||
|
// <P>
|
||||||
|
// This filter is fast.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Shear.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Shear extends RGBAllFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
private double angle;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Shear( ImageProducer producer, double angle )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.angle = angle * Math.PI / 180.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void filterRGBAll( int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
double shearfac = Math.tan( angle );
|
||||||
|
if ( shearfac < 0.0 )
|
||||||
|
shearfac = -shearfac;
|
||||||
|
int newWidth = (int) ( height * shearfac + width + 0.999999 );
|
||||||
|
int[][] newPixels = new int[height][newWidth];
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
{
|
||||||
|
double new0;
|
||||||
|
if ( angle > 0.0 )
|
||||||
|
new0 = row * shearfac;
|
||||||
|
else
|
||||||
|
new0 = ( height - row ) * shearfac;
|
||||||
|
int intnew0 = (int) new0;
|
||||||
|
double fracnew0 = new0 - intnew0;
|
||||||
|
double omfracnew0 = 1.0 - fracnew0;
|
||||||
|
|
||||||
|
for ( int col = 0; col < newWidth; ++col )
|
||||||
|
newPixels[row][col] = 0x00000000;
|
||||||
|
|
||||||
|
int preva = 0;
|
||||||
|
int prevr = ( rgbPixels[row][0] >> 16 ) & 0xff;
|
||||||
|
int prevg = ( rgbPixels[row][0] >> 8 ) & 0xff;
|
||||||
|
int prevb = rgbPixels[row][0] & 0xff;
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
int rgb = rgbPixels[row][col];
|
||||||
|
int a = ( rgb >> 24 ) & 0xff;
|
||||||
|
int r = ( rgb >> 16 ) & 0xff;
|
||||||
|
int g = ( rgb >> 8 ) & 0xff;
|
||||||
|
int b = rgb & 0xff;
|
||||||
|
newPixels[row][intnew0 + col] =
|
||||||
|
( (int) ( fracnew0 * preva + omfracnew0 * a ) << 24 ) |
|
||||||
|
( (int) ( fracnew0 * prevr + omfracnew0 * r ) << 16 ) |
|
||||||
|
( (int) ( fracnew0 * prevg + omfracnew0 * g ) << 8 ) |
|
||||||
|
( (int) ( fracnew0 * prevb + omfracnew0 * b ) );
|
||||||
|
preva = a;
|
||||||
|
prevr = r;
|
||||||
|
prevg = g;
|
||||||
|
prevb = b;
|
||||||
|
}
|
||||||
|
newPixels[row][intnew0 + width] =
|
||||||
|
( (int) ( fracnew0 * preva ) << 24 ) |
|
||||||
|
( prevr << 16 ) | ( prevg << 8 ) | prevb;
|
||||||
|
}
|
||||||
|
setPixels( newWidth, height, newPixels );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 1 )
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter = new Shear( null, Integer.parseInt( args[0] ) );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Shear <angle>" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
116
src/Acme/JPM/Filters/Shrink.java
Normal file
116
src/Acme/JPM/Filters/Shrink.java
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// Shrink - an ImageFilter that shrinks by pixel averaging
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// An ImageFilter that shrinks by pixel averaging.
|
||||||
|
// <P>
|
||||||
|
// Shrinks an image an integral factor by averaging pixels.
|
||||||
|
// Because the resulting pixels might not fit into the input's
|
||||||
|
// color model, the output is always in the default RGB color model.
|
||||||
|
// This filter is somewhat slow.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Shrink.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see Enlarge
|
||||||
|
// @see ScaleCopy
|
||||||
|
|
||||||
|
public class Shrink extends RGBAllFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
private int divisor;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Shrink( ImageProducer producer, int divisor )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.divisor = divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void filterRGBAll( int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
int divisor2 = divisor * divisor;
|
||||||
|
int newWidth = Math.max( width / divisor, 1 );
|
||||||
|
int newHeight = Math.max( height / divisor, 1 );
|
||||||
|
int[][] newPixels = new int[newHeight][newWidth];
|
||||||
|
for ( int newRow = 0; newRow < newHeight; ++newRow )
|
||||||
|
{
|
||||||
|
for ( int newCol = 0; newCol < newWidth; ++newCol )
|
||||||
|
{
|
||||||
|
int a = 0, r = 0, g = 0, b = 0;
|
||||||
|
for ( int i = 0; i < divisor; ++i )
|
||||||
|
{
|
||||||
|
int row = newRow * divisor + i;
|
||||||
|
if ( row >= height )
|
||||||
|
continue;
|
||||||
|
for ( int j = 0; j < divisor; ++j )
|
||||||
|
{
|
||||||
|
int col = newCol * divisor + j;
|
||||||
|
if ( col >= width )
|
||||||
|
continue;
|
||||||
|
int rgb = rgbPixels[row][col];
|
||||||
|
a += ( rgb >> 24 ) & 0xff;
|
||||||
|
r += ( rgb >> 16 ) & 0xff;
|
||||||
|
g += ( rgb >> 8 ) & 0xff;
|
||||||
|
b += rgb & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a /= divisor2;
|
||||||
|
r /= divisor2;
|
||||||
|
g /= divisor2;
|
||||||
|
b /= divisor2;
|
||||||
|
newPixels[newRow][newCol] =
|
||||||
|
( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPixels( newWidth, newHeight, newPixels );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 1 )
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter = new Enlarge(
|
||||||
|
null, Integer.parseInt( args[0] ) );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Shrink <divisor>" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
src/Acme/JPM/Filters/Smooth.java
Normal file
123
src/Acme/JPM/Filters/Smooth.java
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// Smooth - smoothing filter
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// Smoothing filter.
|
||||||
|
// <P>
|
||||||
|
// Smooths an image by averaging adjacent pixels.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Smooth.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Smooth extends RGBAllFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
private int n;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Smooth( ImageProducer producer, int n )
|
||||||
|
{
|
||||||
|
super( producer );
|
||||||
|
this.n = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor, default value.
|
||||||
|
public Smooth( ImageProducer producer )
|
||||||
|
{
|
||||||
|
this( producer, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void filterRGBAll( int width, int height, int[][] rgbPixels )
|
||||||
|
{
|
||||||
|
int[][] newPixels = new int[height][width];
|
||||||
|
for ( int row = 0; row < height; ++row )
|
||||||
|
for ( int col = 0; col < width; ++col )
|
||||||
|
{
|
||||||
|
int a = 0, r = 0, g = 0, b = 0, c = 0;
|
||||||
|
for ( int subrow = row - n; subrow <= row + n; ++subrow )
|
||||||
|
if ( subrow >= 0 && subrow < height )
|
||||||
|
for ( int subcol = col - n; subcol <= col + n; ++subcol )
|
||||||
|
if ( subcol >= 0 && subcol < width )
|
||||||
|
{
|
||||||
|
int rgb = rgbPixels[subrow][subcol];
|
||||||
|
a += ( rgb >> 24 ) & 0xff;
|
||||||
|
r += ( rgb >> 16 ) & 0xff;
|
||||||
|
g += ( rgb >> 8 ) & 0xff;
|
||||||
|
b += rgb & 0xff;
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
a /= c;
|
||||||
|
r /= c;
|
||||||
|
g /= c;
|
||||||
|
b /= c;
|
||||||
|
newPixels[row][col] =
|
||||||
|
( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
|
||||||
|
}
|
||||||
|
setPixels( width, height, newPixels );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
int n = -1;
|
||||||
|
int argc = args.length;
|
||||||
|
int argn;
|
||||||
|
for ( argn = 0; argn < argc && args[argn].charAt( 0 ) == '-'; ++argn )
|
||||||
|
{
|
||||||
|
if ( args[argn].equals( "-n" ) && argn + 1 < argc )
|
||||||
|
{
|
||||||
|
++argn;
|
||||||
|
n = Integer.parseInt( args[argn] );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
if ( argn != argc )
|
||||||
|
usage();
|
||||||
|
|
||||||
|
ImageFilterPlus filter;
|
||||||
|
if ( n == -1 )
|
||||||
|
filter = new Smooth( null );
|
||||||
|
else
|
||||||
|
filter = new Smooth( null, n );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Smooth [-n N]" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
126
src/Acme/JPM/Filters/Tile.java
Normal file
126
src/Acme/JPM/Filters/Tile.java
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
// Tile - an ImageFilter that replicates an image in a tiled pattern
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM.Filters;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/// An ImageFilter that replicates an image in a tiled pattern.
|
||||||
|
// <P>
|
||||||
|
// Tiles the image onto an output image of a specified size.
|
||||||
|
// The output uses the same color model as the input.
|
||||||
|
// This filter is very fast.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/Filters/Tile.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class Tile extends ImageFilterPlus
|
||||||
|
{
|
||||||
|
|
||||||
|
private int width, height;
|
||||||
|
private int newWidth, newHeight;
|
||||||
|
private int nWide, nHigh;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public Tile( ImageProducer producer, int newWidth, int newHeight )
|
||||||
|
{
|
||||||
|
super( producer, true );
|
||||||
|
this.newWidth = newWidth;
|
||||||
|
this.newHeight = newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setDimensions( int width, int height )
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
consumer.setDimensions( newWidth, newHeight );
|
||||||
|
nWide = ( newWidth + width - 1 ) / width;
|
||||||
|
nHigh = ( newHeight + height - 1 ) / height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
for ( int r = 0; r < nHigh; ++r )
|
||||||
|
{
|
||||||
|
int ty = r * height + y;
|
||||||
|
int th = h;
|
||||||
|
if ( ty + th > newHeight )
|
||||||
|
th = newHeight - ty;
|
||||||
|
for ( int c = 0; c < nWide; ++c )
|
||||||
|
{
|
||||||
|
int tx = c * width + x;
|
||||||
|
int tw = w;
|
||||||
|
if ( tx + tw > newWidth )
|
||||||
|
tw = newWidth - tx;
|
||||||
|
consumer.setPixels(
|
||||||
|
tx, ty, tw, th, model, pixels, off, scansize );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixels( int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize )
|
||||||
|
{
|
||||||
|
for ( int r = 0; r < nHigh; ++r )
|
||||||
|
{
|
||||||
|
int ty = r * height + y;
|
||||||
|
int th = h;
|
||||||
|
if ( ty + th > newHeight )
|
||||||
|
th = newHeight - ty;
|
||||||
|
for ( int c = 0; c < nWide; ++c )
|
||||||
|
{
|
||||||
|
int tx = c * width + x;
|
||||||
|
int tw = w;
|
||||||
|
if ( tx + tw > newWidth )
|
||||||
|
tw = newWidth - tx;
|
||||||
|
consumer.setPixels(
|
||||||
|
tx, ty, tw, th, model, pixels, off, scansize );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main routine for command-line interface.
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 2 )
|
||||||
|
usage();
|
||||||
|
ImageFilterPlus filter =
|
||||||
|
new Tile( null,
|
||||||
|
Integer.parseInt( args[0] ), Integer.parseInt( args[1] ) );
|
||||||
|
System.exit(
|
||||||
|
ImageFilterPlus.filterStream( System.in, System.out, filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage()
|
||||||
|
{
|
||||||
|
System.err.println( "usage: Tile <width> <height>" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
312
src/Acme/JPM/JPMTest.java
Normal file
312
src/Acme/JPM/JPMTest.java
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
// JPMTest - test program for the ACME Java pixmap utilities
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.*;
|
||||||
|
import Acme.JPM.Filters.*;
|
||||||
|
import Acme.JPM.Encoders.*;
|
||||||
|
import Acme.Widgets.*;
|
||||||
|
|
||||||
|
/// Test program for the ACME Java pixmap utilities.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/JPMTest.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class JPMTest extends Frame
|
||||||
|
{
|
||||||
|
|
||||||
|
private static JPMTest jpmTest;
|
||||||
|
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 1 )
|
||||||
|
{
|
||||||
|
System.err.println( "usage: JPMTest [filename]" );
|
||||||
|
System.exit( 1 );
|
||||||
|
}
|
||||||
|
jpmTest = new JPMTest( args[0] );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ImageLabel origImageLabel, filt1ImageLabel, filt2ImageLabel;
|
||||||
|
private Image origImage, filt1Image, filt2Image;
|
||||||
|
private Choice filt1Choice, filt2Choice, outputChoice;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public JPMTest( String fileName )
|
||||||
|
{
|
||||||
|
|
||||||
|
setTitle( "JPMTest" );
|
||||||
|
|
||||||
|
GridBagLayout gb = new GridBagLayout();
|
||||||
|
setLayout( gb );
|
||||||
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
gbc.insets = new Insets( 5, 5, 5, 5 );
|
||||||
|
|
||||||
|
Panel panel = new Panel();
|
||||||
|
GridBagLayout gb2 = new GridBagLayout();
|
||||||
|
panel.setLayout( gb2 );
|
||||||
|
GridBagConstraints gbc2 = new GridBagConstraints();
|
||||||
|
gbc2.insets = new Insets( 5, 5, 5, 5 );
|
||||||
|
|
||||||
|
Label label = new Label( fileName );
|
||||||
|
gbc2.gridwidth = 1;
|
||||||
|
gb2.setConstraints( label, gbc2 );
|
||||||
|
panel.add( label );
|
||||||
|
|
||||||
|
filt1Choice = new Choice();
|
||||||
|
addChoiceItems( filt1Choice );
|
||||||
|
gbc2.gridwidth = 1;
|
||||||
|
gb2.setConstraints( filt1Choice, gbc2 );
|
||||||
|
panel.add( filt1Choice );
|
||||||
|
|
||||||
|
filt2Choice = new Choice();
|
||||||
|
addChoiceItems( filt2Choice );
|
||||||
|
gbc2.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
gb2.setConstraints( filt2Choice, gbc2 );
|
||||||
|
panel.add( filt2Choice );
|
||||||
|
|
||||||
|
origImageLabel = new ImageLabel( Acme.GuiUtils.brokenIcon( this ) );
|
||||||
|
gbc2.gridwidth = 1;
|
||||||
|
gb2.setConstraints( origImageLabel, gbc2 );
|
||||||
|
panel.add( origImageLabel );
|
||||||
|
|
||||||
|
filt1ImageLabel = new ImageLabel( Acme.GuiUtils.brokenIcon( this ) );
|
||||||
|
gbc2.gridwidth = 1;
|
||||||
|
gb2.setConstraints( filt1ImageLabel, gbc2 );
|
||||||
|
panel.add( filt1ImageLabel );
|
||||||
|
|
||||||
|
filt2ImageLabel = new ImageLabel( Acme.GuiUtils.brokenIcon( this ) );
|
||||||
|
gbc2.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
gb2.setConstraints( filt2ImageLabel, gbc2 );
|
||||||
|
panel.add( filt2ImageLabel );
|
||||||
|
|
||||||
|
gb.setConstraints( panel, gbc );
|
||||||
|
add( panel );
|
||||||
|
|
||||||
|
panel = new Panel();
|
||||||
|
gb2 = new GridBagLayout();
|
||||||
|
panel.setLayout( gb2 );
|
||||||
|
|
||||||
|
outputChoice = new Choice();
|
||||||
|
outputChoice.addItem( "GIF" );
|
||||||
|
outputChoice.addItem( "PPM" );
|
||||||
|
gbc2.gridwidth = 1;
|
||||||
|
gb2.setConstraints( outputChoice, gbc2 );
|
||||||
|
panel.add( outputChoice );
|
||||||
|
|
||||||
|
Button button = new Button( "Write" );
|
||||||
|
gbc2.gridwidth = 1;
|
||||||
|
gb2.setConstraints( button, gbc2 );
|
||||||
|
panel.add( button );
|
||||||
|
|
||||||
|
button = new Button( "Quit" );
|
||||||
|
gbc2.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
gb2.setConstraints( button, gbc2 );
|
||||||
|
panel.add( button );
|
||||||
|
|
||||||
|
gb.setConstraints( panel, gbc );
|
||||||
|
add( panel );
|
||||||
|
|
||||||
|
pack();
|
||||||
|
validate();
|
||||||
|
show();
|
||||||
|
|
||||||
|
origImage = getToolkit().getImage( fileName );
|
||||||
|
origImageLabel.setImage( origImage );
|
||||||
|
filt1Choice.select( NONE );
|
||||||
|
filt2Choice.select( NONE );
|
||||||
|
filter1();
|
||||||
|
filter2();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addChoiceItems( Choice choice )
|
||||||
|
{
|
||||||
|
// These items must match the order of the enumeration.
|
||||||
|
choice.addItem( "NONE" );
|
||||||
|
choice.addItem( "Shrink 2" );
|
||||||
|
choice.addItem( "Enlarge 2" );
|
||||||
|
choice.addItem( "ScaleCopy 0.51" );
|
||||||
|
choice.addItem( "ScaleCopy 1.99" );
|
||||||
|
choice.addItem( "Oil" );
|
||||||
|
choice.addItem( "Smooth 2" );
|
||||||
|
choice.addItem( "EdgeDetect" );
|
||||||
|
choice.addItem( "Gamma 2" );
|
||||||
|
choice.addItem( "Tile 400" );
|
||||||
|
choice.addItem( "Flip -lr" );
|
||||||
|
choice.addItem( "Flip -tb" );
|
||||||
|
choice.addItem( "Flip -cw" );
|
||||||
|
choice.addItem( "Invert" );
|
||||||
|
choice.addItem( "Margin" );
|
||||||
|
choice.addItem( "Shear 30" );
|
||||||
|
choice.addItem( "Rotate 30" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Event handler.
|
||||||
|
public boolean handleEvent( Event evt )
|
||||||
|
{
|
||||||
|
switch ( evt.id )
|
||||||
|
{
|
||||||
|
case Event.ACTION_EVENT:
|
||||||
|
if ( evt.arg.equals( "Quit" ) )
|
||||||
|
System.exit( 0 );
|
||||||
|
else if ( evt.arg.equals( "Write" ) )
|
||||||
|
write();
|
||||||
|
else if ( evt.target == filt1Choice )
|
||||||
|
{
|
||||||
|
filter1();
|
||||||
|
filter2();
|
||||||
|
}
|
||||||
|
else if ( evt.target == filt2Choice )
|
||||||
|
filter2();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return super.handleEvent( evt );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This must match the order of the choice items.
|
||||||
|
private static final int GIF = 0;
|
||||||
|
private static final int PPM = 1;
|
||||||
|
|
||||||
|
private void write()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImageEncoder encoder = null;
|
||||||
|
switch ( outputChoice.getSelectedIndex() )
|
||||||
|
{
|
||||||
|
case GIF:
|
||||||
|
encoder = new GifEncoder( filt2Image.getSource(), System.out );
|
||||||
|
break;
|
||||||
|
case PPM:
|
||||||
|
encoder = new PpmEncoder( filt2Image.getSource(), System.out );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
encoder.encode();
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
System.err.println( e.toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This must match the order of the choice items.
|
||||||
|
private static final int NONE = 0;
|
||||||
|
private static final int SHRINK2 = 1;
|
||||||
|
private static final int ENLARGE2 = 2;
|
||||||
|
private static final int SCALE051 = 3;
|
||||||
|
private static final int SCALE199 = 4;
|
||||||
|
private static final int OIL = 5;
|
||||||
|
private static final int SMOOTH2 = 6;
|
||||||
|
private static final int EDGEDETECT = 7;
|
||||||
|
private static final int GAMMA2 = 8;
|
||||||
|
private static final int TILE400 = 9;
|
||||||
|
private static final int FLIPLR = 10;
|
||||||
|
private static final int FLIPTB = 11;
|
||||||
|
private static final int FLIPCW = 12;
|
||||||
|
private static final int INVERT = 13;
|
||||||
|
private static final int MARGIN = 14;
|
||||||
|
private static final int SHEAR30 = 15;
|
||||||
|
private static final int ROTATE30 = 16;
|
||||||
|
|
||||||
|
private void filter1()
|
||||||
|
{
|
||||||
|
filt1Image = filterImage( filt1Choice.getSelectedIndex(), origImage );
|
||||||
|
filt1ImageLabel.setImage( filt1Image );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filter2()
|
||||||
|
{
|
||||||
|
filt2Image = filterImage( filt2Choice.getSelectedIndex(), filt1Image );
|
||||||
|
filt2ImageLabel.setImage( filt2Image );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image filterImage( int which, Image image )
|
||||||
|
{
|
||||||
|
switch ( which )
|
||||||
|
{
|
||||||
|
case NONE:
|
||||||
|
return image;
|
||||||
|
case SHRINK2:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Shrink( image.getSource(), 2 ) );
|
||||||
|
case ENLARGE2:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Enlarge( image.getSource(), 2 ) );
|
||||||
|
case SCALE051:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new ScaleCopy( image.getSource(), 0.51 ) );
|
||||||
|
case SCALE199:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new ScaleCopy( image.getSource(), 1.99 ) );
|
||||||
|
case OIL:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Oil( image.getSource() ) );
|
||||||
|
case SMOOTH2:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Smooth( image.getSource(), 2 ) );
|
||||||
|
case EDGEDETECT:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new EdgeDetect( image.getSource() ) );
|
||||||
|
case GAMMA2:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Gamma( image.getSource(), 2.0 ) );
|
||||||
|
case TILE400:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Tile( image.getSource(), 400, 400 ) );
|
||||||
|
case FLIPLR:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Flip( image.getSource(), Flip.FLIP_LR ) );
|
||||||
|
case FLIPTB:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Flip( image.getSource(), Flip.FLIP_TB ) );
|
||||||
|
case FLIPCW:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Flip( image.getSource(), Flip.FLIP_CW ) );
|
||||||
|
case INVERT:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Invert( image.getSource() ) );
|
||||||
|
case MARGIN:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Margin( image.getSource(), Color.black, 10 ) );
|
||||||
|
case SHEAR30:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Shear( image.getSource(), 30.0 ) );
|
||||||
|
case ROTATE30:
|
||||||
|
return JPMUtils.filterImage(
|
||||||
|
this, new Rotate( image.getSource(), 30.0 ) );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
src/Acme/JPM/JPMUtils.java
Normal file
50
src/Acme/JPM/JPMUtils.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// JPMUtils - static utility routines for the JPM packages
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme.JPM;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import Acme.JPM.Filters.*;
|
||||||
|
|
||||||
|
/// Static utility routines for the JPM packages.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/JPM/JPMUtils.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
|
||||||
|
public class JPMUtils
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Filter one image into another.
|
||||||
|
public static Image filterImage( Component comp, ImageFilterPlus filter )
|
||||||
|
{
|
||||||
|
return comp.createImage(
|
||||||
|
new FilteredImageSource( filter.getSource(), filter ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1053
src/Acme/Utils.java
Normal file
1053
src/Acme/Utils.java
Normal file
File diff suppressed because it is too large
Load diff
147
src/Acme/WildcardDictionary.java
Normal file
147
src/Acme/WildcardDictionary.java
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// WildcardDictionary - a dictionary with wildcard lookups
|
||||||
|
//
|
||||||
|
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
// SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
// fine Java utilities: http://www.acme.com/java/
|
||||||
|
|
||||||
|
package Acme;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/// A dictionary with wildcard lookups.
|
||||||
|
// <P>
|
||||||
|
// The keys in this dictionary are wildcard patterns. When you do a get(),
|
||||||
|
// the string you pass in is matched against all the patterns, and the
|
||||||
|
// first match is returned.
|
||||||
|
// <P>
|
||||||
|
// The wildcard matcher is fairly simple, it implements * meaning any
|
||||||
|
// string, ? meaning any single character, and | separating multiple
|
||||||
|
// patterns. All other characters must match literally.
|
||||||
|
// <P>
|
||||||
|
// <A HREF="/resources/classes/Acme/WildcardDictionary.java">Fetch the software.</A><BR>
|
||||||
|
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||||
|
// <P>
|
||||||
|
// @see Acme.Utils#match
|
||||||
|
|
||||||
|
public class WildcardDictionary extends Dictionary
|
||||||
|
{
|
||||||
|
|
||||||
|
private Vector keys;
|
||||||
|
private Vector elements;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
public WildcardDictionary()
|
||||||
|
{
|
||||||
|
keys = new Vector();
|
||||||
|
elements = new Vector();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of elements contained within the dictionary.
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return elements.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the dictionary contains no elements.
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an enumeration of the dictionary's keys.
|
||||||
|
public Enumeration keys()
|
||||||
|
{
|
||||||
|
return keys.elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an enumeration of the elements. Use the Enumeration methods
|
||||||
|
// on the returned object to fetch the elements sequentially.
|
||||||
|
public Enumeration elements()
|
||||||
|
{
|
||||||
|
return elements.elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the object associated with the specified key in the dictionary.
|
||||||
|
// The key is assumed to be a String, which is matched against
|
||||||
|
// the wildcard-pattern keys in the dictionary.
|
||||||
|
// @param key the string to match
|
||||||
|
// @returns the element for the key, or null if there's no match
|
||||||
|
// @see Acme.Utils#match
|
||||||
|
public synchronized Object get( Object key )
|
||||||
|
{
|
||||||
|
String sKey = (String) key;
|
||||||
|
for ( int i = 0; i < keys.size(); ++i )
|
||||||
|
{
|
||||||
|
String thisKey = (String) keys.elementAt( i );
|
||||||
|
if ( Acme.Utils.match( thisKey, sKey ) )
|
||||||
|
return elements.elementAt( i );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts the specified element into the Dictionary, using the specified
|
||||||
|
// key. The element may be retrieved by doing a get() with the same
|
||||||
|
// key. The key and the element cannot be null.
|
||||||
|
// @param key the specified wildcard-pattern key
|
||||||
|
// @param value the specified element
|
||||||
|
// @return the old value of the key, or null if it did not have one.
|
||||||
|
// @exception NullPointerException If the value of the specified
|
||||||
|
// element is null.
|
||||||
|
public synchronized Object put( Object key, Object element )
|
||||||
|
{
|
||||||
|
int i = keys.indexOf( key );
|
||||||
|
if ( i != -1 )
|
||||||
|
{
|
||||||
|
Object oldElement = elements.elementAt( i );
|
||||||
|
elements.setElementAt( element, i );
|
||||||
|
return oldElement;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keys.addElement( key );
|
||||||
|
elements.addElement( element );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the element corresponding to the key. Does nothing if the
|
||||||
|
// key is not present.
|
||||||
|
// @param key the key that needs to be removed
|
||||||
|
// @return the value of key, or null if the key was not found.
|
||||||
|
public synchronized Object remove( Object key )
|
||||||
|
{
|
||||||
|
int i = keys.indexOf( key );
|
||||||
|
if ( i != -1 )
|
||||||
|
{
|
||||||
|
Object oldElement = elements.elementAt( i );
|
||||||
|
keys.removeElementAt( i );
|
||||||
|
elements.removeElementAt( i );
|
||||||
|
return oldElement;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue