// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/rpf/RpfColortable.java,v $
// $RCSfile: RpfColortable.java,v $
// $Revision: 1.5 $
// $Date: 2007/02/26 17:34:03 $
// $Author: dietrick $
//
// **********************************************************************
/*
* The meat of this code is based on source code provided by
* The MITRE Corporation, through the browse application source
* code. Many thanks to Nancy Markuson who provided BBN with the
* software, and Theron Tock, who wrote the software, and to
* Daniel Scholten, who revised it - (c) 1994 The MITRE
* Corporation for those parts, and used/distributed with permission.
*/
package com.bbn.openmap.layer.rpf;
import java.awt.Color;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.bbn.openmap.io.BinaryBufferedFile;
import com.bbn.openmap.io.BinaryFile;
import com.bbn.openmap.io.FormatException;
import com.bbn.openmap.util.Debug;
/**
* Set up the colors used in creating the images. They are created from RGB
* value arrays read in from the RPF file. If the number of colors that are
* allowed is less than 216, then the RpfColortable looks inside the RpfFile and
* uses the color conversion tables inside. There will still be 216 color
* indexes, but some of the colors will be duplicates.
*/
public class RpfColortable {
public final static int CADRG_COLORS = 216;
public final static int COLORS_216 = 0;
public final static int COLORS_32 = 1;
public final static int COLORS_16 = 2;
public final static int CIB_SPEC_CODE_ID = 3;
public final static int DEFAULT_OPAQUENESS = 255;
/**
* Color conversion table (to be filled) from within frame, colortable
* section. The colortable is always 216 entries long. If you want fewer
* colors, some of the entries are duplicated.
*/
public int[] colorConvTable = new int[CADRG_COLORS];
/** Index to use a color conversion table, and if so, which one. */
protected int reducedColorTable = COLORS_216;
protected int numColors = 0;
protected boolean Cib = false;
protected int opaqueness = DEFAULT_OPAQUENESS;
/** The actual OMColors to use in the image construction. */
public Color[] colors = null;
/** Zone ID for these colors. */
public char zone;
/** Chart Series Code for these colors. */
public String seriesCode;
/**
* The index of the A.TOC file in the RpfTocHandler being used for the
* current colors.
*/
protected int tocNumber = -1;
/**
* The index of the RpfEntry in the A.TOC file being used for the current
* colors.
*/
protected int entryNumber = -1;
public RpfColortable() {
this(CADRG_COLORS, DEFAULT_OPAQUENESS, false);
}
public RpfColortable(int nColors) {
this(nColors, DEFAULT_OPAQUENESS, false);
}
public RpfColortable(int nColors, int opaque, boolean cib) {
setNumColors(nColors);
setOpaqueness(opaque);
setCib(cib);
}
/**
* Load the provided colortable with the color values of this colortable.
* The A.TOC values that are set by the RpfFrameCacheHandler are not set,
* just the color table information read from the RPF frame file.
*
* @param colortable
*/
public void setFrom(RpfColortable colortable) {
colors = colortable.colors;
reducedColorTable = colortable.reducedColorTable;
colorConvTable = colortable.colorConvTable;
Cib = colortable.Cib;
opaqueness = colortable.opaqueness;
zone = colortable.zone;
}
/**
* Set the alpha values of the OMColors, which governs the
* transparency/opaqueness of the images.
*
* @param value index between 0-255 (0 is transparent, 255 is opaque)
*/
public void setOpaqueness(int value) {
opaqueness = value;
if (colors != null) {
for (int i = 0; i < colors.length; i++) {
Color tmp = colors[i];
colors[i] = new Color(tmp.getRed(), tmp.getGreen(), tmp.getBlue(), opaqueness);
}
}
}
public int getOpaqueness() {
return opaqueness;
}
/**
* Set the alpha values of the OMColors, which governs the
* transparency/opaqueness of the images. This method lets you set the value
* as a percentage between 0-100.
*
* @param percent index between 0-100 (0 is transparent, 100 is opaque)
*/
public void setOpaquePercent(int percent) {
setOpaqueness((int) ((float) (percent * 2.55)));
}
public int getOpaquePercent() {
return (int) ((float) opaqueness * 100.0 / 255.0);
}
public void setNumColors(int numColorsValue) {
numColors = numColorsValue;
if (numColors >= 216)
reducedColorTable = COLORS_216;
else if (numColors >= 32)
reducedColorTable = COLORS_32;
else
reducedColorTable = COLORS_16;
}
/** Returns the number of colors. */
public int getNumColors() {
return numColors;
}
/**
* Returns the color reduction index. These values correspond to the
* constants defined in this class.
*/
public int getColorTableReduction() {
return reducedColorTable;
}
/**
* If this object is going to provide colors for CIB imagery, you have to
* let this object know that. Set this to true. It is false by default.
*
* @param value true if the colortable will be used for greyscale images.
*/
public void setCib(boolean value) {
Cib = value;
}
public boolean isCib() {
return Cib;
}
/**
* Should be set when a new colortable is read in, so that you can tell when
* you don't have to read a new one.
*/
public void setATOCIndexes(int tocIndex, int entryIndex) {
tocNumber = tocIndex;
entryNumber = entryIndex;
}
/**
* Return true of the toc index and entry index are the same as what the
* colortable is currently holding.
*/
public boolean isSameATOCIndexes(int tocIndex, int entryIndex) {
return (tocIndex == tocNumber && entryIndex == entryNumber);
}
/**
* Not really used, but someone might need them. Returns the A.TOC index
* number of the colors, to compare to see if a new colortable is needed.
*/
public int getTocNumber() {
return tocNumber;
}
/**
* Not really used, but someone might need them. Returns the A.TOC entry
* number of the colors, to compare to see if a new colortable is needed.
*/
public int getEntryNumber() {
return entryNumber;
}
/**
* The method to call to read in the colortable from within the RPF file.
* The method will use the input to determine where in the file to read
* from.
*
* @param binFile the file to read it in from.
* @param loc the RpfLocationRecord that tells you where the sections are.
* @return an array of OMColors to use in images.
*/
public Color[] parseColorLookUpTable(BinaryFile binFile,
RpfFileSections.RpfLocationRecord[] loc) {
if (Debug.debugging("rpfcolortable")) {
Debug.output("RpfColortable: creating new colors for colortable.");
}
// change this to the proper color structure
Color[] rgb = new Color[CADRG_COLORS]; /* DKS NEW: 216 */
int i, j;
long ncr;
int red, green, blue, alpha;
int numColorOffsetRecs; // uchar, # of color/gray offset
// records */
int numColorConvOffsetRecs; // uchar
int offsetRecordLength = 17; // ushort
/* see frame.h */
ColorOffset[] colorOffset;
long colormapOffsetTableOffset; // uint
/* color converter subsection hdr */
long colorConvOffsetTableOffset; // uint
int colorConvOffsetRecl; // ushort
int colorConvRecl; // ushort
boolean foundLUT; /* found lut flag */
if (Debug.debugging("rpfdetail")) {
Debug.output("ENTER PARSE Colortable");
}
try {
/* Go find the color table: loc[0].id=LOC_CLUT */
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: Color/gray section subheader (ID=134) location: "
+ loc[0].componentLocation);
}
binFile.seek(loc[0].componentLocation);
/* Read section subheader */
/* Number of offset records: 2 */
numColorOffsetRecs = binFile.read();
/* Number of cc offset records: 3 */
numColorConvOffsetRecs = binFile.read();
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: numColorOffsetRecs(3): "
+ numColorOffsetRecs);
Debug.output("RpfColortable: numColorConvOffsetRecs(2): "
+ numColorConvOffsetRecs);
}
/* DKS. New, read array of structures */
/* Read colormap offset table */
colorOffset = new ColorOffset[numColorOffsetRecs];
/*
* DKS. Read color/gray offset records (colormap subsection)
*/
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: Colormap subsection loc[1]: "
+ loc[1].componentLocation);
}
binFile.seek(loc[1].componentLocation);
/* colormap offset table offset: length 4 */
colormapOffsetTableOffset = (long) binFile.readInteger();
/* offset record length:17? length 2 */
offsetRecordLength = (int) binFile.readShort();
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: colormapOffsetTableOffset: "
+ colormapOffsetTableOffset);
Debug.output("RpfColortable: offsetRecordLength:"
+ offsetRecordLength);
}
if (reducedColorTable == COLORS_216 || Cib) { /*
* 216 or 217 colors
* desired. No cct
* reading needed
*/
/* Read colormap offset table */
for (i = 0; i < numColorOffsetRecs; i++) { /* 3 */
colorOffset[i] = new ColorOffset();
colorOffset[i].tableId = (int) binFile.readShort();
colorOffset[i].numColorRecords = (long) (binFile.readInteger() & 0xFFFFFFFFL);
colorOffset[i].colorElementLength = binFile.read();
colorOffset[i].histogramRecordLength = (int) binFile.readShort();
colorOffset[i].colorTableOffset = (long) binFile.readInteger() & 0xFFFFFFFFL;
colorOffset[i].histogramTableOffset = (long) binFile.readInteger() & 0xFFFFFFFFL;
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: Parse_clut: " + i);
Debug.output(colorOffset[i].toString());
}
// May look hackish, but 3 is the specification
// number for CIB
if (colorOffset[i].tableId == CIB_SPEC_CODE_ID) {
Cib = true;
} else {
Cib = false;
}
/* look for numColorRecords[i] == 216 or 217 */
ncr = colorOffset[i].numColorRecords;
if ((ncr == 216) || (ncr == 217))
foundLUT = true;
else
foundLUT = false;
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: foundLUT of desired 216?: "
+ foundLUT);
}
if (foundLUT) {
/*
* Read the color/gray records: 216 or 217 (transp)
* color table.
*/
/* loc[1] is colormap subsection */
binFile.seek(loc[1].componentLocation
+ colorOffset[i].colorTableOffset);
if (ncr >= CADRG_COLORS) {
if (Debug.debugging("rpf")) {
Debug.error("RpfColortable: ncr is not correct, wingin' it ("
+ ncr + ")");
}
ncr = CADRG_COLORS;
}
for (j = 0; j < ncr; j++) { /* 216 or 217 */
colorConvTable[j] = j;
// Allocate the OMColor here......
if (Cib) {
red = binFile.read() & 0x00ff; /*
* read mono
* byte value
*/
alpha = opaqueness;
green = red;
blue = red;
} else {
red = binFile.read() & 0x00ff; /*
* read byte
* value
*/
green = binFile.read() & 0x00ff; /*
* read byte
* value
*/
blue = binFile.read() & 0x00ff; /*
* read byte
* value
*/
alpha = binFile.read(); /*
* read byte value
*/
alpha = opaqueness;
/* DKS NEW TRANSP */
if (ncr == 217 && rgb[(int) (ncr - 1)] == null) { /*
* transp
* exists
*/
alpha = 255;
red = 255;
green = 255;
blue = 255;
rgb[(int) (ncr - 1)] = new Color(red, green, blue, alpha);
} /* if */
} /* else */
rgb[j] = new Color(red, green, blue, alpha);
if (Debug.debugging("rpfcolortable")) {
if (j == 0)
Debug.output("RpfColortable:\n\n---Full color table---\n");
Debug.output("RpfColortable:red: " + red
+ ", green: " + green + ", blue: "
+ blue + ", alpha: " + alpha);
}
} /* for j */
break; /* out of for i */
} /* if foundLUT */
} /* for i */
} /* if reducedColorTable == COLOR_216 */
else { /* cct needed */
/* DKS. Read cct records */
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: color converter subsection loc[2]:"
+ loc[2].componentLocation);
}
binFile.seek(loc[2].componentLocation);
colorConvOffsetTableOffset = (long) binFile.readInteger();
colorConvOffsetRecl = (int) binFile.readShort();
colorConvRecl = (int) binFile.readShort();
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: colorConvOffsetTableOffset:"
+ colorConvOffsetTableOffset);
Debug.output("RpfColortable: colorConvOffsetRecl:"
+ colorConvOffsetRecl);
Debug.output("RpfColortable: colorConvRecl:"
+ colorConvRecl);
}
ColorConversionTable[] cct = new ColorConversionTable[numColorConvOffsetRecs];
/* Color Converter offset table */
for (i = 0; i < numColorConvOffsetRecs; i++) { /*
* 2 cct recs
*/
cct[i] = new ColorConversionTable();
cct[i].colorConvTableId = (int) binFile.readShort();
cct[i].colorConvNumRecs = (long) binFile.readInteger();
cct[i].colorConvTableOffset = (long) binFile.readInteger();
cct[i].colorConvSourceTableOffset = (long) binFile.readInteger();
cct[i].colorConvTargetTableOffset = (long) binFile.readInteger();
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: color conversion table - "
+ i);
Debug.output(cct[i].toString());
}
} /* for i */
colorOffset = new ColorOffset[numColorConvOffsetRecs];
for (i = 0; i < numColorConvOffsetRecs; i++) { /* 2 */
/*
* Read colormap subsection for this target table: find #
* color/gray recs.
*/
binFile.seek(loc[1].componentLocation
+ cct[i].colorConvTargetTableOffset);
colorOffset[i] = new ColorOffset();
colorOffset[i].tableId = (int) binFile.readShort();
colorOffset[i].numColorRecords = (long) binFile.readInteger();
/* look for numColorRecords[i] == 216 or 217 */
ncr = colorOffset[i].numColorRecords;
/* numColorRecords[0] can't be 216 for a cct */
/* Read, use 32 or 33 clrs */
if ((((ncr == 32) || (ncr == 33)) && (reducedColorTable == COLORS_32))
|| (((ncr == 16) || (ncr == 17)) && (reducedColorTable == COLORS_16))) {
/* Read, use 16 or 17 clrs */
foundLUT = true;
} else {
foundLUT = false;
}
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: foundLUT?:" + foundLUT);
}
if (foundLUT) { /*
* continue reading colormap subsection
*/
colorOffset[i].colorElementLength = binFile.read();
colorOffset[i].histogramRecordLength = (int) binFile.readShort();
colorOffset[i].colorTableOffset = (long) binFile.readInteger();
colorOffset[i].histogramTableOffset = (long) binFile.readInteger();
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: Parse_clut: " + i);
Debug.output(colorOffset[i].toString());
}
// ////////////////////////////
/*
* loc[1] is colormap subsection. Seek to color/gray
* table.
*/
binFile.seek(loc[1].componentLocation
+ colorOffset[i].colorTableOffset);
/*
* Read the color/gray records: 32 or 33, or 16 or 17
* color tables
*/
for (j = 0; j < ncr; j++) { /*
* 32 or 33, or 16 or 17
*/
red = binFile.read() & 0x00ff; /*
* read byte value
*/
green = binFile.read() & 0x00ff; /*
* read byte
* value
*/
blue = binFile.read() & 0x00ff; /*
* read byte value
*/
alpha = binFile.read(); /* read byte value */
alpha = opaqueness;
/* DKS NEW TRANSP */
if (ncr == 217 && rgb[(int) (ncr - 1)] == null) { /*
* transp
* exists
*/
alpha = opaqueness;
red = 255;
green = 255;
blue = 255;
rgb[(int) (ncr - 1)] = new Color(red, green, blue, alpha);
} /* if */
rgb[j] = new Color(red, green, blue, alpha);
if (Debug.debugging("rpfcolortable")) {
if (j == 0)
Debug.output("RpfColortable:\n\n---CCT color table---\n");
Debug.output("RpfColortable: red:" + red
+ ", green:" + green + ", blue:" + blue
+ ", alpha: " + alpha);
}
} /* for j */
/* go to start of color converter table */
/* loc[2] is color converter subsection */
binFile.seek(loc[2].componentLocation
+ cct[i].colorConvTableOffset);
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: i:" + i
+ ", colorConvTableOffset[i]:"
+ cct[i].colorConvTableOffset);
Debug.output("RpfColortable: Read cct values at file location:"
+ binFile.getFilePointer());
}
for (j = 0; j < cct[i].colorConvNumRecs; j++) {
colorConvTable[j] = binFile.readInteger();
if (Debug.debugging("rpfcolortable"))
Debug.output("RpfColortable: j:" + j
+ ", colorConvTable[j]:"
+ colorConvTable[j]);
}
break; /* for i */
} /* if foundLUT */
} /* for i = numColorConvOffsetRecs */
} /* else CCT needed */
if (reducedColorTable == COLORS_216) { /*
* 216 colors chosen
*/
if (Debug.debugging("rpfdetail"))
Debug.output("RpfColortable: WARNING - Full 216 colors being used\n");
for (j = 0; j < CADRG_COLORS; j++) { /* 216 */
colorConvTable[j] = j;
} /* for j */
}
// Since the CIB doesn't contain ccts, we need to fake
// it...
if (Cib && reducedColorTable != COLORS_216) {
int divisor, midoffset;
if (reducedColorTable == COLORS_32) {
divisor = 8;
midoffset = 4;
} else {
divisor = 16;
midoffset = 8;
}
for (j = 0; j < CADRG_COLORS; j++) { /* 216 */
red = (int) (rgb[j].getRed() / divisor) * divisor
+ midoffset;
green = (int) (rgb[j].getGreen() / divisor) * divisor
+ midoffset;
blue = (int) (rgb[j].getBlue() / divisor) * divisor
+ midoffset;
alpha = rgb[j].getAlpha();
rgb[j] = new Color(red, green, blue, alpha);
if (Debug.debugging("rpfcolortable")) {
if (j == 0)
Debug.output("RpfColortable:\n\n---Final color table CIB---\n");
Debug.output("RpfColortable: Color " + j + " red: "
+ rgb[j].getRed() + ", green: "
+ rgb[j].getGreen() + ", blue: "
+ rgb[j].getBlue());
}
}
} // if Cib
// For CADRG that has a cct or also Cib that doesn't
/* DKS. cct added here instead of load_frame */
else if (reducedColorTable != COLORS_216) {
for (j = 0; j < CADRG_COLORS; j++) { /* 216 */
red = rgb[colorConvTable[j]].getRed();
green = rgb[colorConvTable[j]].getGreen();
blue = rgb[colorConvTable[j]].getBlue();
alpha = rgb[colorConvTable[j]].getAlpha();
rgb[j] = new Color(red, green, blue, alpha);
if (Debug.debugging("rpfcolortable")) {
if (j == 0)
Debug.output("RpfColortable:\n\n---Final color table---\n");
Debug.output("RpfColortable: Color " + j + " red: "
+ rgb[j].getRed() + ", green: "
+ rgb[j].getGreen() + ", blue: "
+ rgb[j].getBlue());
}
} /* for j */
}
if (Debug.debugging("rpfdetail")) {
Debug.output("RpfColortable: LEAVE PARSE Colortable");
}
} catch (IOException ioe) {
Debug.error("RpfTocHandler: IO ERROR parsing file!\n" + ioe);
return null;
} catch (FormatException fe) {
Debug.error("RpfTocHandler: Format ERROR parsing file!\n" + fe);
return null;
}
colors = rgb;
return rgb;
} /* parse_clut.c */
static public class ColorOffset {
public int tableId;
public long numColorRecords;
public int colorElementLength; // uchar
public int histogramRecordLength;
public long colorTableOffset;
public long histogramTableOffset;
public ColorOffset() {}
public String toString() {
StringBuffer s = new StringBuffer();
s.append("RpfColortable: tableId 2:CADRG; 3:CIB): ").append(tableId)
.append("\n");
s.append("RpfColortable: numColorRecords: ").append(numColorRecords)
.append("\n");
s.append("RpfColortable: colorElementLength: ")
.append(colorElementLength).append("\n");
s.append("RpfColortable: histogramRecordLength: ")
.append(histogramRecordLength).append("\n");
s.append("RpfColortable: colorTableOffset: ")
.append(colorTableOffset).append("\n");
s.append("RpfColortable: histogramTableOffset: ")
.append(histogramTableOffset);
return s.toString();
}
}
static public class ColorConversionTable {
public int colorConvTableId; // ushort
public long colorConvNumRecs; // uint
public long colorConvTableOffset; // uint
public long colorConvSourceTableOffset; // uint
public long colorConvTargetTableOffset; // uint
public ColorConversionTable() {}
public String toString() {
StringBuffer s = new StringBuffer();
s.append("RpfColortable: colorConvTableId: ")
.append(colorConvTableId).append("\n");
s.append("RpfColortable: colorConvNumRecs: ")
.append(colorConvNumRecs).append("\n");
s.append("RpfColortable: colorConvTableOffset: ")
.append(colorConvTableOffset).append("\n");
s.append("RpfColortable: colorConvSourceTableOffset: ")
.append(colorConvSourceTableOffset).append("\n");
s.append("RpfColortable: colorConvTargetTableOffset: ")
.append(colorConvTargetTableOffset);
return s.toString();
}
}
public static void main(String[] args) {
Debug.init(System.getProperties());
if (args.length != 1) {
Debug.output("Usage: java RpfColortable <path to RPF frame>");
return;
}
File file = new File(args[0]);
BinaryFile binFile = null;
try {
binFile = new BinaryBufferedFile(file);
} catch (FileNotFoundException e) {
Debug.error("RpfHeader: file " + args[0] + " not found");
System.exit(1);
} catch (IOException ioe) {
Debug.error("RpfHeader: File IO Error while handling colortable:\n"
+ ioe);
System.exit(1);
}
RpfColortable tbl = new RpfColortable();
RpfFileSections rfs = new RpfFileSections();
RpfHeader head = new RpfHeader();
head.read(binFile);
rfs.parse(binFile);
Color[] colors = rfs.parseColorSection(binFile, tbl);
if (colors == null)
Debug.output("RpfColortable: NOT read successfully!");
}
}