Issue #2492 Implemented support for colormapping in different units than the data comes in as. Other changes include NPE fix in RecordTileSetRenderable, glsl cleanup to use structs for code sharing, a topo display fix, and moved viirs colormaps to common

Amend: Fixed precision issue with float comparison, made factory better synchronized.

Change-Id: Ie72d8d26ca5dc5ee1a0b5c14ca726edbee3bc1a2

Former-commit-id: ae724ecce3d4c65f8845f3278f9b5eeb5b04908a
This commit is contained in:
Max Schenkelberg 2013-11-04 17:52:38 -06:00
parent 7156ac5d4f
commit 3369fbec2d
35 changed files with 1934 additions and 907 deletions

View file

@ -195,25 +195,28 @@ public class RecordTileSetRenderable extends TileSetRenderable {
// All the images need staging, do bulk request
ColorMapData data = retrieveRecordData(bigTile);
Rectangle bigTileRect = bigTile.getRectangle();
for (int i = 0; i < numTiles; i += 1) {
Tile tile = subTiles.get(i);
DrawableImage image = images.get(i);
if (image != null) {
if (image.getImage().getStatus() == Status.UNLOADED) {
Rectangle tileRect = tile.getRectangle();
ColorMapData subData = new ColorMapData(
BufferSlicer.slice(data.getBuffer(),
tileRect, bigTileRect), new int[] {
tileRect.width, tileRect.height },
data.getDataType(), data.getDataUnit());
if (data != null) {
Rectangle bigTileRect = bigTile.getRectangle();
for (int i = 0; i < numTiles; i += 1) {
Tile tile = subTiles.get(i);
DrawableImage image = images.get(i);
if (image != null) {
if (image.getImage().getStatus() == Status.UNLOADED) {
Rectangle tileRect = tile.getRectangle();
ColorMapData subData = new ColorMapData(
BufferSlicer.slice(data.getBuffer(),
tileRect, bigTileRect),
new int[] { tileRect.width,
tileRect.height },
data.getDataType(), data.getDataUnit());
callbacks.get(i).setRetrievedData(subData);
try {
image.getImage().stage();
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
callbacks.get(i).setRetrievedData(subData);
try {
image.getImage().stage();
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
}
}
}

View file

@ -424,6 +424,7 @@ public class VIIRSResource extends
}
colorMapParameters.setDataUnit(dataUnit);
colorMapParameters.setColorMapUnit(dataUnit);
colorMapParameters.setDisplayUnit(displayUnit);
colorMapParameters.setColorMapMin(colorMapParameters.getDataMin());
@ -433,12 +434,14 @@ public class VIIRSResource extends
if (scale != null) {
UnitConverter displayToData = colorMapParameters
.getDisplayToDataConverter();
UnitConverter displayToColorMap = colorMapParameters
.getDisplayToColorMapConverter();
if (scale.getMinValue() != null) {
colorMapParameters.setColorMapMin((float) displayToData
colorMapParameters.setColorMapMin((float) displayToColorMap
.convert(scale.getMinValue()));
}
if (scale.getMaxValue() != null) {
colorMapParameters.setColorMapMax((float) displayToData
colorMapParameters.setColorMapMax((float) displayToColorMap
.convert(scale.getMaxValue()));
}
if (scale.getMinValue2() != null) {

View file

@ -1,90 +1,89 @@
#include <colorUtil>
#include <indexing>
#include <mapping>
// Multiplier used to store bit mask safely between 0-1
const float maskMultiplier = 8.0;
const int RED_BAND = 0;
const int GREEN_BAND = 1;
const int BLUE_BAND = 2;
uniform sampler2D rawTex;
uniform float naturalMin;
uniform float naturalMax;
uniform float cmapMin;
uniform float cmapMax;
uniform int isFloat;
uniform int band;
uniform DataTexture rawData;
uniform DataMapping dataMapping;
uniform ColorMapping colorMapping;
uniform sampler2D trueColorTexture;
uniform int height;
uniform int width;
uniform float height;
uniform float width;
uniform float noDataValue;
uniform float alphaStep;
uniform int band;
uniform int expectedMask;
int toBitMask(float alpha) {
return int((alpha * maskMultiplier) + 0.5);
}
float getIndex(sampler2D rawTex, float cmapMin, float cmapMax, float naturalMin, float naturalMax, int isFloat) {
vec4 textureValue = texture2D(rawTex, gl_TexCoord[0].st);
float naturalVal = textureValue.r;
if ( isFloat == 0 ) {
naturalVal = ((naturalVal * (naturalMax - naturalMin)) + naturalMin);
}
float index = -1.0;
if (naturalVal != noDataValue && naturalVal == naturalVal) {
index = findIndex(naturalVal, cmapMin, cmapMax);
}
return index;
float fromBitMask(int bitMask) {
return bitMask / maskMultiplier;
}
void main(void)
{
if ( band == -1 ) {
vec4 imageVal = texture2D(rawTex,gl_TexCoord[0].st);
float r = imageVal.r;
float g = imageVal.g;
float b = imageVal.b;
float a = imageVal.a;
// Round because of 8-bit floating point precision
int bitMask = toBitMask(a);
if (expectedMask > 0 && bitMask == expectedMask ) {
a = 1.0;
} else {
a = 0.0;
}
gl_FragColor = vec4(r,g,b,a);
vec4 getFinalColor() {
vec4 imageVal = texture2D(trueColorTexture, gl_TexCoord[0].st);
float r = imageVal.r;
float g = imageVal.g;
float b = imageVal.b;
float a = imageVal.a;
// Round because of 8-bit floating point precision
int bitMask = toBitMask(a);
if (expectedMask > 0 && bitMask == expectedMask) {
a = 1.0;
} else {
vec2 xy = gl_FragCoord.xy;
vec4 imageVal = texture2D(rawTex,gl_TexCoord[0].st);
vec4 curVal = texture2D(trueColorTexture, vec2((xy.x / float(width)), (xy.y / float(height))));
a = 0.0;
}
return vec4(r, g, b, a);
}
vec4 applyColorBand(int colorband) {
vec2 xy = gl_FragCoord.xy;
vec4 curVal = texture2D(trueColorTexture,
vec2((xy.x / width), (xy.y / height)));
// Lookup raw data value
float dataValue = getDataValue(rawData, gl_TexCoord[0].st);
float r = curVal.r;
float g = curVal.g;
float b = curVal.b;
float a = curVal.a;
if (dataValue != rawData.noDataValue && dataValue == dataValue) {
// Convert dataValue to cmapValue
float cmapValue = dataToColorMapValue(dataValue, dataMapping);
float index = getColorMappingIndex(cmapValue, colorMapping);
float r = curVal.r;
float g = curVal.g;
float b = curVal.b;
float a = curVal.a;
float index = getIndex(rawTex, cmapMin, cmapMax, naturalMin, naturalMax, isFloat);
if ( index != -1.0 ) {
int currentMask = toBitMask(a);
int bitValue = (1 << band);
if ( band == 0 && index > r ) {
r = index;
} else if ( band == 1 && index > g ) {
g = index;
} else if ( band == 2 && index > b ) {
b = index;
}
if ( (currentMask & bitValue) == 0 ) {
// alpha does not contain this bit yet!
a = (currentMask | bitValue) / maskMultiplier;
}
int currentMask = toBitMask(a);
int bitValue = (1 << band);
if (colorband == RED_BAND && index > r) {
r = index;
} else if (colorband == GREEN_BAND && index > g) {
g = index;
} else if (colorband == BLUE_BAND && index > b) {
b = index;
}
gl_FragColor = vec4(r,g,b,a);
if ((currentMask & bitValue) == 0) {
// alpha does not contain this bit yet!
a = fromBitMask(currentMask | bitValue);
}
}
return vec4(r, g, b, a);
}
void main(void) {
if (band == -1) {
gl_FragColor = getFinalColor();
} else {
gl_FragColor = applyColorBand(band);
}
}

View file

@ -35,10 +35,13 @@ import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension;
import com.raytheon.uf.viz.truecolor.gl.image.GLTrueColorImage;
import com.raytheon.viz.core.gl.ext.GLOffscreenRenderingExtension;
import com.raytheon.viz.core.gl.ext.imaging.GLColormappedImageExtension;
import com.raytheon.viz.core.gl.ext.imaging.GLDataMappingFactory.GLDataMapping;
import com.raytheon.viz.core.gl.glsl.AbstractGLSLImagingExtension;
import com.raytheon.viz.core.gl.glsl.GLSLStructFactory;
import com.raytheon.viz.core.gl.glsl.GLShaderProgram;
import com.raytheon.viz.core.gl.images.AbstractGLColormappedImage;
import com.raytheon.viz.core.gl.images.AbstractGLImage;
import com.raytheon.viz.core.gl.images.GLColormappedImage;
/**
* GL implementation of the {@link ITrueColorImagingExtension}
@ -64,9 +67,6 @@ public class GLTrueColorImagingExtension extends AbstractGLSLImagingExtension
private Channel renderingChannel;
/** The current rendering bit mask, specifies what bands we rendered */
private int currentMask = 0;
private Map<ColorMapParameters, Object> parameters = new IdentityHashMap<ColorMapParameters, Object>();
/*
@ -111,7 +111,6 @@ public class GLTrueColorImagingExtension extends AbstractGLSLImagingExtension
GLTrueColorImage trueColorImage = (GLTrueColorImage) image;
if (trueColorImage.isRepaint()) {
// Reset current bit mask
currentMask = 0;
parameters.clear();
writeToImage = trueColorImage;
GLOffscreenRenderingExtension extension = target
@ -125,9 +124,6 @@ public class GLTrueColorImagingExtension extends AbstractGLSLImagingExtension
DrawableImage[] imagesToDraw = trueColorImage
.getImages(channel);
if (imagesToDraw != null && imagesToDraw.length > 0) {
// Mark the channel bit in the current bit mask
currentMask |= (1 << channel.ordinal());
// Make sure images are staged before we mosaic them
ImagingSupport.prepareImages(target, imagesToDraw);
@ -160,12 +156,17 @@ public class GLTrueColorImagingExtension extends AbstractGLSLImagingExtension
imageCoverage));
return null;
}
} else {
} else if (image instanceof AbstractGLColormappedImage) {
GL gl = target.getGl();
GLColormappedImageExtension.setupDataMapping(gl,
(AbstractGLColormappedImage) image, GL.GL_TEXTURE2,
GL.GL_TEXTURE3);
// bind on GL_TEXTURE1 as 0 is channel image
writeToImage.bind(gl, GL.GL_TEXTURE1);
return image;
}
return null;
}
/*
@ -190,6 +191,13 @@ public class GLTrueColorImagingExtension extends AbstractGLSLImagingExtension
// Unbind the writeToImage from GL_TEXTURE1
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(writeToImage.getTextureStorageType(), 0);
// Unbind the data mapped textures
gl.glActiveTexture(GL.GL_TEXTURE2);
gl.glBindTexture(GL.GL_TEXTURE_1D, 0);
gl.glActiveTexture(GL.GL_TEXTURE3);
gl.glBindTexture(GL.GL_TEXTURE_1D, 0);
}
}
@ -206,36 +214,41 @@ public class GLTrueColorImagingExtension extends AbstractGLSLImagingExtension
public void loadShaderData(GLShaderProgram program, IImage image,
PaintProperties paintProps) throws VizException {
if (image instanceof GLTrueColorImage) {
GLTrueColorImage glImage = (GLTrueColorImage) image;
program.setUniform("band", -1);
program.setUniform("rawTex", 0);
program.setUniform("expectedMask", currentMask);
program.setUniform("trueColorTexture", 0);
program.setUniform("expectedMask", glImage.getColorMask());
} else {
if (image instanceof GLColormappedImage == false) {
if (image instanceof AbstractGLColormappedImage == false) {
throw new VizException(
"Can only render colormapped images in true color");
}
GLColormappedImage cmapImage = (GLColormappedImage) image;
AbstractGLColormappedImage cmapImage = (AbstractGLColormappedImage) image;
ColorMapParameters colorMapParameters = cmapImage
.getColorMapParameters();
parameters.put(colorMapParameters, null);
int textureType = cmapImage.getTextureType();
// Set the band image data
program.setUniform("rawTex", 0);
program.setUniform("naturalMin", colorMapParameters.getDataMin());
program.setUniform("naturalMax", colorMapParameters.getDataMax());
program.setUniform("cmapMin", colorMapParameters.getColorMapMin());
program.setUniform("cmapMax", colorMapParameters.getColorMapMax());
program.setUniform("isFloat", textureType == GL.GL_FLOAT
|| textureType == GL.GL_HALF_FLOAT_ARB ? 1 : 0);
program.setUniform("noDataValue",
parameters.put(colorMapParameters, null);
GLSLStructFactory.createDataTexture(program, "rawData", 0,
cmapImage.getDataFormat(),
colorMapParameters.getNoDataValue());
int numMappingValues = 0;
GLDataMapping mapping = cmapImage.getDataMapping();
if (mapping != null && mapping.isValid()) {
numMappingValues = mapping.getNumMappingValues();
}
GLSLStructFactory.createDataMapping(program, "dataMapping", 2, 3,
numMappingValues);
GLSLStructFactory.createColorMapping(program, "colorMapping", -1,
-1, colorMapParameters);
// Set the composite image data
program.setUniform("trueColorTexture", 1);
program.setUniform("width", writeToImage.getWidth());
program.setUniform("height", writeToImage.getHeight());
program.setUniform("width", (float) writeToImage.getWidth());
program.setUniform("height", (float) writeToImage.getHeight());
// Set the band we are rendering to
program.setUniform("band", renderingChannel.ordinal());

View file

@ -97,6 +97,22 @@ public class GLTrueColorImage extends GLDelegateImage<GLImage> implements
this.imageExtent = imageExtent;
}
/**
* Returns a bitmask of the expected RGB components to render
*
* @return
*/
public int getColorMask() {
int colorMask = 0;
for (Channel channel : Channel.values()) {
DrawableImage[] images = getImages(channel);
if (images != null && images.length > 0) {
colorMask |= (1 << channel.ordinal());
}
}
return colorMask;
}
/**
* @return the imageExtent
*/

View file

@ -1,24 +1,21 @@
// Simple shader program for applying alpha,brightness, and contrast to the
// colormap in the same way they are applied to data
#include <mapping>
#include <coloring>
#include <colorUtil>
#include <indexing>
uniform float brightness;
uniform float contrast;
uniform float alphaVal;
uniform ColorMapping colorMapping;
uniform ColorModifiers modifiers;
uniform float bkgrndRed;
uniform float bkgrndGreen;
uniform float bkgrndBlue;
uniform sampler1D colorMap;
uniform sampler2D alphaMask;
uniform int applyMask;
uniform float logFactor;
void main(void){
sampler1D colorMap = colorMapping.colorMap;
float logFactor = colorMapping.logFactor;
int applyMask = colorMapping.applyMask;
sampler1D alphaMask = colorMapping.alphaMask;
// Lookup color in colorMap for index
float index = gl_TexCoord[0].s;
if ( logFactor > 0.0 ) {
@ -29,10 +26,11 @@ void main(void){
// Apply alpha mask if set
float alpha = color.a;
if ( applyMask == 1 ) {
if ( texture2D(alphaMask , vec2(index,index) ).r != 0.0 ) {
if ( texture1D(alphaMask , index ).r != 0.0 ) {
color = vec4(bkgrndRed, bkgrndGreen, bkgrndBlue, alpha);
}
}
if(alpha < 1.0){
// blend the color with background color, the colorbar should not be transparent
alpha = 1.0;
@ -42,5 +40,5 @@ void main(void){
alpha);
}
gl_FragColor = applyContrastAlphaBrightness(color, alphaVal, brightness, contrast);
gl_FragColor = applyColorModifiers(color, modifiers);
}

View file

@ -1,56 +1,24 @@
#include <colorUtil>
#include <indexing>
#include <mapping>
#include <coloring>
uniform float alpha;
uniform float brightness;
uniform float contrast;
uniform int applyMask;
uniform float naturalMin;
uniform float naturalMax;
uniform float cmapMin;
uniform float cmapMax;
uniform sampler1D colorMap;
uniform sampler2D alphaMask;
uniform sampler2D rawTex;
uniform float colorMapSz;
uniform int isFloat;
uniform int logarithmic;
uniform int mirror;
uniform float logFactor;
uniform DataTexture rawData;
uniform DataMapping dataMapping;
uniform ColorMapping colorMapping;
uniform ColorModifiers modifiers;
void main(void) {
vec4 textureColor = texture2D(rawTex, gl_TexCoord[0].st);
float index = 0.0;
float rawValue = textureColor.r;
if ( isFloat == 1 ) {
if ( logarithmic == 1 ) {
index = findFloatIndexLog(rawValue, cmapMin, cmapMax, mirror);
} else {
index = findFloatIndex(rawValue, cmapMin, cmapMax);
}
// Special float handling, -1.0 is NaN
if (index == -1.0){
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
}
} else {
float naturalValue = ((rawValue * (naturalMax - naturalMin)) + naturalMin);
index = findIndex(naturalValue, cmapMin, cmapMax);
}
// Lookup color in colorMap for index
if ( logFactor > 0.0 ) {
index = getLogFactorIndex(index, logFactor);
}
textureColor = texture1D(colorMap, index).rgba;
// Apply alpha mask
if ( applyMask == 1 ) {
if ( texture2D(alphaMask , vec2(index,index) ).r != 0.0 ) {
textureColor = vec4(textureColor.rgb, 0.0);
}
float dataValue = getDataValue(rawData, gl_TexCoord[0].st);
// No data check/special NaN check
if (dataValue == rawData.noDataValue || dataValue != dataValue) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
}
gl_FragColor = applyContrastAlphaBrightness(textureColor, alpha, brightness, contrast);
// Convert dataValue to cmapValue
float cmapValue = dataToColorMapValue(dataValue, dataMapping);
// Get color for colormapping, given value
vec4 textureColor = getColorByValue(cmapValue, colorMapping);
// Apply the color modifiers into gl_FragColor
gl_FragColor = applyColorModifiers(textureColor, modifiers);
}

View file

@ -1,34 +0,0 @@
vec3 AvgLuminance = vec3(0.5, 0.5, 0.5);
/**
* This function applies the specified alpha, brightness, and contrast values
* to the color passed in
*/
vec4 applyContrastAlphaBrightness(vec4 color, float alpha, float brightness, float contrast){
vec3 textureColor3 = vec3(color);
vec3 adjustedColor = mix(AvgLuminance, textureColor3, contrast);
float curAlpha = min(color.a, alpha);
return vec4(adjustedColor.r * brightness, adjustedColor.g * brightness, adjustedColor.b * brightness, curAlpha);
}
/**
* This function calculates a new index to use based on the logFactor
*/
float getLogFactorIndex(float index, float logFactor) {
if (logFactor > 0.0){
float minLog = log(logFactor);
float maxLog = log(logFactor + 1.0);
float lg = log(logFactor + index);
index = (lg - minLog) / (maxLog - minLog);
if (index < 0.0){
index = 0.0;
}
else if (index > 1.0){
index = 1.0;
}
}
return index;
}

View file

@ -0,0 +1,23 @@
struct ColorModifiers {
float alpha;
float brightness;
float contrast;
};
vec3 AvgLuminance = vec3(0.5, 0.5, 0.5);
/**
* This function applies the specified ColorModifier values to the
* color passed in
*/
vec4 applyColorModifiers(vec4 color, ColorModifiers modifiers){
float alpha = modifiers.alpha;
float brightness = modifiers.brightness;
float contrast = modifiers.contrast;
vec3 textureColor3 = vec3(color);
vec3 adjustedColor = mix(AvgLuminance, textureColor3, contrast);
float curAlpha = min(color.a, alpha);
return vec4(adjustedColor.r * brightness, adjustedColor.g * brightness, adjustedColor.b * brightness, curAlpha);
}

View file

@ -1,104 +0,0 @@
float HALF_FLOAT_NaN = 65504.0;
/**
* This function takes an index number and caps it to the range 0-1
*/
float capIndex(float index) {
if ( index < 0.0 ) {
index = 0.0;
} else if ( index > 1.0 ) {
index = 1.0;
}
return index;
}
/**
* This function linearly finds the index for the rawValue into cmapMin/cmapMax.
* 65504.0 is treated as NaN for half floats and -1 is returned as special case
*/
float findFloatIndex(float rawValue, float cmapMin, float cmapMax) {
if ( rawValue == HALF_FLOAT_NaN || rawValue != rawValue) {
return -1.0;
}
float index = ((rawValue - cmapMin) / abs(cmapMax-cmapMin));
return capIndex(index);
}
/**
* This function logarithmically finds the index for the rawValue into cmapMin/cmapMax.
* 65504.0 is treated as NaN for half floats and -1 is returned as special case
*/
float findFloatIndexLog(float rawValue, float cmapMin, float cmapMax, int mirror) {
if ( rawValue == HALF_FLOAT_NaN ) {
return -1.0;
}
float index = 0.0;
// is this strictly negative, strictly positive or neg to pos scaling?
if ( cmapMin >= 0.0 && cmapMax >= 0.0 && mirror!=1) {
if(rawValue < cmapMin){
index = 0.0;
}else{
// simple calculation
index = ((log(rawValue) - log(cmapMin)) / abs(log(cmapMax)-log(cmapMin)));
}
} else if (cmapMin <= 0.0 && cmapMax <= 0.0 && mirror!=1) {
index = ((log(rawValue) - log(cmapMax)) / abs(log(cmapMin)-log(cmapMax)));
} else {
// special case, neg to pos:
float colorMapMin = cmapMin;
float colorMapMax = cmapMax;
float zeroVal = max(colorMapMax, abs(colorMapMin)) * 0.0001;
if (mirror==1 && (colorMapMin > 0.0 || colorMapMax < 0.0)) {
if (colorMapMax < 0.0) {
colorMapMax = -cmapMax;
rawValue = -rawValue;
zeroVal = -colorMapMin;
} else {
zeroVal = cmapMin;
}
colorMapMin = -cmapMax;
}
float leftZero = 0.0;
float rightZero = 0.0;
float absLogZeroVal = abs(log(zeroVal));
rightZero = absLogZeroVal + log(colorMapMax);
float cmapMax2 = abs(colorMapMin);
leftZero = absLogZeroVal + log(cmapMax2);
float zeroIndex = leftZero / (leftZero + rightZero);
// figure out index for texture val
float absTextureColor = abs(rawValue);
if (absTextureColor <= zeroVal) {
index = zeroIndex;
} else if (rawValue > 0.0) {
// positive texture color value, find index from 0 to
// cmapMax:
float logTexColor = absLogZeroVal + log(rawValue);
float texIndex = logTexColor / rightZero;
index = (zeroIndex + ((1.0 - zeroIndex) * texIndex));
} else {
// negative texture color value, find index from 0 to
// cmapMax:
float logTexColor = absLogZeroVal + log(absTextureColor);
float texIndex = logTexColor / leftZero;
index = (zeroIndex - (zeroIndex * texIndex));
}
}
return capIndex(index);
}
/**
* Given a raw data value linearly determine the index(0-1) into cmapMin/cmapMax
*/
float findIndex(float rawValue, float cmapMin, float cmapMax) {
float index = ((rawValue - cmapMin) / abs(cmapMax-cmapMin));
return capIndex(index);
}

View file

@ -0,0 +1,299 @@
/**
* Mapping glsl library for use by other glsl programs. Defines
* commonly used structures and functions for data and color mapping
*/
/**
* Fields for the raw texture mapping is applied to. isScaled
* implied data will range 0-1 and need scaling to get raw value
* where scaleMin maps to 0 and scaleMax maps to 1.
*/
struct DataTexture {
sampler2D rawTex;
float noDataValue;
int isScaled;
float scaleMin;
float scaleMax;
};
/**
* Fields used for converting from image data values to
* colormapping data values. Done to avoid conversions in
* application code. dmv[i] -> cmv[i]. Linear interpolation
* is done where non-exact matches are found. Mappings should
* be uploaded as floats so no scaling is needed
*/
struct DataMapping {
sampler1D dataMappingValues;
sampler1D colorMappingValues;
int numMappingValues;
};
struct ColorMapping {
/** Fields for color map and size. colorMap contains colors to
* use for mapping. cmapMin/Max is range colormap is applied over */
sampler1D colorMap;
float cmapMin;
float cmapMax;
/** Field for alpha masking the colors. alphaMask is a texture the
* same size as colorMap and contains 0s and 1s, 1 indicating alpha
* should be set to completely transparent */
int applyMask;
sampler1D alphaMask;
/** Fields for logarithmic and mirrored indexing into the colorMap */
int isMirrored;
float logFactor;
int isLogarithmic;
};
/**
* Returns the data value for the DataTexture at location.
*/
float getDataValue(DataTexture texture, vec2 location) {
vec4 textureValue = texture2D(texture.rawTex, location);
float dataValue = textureValue.r;
if (texture.isScaled == 1) {
// Convert to non-scaled value
dataValue = ((dataValue * (texture.scaleMax - texture.scaleMin))
+ texture.scaleMin);
}
return dataValue;
}
/**
* Looks up a value in a mapping texture given an index [0-numMappingValues).
*/
float lookupMappingValue(sampler1D mappingTex, int index,
int numMappingValues) {
return texture1D(mappingTex, float(index) / float(numMappingValues - 1)).r;
}
/**
* Converts a data value into a colorMap value given the DataMapping
*/
float dataToColorMapValue(float dataValue, DataMapping mapping) {
int numMappingValues = mapping.numMappingValues;
if (numMappingValues == 0) {
// Short circuit if no mapping is needed
return dataValue;
}
// Convert to colormap value
int lowIndex = 0;
int highIndex = numMappingValues - 1;
float lowValue = lookupMappingValue(mapping.dataMappingValues, lowIndex,
numMappingValues);
float highValue = lookupMappingValue(mapping.dataMappingValues, highIndex,
numMappingValues);
int reversed = 0;
if (lowValue > highValue) {
reversed = 1;
float tmp = lowValue;
lowValue = highValue;
highValue = tmp;
}
int done = 0;
// While there is at least one index to check
while (done == 0) {
int nextIndex = lowIndex + ((highIndex - lowIndex) / 2);
if (nextIndex > lowIndex && nextIndex < highIndex) {
// Look up next value and determine if it is a high or low
float nextValue = lookupMappingValue(mapping.dataMappingValues, nextIndex,
numMappingValues);
if (nextValue < dataValue) {
if (reversed == 0) {
lowIndex = nextIndex;
} else {
highIndex = nextIndex;
}
lowValue = nextValue;
} else {
if (reversed == 0) {
highIndex = nextIndex;
} else {
lowIndex = nextIndex;
}
highValue = nextValue;
}
} else {
done = 1;
}
}
// Percentage dataValue is linearly between low and high value
float factor = (dataValue - lowValue) / (highValue - lowValue);
if (reversed == 1) {
// Reverse factor for high->low indexing
factor = 1.0 - factor;
}
float lowCmapValue = lookupMappingValue(mapping.colorMappingValues, lowIndex,
numMappingValues);
float highCmapValue = lookupMappingValue(mapping.colorMappingValues, highIndex,
numMappingValues);
return lowCmapValue + (highCmapValue - lowCmapValue) * factor;
}
/**
* This function takes an index number and caps it to the range 0-1
*/
float capIndex(float index) {
if (index < 0.0) {
index = 0.0;
} else if (index > 1.0) {
index = 1.0;
}
return index;
}
/**
* Given a colorMap value linearly determine the index (capped at 0-1)
* into cmapMin/cmapMax
*/
float getLinearIndex(float cmapValue, float cmapMin, float cmapMax) {
float index = (cmapValue - cmapMin) / (cmapMax - cmapMin);
return capIndex(index);
}
/**
* This function logarithmically finds the index for the cmapValue into
* cmapMin/cmapMax (capped at 0-1).
*/
float getLogIndex(float cmapValue, float cmapMin, float cmapMax, int mirror) {
float index = 0.0;
// is this strictly negative, strictly positive or neg to pos scaling?
if (cmapMin >= 0.0 && cmapMax >= 0.0 && mirror != 1) {
if (cmapValue < cmapMin) {
index = 0.0;
} else {
// simple calculation
index = ((log(cmapValue) - log(cmapMin))
/ abs(log(cmapMax) - log(cmapMin)));
}
} else if (cmapMin <= 0.0 && cmapMax <= 0.0 && mirror != 1) {
index = ((log(cmapValue) - log(cmapMax))
/ abs(log(cmapMin) - log(cmapMax)));
} else {
// special case, neg to pos:
float colorMapMin = cmapMin;
float colorMapMax = cmapMax;
float zeroVal = max(colorMapMax, abs(colorMapMin)) * 0.0001;
if (mirror == 1 && (colorMapMin > 0.0 || colorMapMax < 0.0)) {
if (colorMapMax < 0.0) {
colorMapMax = -cmapMax;
cmapValue = -cmapValue;
zeroVal = -colorMapMin;
} else {
zeroVal = cmapMin;
}
colorMapMin = -cmapMax;
}
float leftZero = 0.0;
float rightZero = 0.0;
float absLogZeroVal = abs(log(zeroVal));
rightZero = absLogZeroVal + log(colorMapMax);
float cmapMax2 = abs(colorMapMin);
leftZero = absLogZeroVal + log(cmapMax2);
float zeroIndex = leftZero / (leftZero + rightZero);
// figure out index for texture val
float absTextureColor = abs(cmapValue);
if (absTextureColor <= zeroVal) {
index = zeroIndex;
} else if (cmapValue > 0.0) {
// positive texture color value, find index from 0 to
// cmapMax:
float logTexColor = absLogZeroVal + log(cmapValue);
float texIndex = logTexColor / rightZero;
index = (zeroIndex + ((1.0 - zeroIndex) * texIndex));
} else {
// negative texture color value, find index from 0 to
// cmapMax:
float logTexColor = absLogZeroVal + log(absTextureColor);
float texIndex = logTexColor / leftZero;
index = (zeroIndex - (zeroIndex * texIndex));
}
}
return capIndex(index);
}
/**
* This function calculates a new index to use based on the logFactor
* and passed in index
*/
float getLogFactorIndex(float index, float logFactor) {
if (logFactor > 0.0) {
float minLog = log(logFactor);
float maxLog = log(logFactor + 1.0);
float lg = log(logFactor + index);
index = (lg - minLog) / (maxLog - minLog);
if (index < 0.0) {
index = 0.0;
} else if (index > 1.0) {
index = 1.0;
}
}
return index;
}
/**
* Returns an index for the cmapValue based on the ColorMapping
*/
float getColorMappingIndex(float cmapValue, ColorMapping colorMapping) {
int logarithmic = colorMapping.isLogarithmic;
int mirror = colorMapping.isMirrored;
float logFactor = colorMapping.logFactor;
float cmapMin = colorMapping.cmapMin;
float cmapMax = colorMapping.cmapMax;
float index;
if (logarithmic == 1) {
index = getLogIndex(cmapValue, cmapMin, cmapMax, mirror);
} else {
index = getLinearIndex(cmapValue, cmapMin, cmapMax);
}
// Apply logFactor if set
if (logFactor > 0.0) {
index = getLogFactorIndex(index, logFactor);
}
return index;
}
/**
* Returns a color for the index based on the ColorMapping
*/
vec4 getColorByIndex(float index, ColorMapping colorMapping) {
// Lookup color in colorMap for index
vec4 textureColor = texture1D(colorMapping.colorMap, index).rgba;
// Apply alpha mask
if (colorMapping.applyMask == 1) {
if (texture1D(colorMapping.alphaMask, index).r != 0.0) {
textureColor = vec4(textureColor.rgb, 0.0);
}
}
return textureColor;
}
/**
* Returns a color for the cmapValue based on the ColorMapping
*/
vec4 getColorByValue(float cmapValue, ColorMapping colorMapping) {
return getColorByIndex(getColorMappingIndex(cmapValue, colorMapping),
colorMapping);
}

View file

@ -1,11 +1,9 @@
#include <colorUtil>
#include <coloring>
uniform float brightness;
uniform float contrast;
uniform float alpha;
uniform sampler2D rawTex;
uniform ColorModifiers modifiers;
void main(void) {
vec4 textureColor = texture2D(rawTex, gl_TexCoord[0].st);
gl_FragColor = applyContrastAlphaBrightness(textureColor, alpha, brightness, contrast);
gl_FragColor = applyColorModifiers(textureColor, modifiers);
}

View file

@ -1,13 +1,11 @@
#include <colorUtil>
#include <coloring>
uniform float brightness;
uniform float contrast;
uniform float alpha;
uniform sampler2D rawTex;
uniform vec3 color;
uniform ColorModifiers modifiers;
void main(void) {
vec4 textureColor = texture2D(rawTex, gl_TexCoord[0].st);
textureColor.rgb = color;
gl_FragColor = applyContrastAlphaBrightness(textureColor, alpha, brightness, contrast);
gl_FragColor = applyColorModifiers(textureColor, modifiers);
}

View file

@ -217,7 +217,7 @@ public abstract class AbstractGLColorMapDataFormat {
protected Buffer handleBufferSizing(GLColorMapData data, Buffer buffer,
int[] dimensions) {
int sliceWidth = dimensions[0] * getValuesPerPixel();
int sliceHeight = dimensions[1];
int sliceHeight = dimensions.length > 1 ? dimensions[1] : 1;
int paddedSliceWidth = getAlignedWidth(sliceWidth);
int totalDataSize = buffer.capacity();

View file

@ -54,6 +54,10 @@ public class GLColorMapData {
this.dimensions = dimensions;
}
public AbstractGLColorMapDataFormat getDataFormat() {
return dataFormat;
}
public int getTextureFormat() {
return dataFormat.getTextureFormat();
}
@ -93,6 +97,10 @@ public class GLColorMapData {
return dimensions[index];
}
public int getNumDimensions() {
return dimensions.length;
}
public int[] getDimensions() {
return dimensions;
}

View file

@ -21,9 +21,11 @@ package com.raytheon.viz.core.gl.ext.imaging;
import java.nio.ByteBuffer;
import javax.measure.unit.Unit;
import javax.media.opengl.GL;
import com.raytheon.uf.common.colormap.image.ColorMapData;
import com.raytheon.uf.common.colormap.image.ColorMapData.ColorMapDataType;
import com.raytheon.uf.common.colormap.prefs.ColorMapParameters;
import com.raytheon.uf.viz.core.PixelCoverage;
import com.raytheon.uf.viz.core.data.IColorMapDataRetrievalCallback;
@ -32,10 +34,15 @@ import com.raytheon.uf.viz.core.drawables.IImage;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.drawables.ext.colormap.IColormappedImageExtension;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.core.gl.dataformat.GLByteDataFormat;
import com.raytheon.viz.core.gl.ext.imaging.GLDataMappingFactory.GLDataMapping;
import com.raytheon.viz.core.gl.glsl.AbstractGLSLImagingExtension;
import com.raytheon.viz.core.gl.glsl.GLSLStructFactory;
import com.raytheon.viz.core.gl.glsl.GLShaderProgram;
import com.raytheon.viz.core.gl.images.AbstractGLColormappedImage;
import com.raytheon.viz.core.gl.images.AbstractGLImage;
import com.raytheon.viz.core.gl.images.GLBufferCMTextureData;
import com.raytheon.viz.core.gl.images.GLCMTextureData;
import com.raytheon.viz.core.gl.images.GLColormappedImage;
import com.raytheon.viz.core.gl.objects.GLTextureObject;
@ -53,7 +60,8 @@ import com.raytheon.viz.core.gl.objects.GLTextureObject;
* Feb 14, 2013 1616 bsteffen Add option for interpolation of colormap
* parameters, disable colormap interpolation
* by default.
* Oct 16, 2013 2333 mschenke Cleaned up load shader method, used isScaled
* Oct 16, 2013 2333 mschenke Cleaned up load shader method, used isScaled.
* Added support for colormapping in non-data unit.
*
* </pre>
*
@ -65,7 +73,7 @@ public class GLColormappedImageExtension extends AbstractGLSLImagingExtension
implements IColormappedImageExtension {
private static class GLColormappedImageExtensionData {
public GLColormappedImage alphaMaskTexture;
public GLCMTextureData alphaMask;
}
/*
@ -108,31 +116,10 @@ public class GLColormappedImageExtension extends AbstractGLSLImagingExtension
return null;
}
if (usedColorMapParameters.isUseMask()) {
final byte[] mask = usedColorMapParameters.getAlphaMask();
data.alphaMaskTexture = initializeRaster(
new IColorMapDataRetrievalCallback() {
@Override
public ColorMapData getColorMapData()
throws VizException {
return new ColorMapData(ByteBuffer.wrap(mask),
new int[] { mask.length, 1 });
}
}, usedColorMapParameters);
data.alphaMaskTexture.stage();
data.alphaMaskTexture.target(target);
}
// Get and stage colormap texture
GLTextureObject cmapTexture = target
.getColorMapTexture(usedColorMapParameters);
if (data.alphaMaskTexture != null) {
gl.glActiveTexture(GL.GL_TEXTURE2);
gl.glBindTexture(data.alphaMaskTexture.getTextureStorageType(),
data.alphaMaskTexture.getTextureid());
}
gl.glActiveTexture(GL.GL_TEXTURE1);
cmapTexture.bind(gl, GL.GL_TEXTURE_1D);
@ -147,10 +134,97 @@ public class GLColormappedImageExtension extends AbstractGLSLImagingExtension
gl.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MAG_FILTER,
GL.GL_NEAREST);
}
if (usedColorMapParameters.isUseMask()) {
data.alphaMask = setupAlphaMasking(gl, GL.GL_TEXTURE2,
usedColorMapParameters.getAlphaMask());
}
setupDataMapping(gl, glImage, GL.GL_TEXTURE3, GL.GL_TEXTURE4);
}
return data;
}
/**
* Sets up a {@link GLCMTextureData} for an alpha mask for use in image
* rendering
*
* @param gl
* @param maskTexBinding
* @param mask
* @return The GLCMTextureData the alpha mask is bound to or null if the
* texture failed to initialize
* @throws VizException
*/
public static GLCMTextureData setupAlphaMasking(GL gl, int maskTexBinding,
byte[] mask) throws VizException {
GLBufferCMTextureData maskData = new GLBufferCMTextureData(
new ColorMapData(ByteBuffer.wrap(mask),
new int[] { mask.length }, ColorMapDataType.BYTE),
new GLByteDataFormat());
gl.glActiveTexture(maskTexBinding);
if (maskData.loadTexture(gl)) {
gl.glBindTexture(maskData.getTextureStorageType(),
maskData.getTexId());
} else {
maskData.dispose();
maskData = null;
}
return maskData;
}
/**
* Sets up a {@link GLDataMapping} for use in image rendering
*
* @param gl
* @param glImage
* @param dataMappedTexBinding
* @param colorMappedTexBinding
* @throws VizException
*/
public static void setupDataMapping(GL gl,
AbstractGLColormappedImage glImage, int dataMappedTexBinding,
int colorMappedTexBinding) throws VizException {
ColorMapParameters colorMapParameters = glImage.getColorMapParameters();
// Get GLDataMapping and generate if datamapping is not set. If
// datamapping is not set, the data has already been mapped to
// colorMapUnits and we need not do anything
GLDataMapping dataMapping = glImage.getDataMapping();
if (dataMapping == null && colorMapParameters.getDataMapping() == null) {
Unit<?> colorMapUnit = colorMapParameters.getColorMapUnit();
Unit<?> dataUnit = colorMapParameters.getDataUnit();
int colorMapSize = colorMapParameters.getColorMap().getSize();
float colorMapMin = colorMapParameters.getColorMapMin();
float colorMapMax = colorMapParameters.getColorMapMax();
dataMapping = GLDataMappingFactory.constructGLDataMapping(gl,
dataUnit, colorMapUnit, colorMapMin, colorMapMax,
colorMapSize);
glImage.setDataMapping(dataMapping);
}
if (dataMapping != null && dataMapping.isValid()) {
GLCMTextureData glDataMapping = dataMapping.getDataMapping();
gl.glActiveTexture(dataMappedTexBinding);
if (glDataMapping.isLoaded() == false) {
glDataMapping.loadTexture(gl);
}
if (glDataMapping.isLoaded()) {
gl.glBindTexture(glDataMapping.getTextureStorageType(),
glDataMapping.getTexId());
}
GLCMTextureData glColorMapping = dataMapping.getColorMapping();
gl.glActiveTexture(colorMappedTexBinding);
if (glColorMapping.isLoaded() == false) {
glColorMapping.loadTexture(gl);
}
if (glColorMapping.isLoaded()) {
gl.glBindTexture(glColorMapping.getTextureStorageType(),
glColorMapping.getTexId());
}
}
}
/*
* (non-Javadoc)
*
@ -164,16 +238,22 @@ public class GLColormappedImageExtension extends AbstractGLSLImagingExtension
AbstractGLImage image, Object data) throws VizException {
GLColormappedImageExtensionData imageData = (GLColormappedImageExtensionData) data;
GL gl = target.getGl();
if (imageData.alphaMaskTexture != null) {
gl.glActiveTexture(GL.GL_TEXTURE2);
gl.glBindTexture(
imageData.alphaMaskTexture.getTextureStorageType(), 0);
imageData.alphaMaskTexture.dispose();
}
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_1D, 0);
if (imageData.alphaMask != null) {
gl.glActiveTexture(GL.GL_TEXTURE2);
gl.glBindTexture(imageData.alphaMask.getTextureStorageType(), 0);
imageData.alphaMask.dispose();
}
gl.glActiveTexture(GL.GL_TEXTURE3);
gl.glBindTexture(GL.GL_TEXTURE_1D, 0);
gl.glActiveTexture(GL.GL_TEXTURE4);
gl.glBindTexture(GL.GL_TEXTURE_1D, 0);
}
/*
@ -210,40 +290,22 @@ public class GLColormappedImageExtension extends AbstractGLSLImagingExtension
ColorMapParameters colorMapParameters = image.getColorMapParameters();
program.setUniform("colorMapSz", colorMapParameters.getColorMap()
.getSize());
boolean isScaled = image.isImageFormatScaled();
double dataMin = colorMapParameters.getDataMin();
double dataMax = colorMapParameters.getDataMax();
if (isScaled) {
// get format from image and get data min/max from it
dataMin = image.getDataMin();
dataMax = image.getDataMax();
GLSLStructFactory.createDataTexture(program, "rawData", 0,
image.getDataFormat(), colorMapParameters.getNoDataValue());
int numMappingValues = 0;
GLDataMapping mapping = image.getDataMapping();
if (mapping != null && mapping.isValid()) {
numMappingValues = mapping.getNumMappingValues();
}
GLSLStructFactory.createDataMapping(program, "dataMapping", 3, 4,
numMappingValues);
double cmapMin = colorMapParameters.getColorMapMin();
double cmapMax = colorMapParameters.getColorMapMax();
GLSLStructFactory.createColorMapping(program, "colorMapping", 1, 2,
colorMapParameters);
program.setUniform("isFloat", !isScaled);
program.setUniform("logarithmic",
colorMapParameters.isLogarithmic() ? 1 : 0);
program.setUniform("logFactor", colorMapParameters.getLogFactor());
program.setUniform("mirror", colorMapParameters.isMirror() ? 1 : 0);
program.setUniform("applyMask", colorMapParameters.isUseMask() ? 1 : 0);
program.setUniform("naturalMin", dataMin);
program.setUniform("naturalMax", dataMax);
program.setUniform("cmapMin", cmapMin);
program.setUniform("cmapMax", cmapMax);
program.setUniform("alphaMask", 2);
program.setUniform("colorMap", 1);
program.setUniform("rawText", 0);
program.setUniform("brightness", image.getBrightness());
program.setUniform("contrast", image.getContrast());
program.setUniform("alpha", paintProps.getAlpha());
GLSLStructFactory.createColorModifiers(program, "modifiers",
paintProps.getAlpha(), image.getBrightness(),
image.getContrast());
}
}

View file

@ -0,0 +1,343 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.core.gl.ext.imaging;
import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.measure.converter.UnitConverter;
import javax.measure.unit.Unit;
import javax.media.opengl.GL;
import com.raytheon.uf.common.colormap.image.ColorMapData;
import com.raytheon.viz.core.gl.dataformat.GLBufferColorMapData;
import com.raytheon.viz.core.gl.dataformat.GLFloatDataFormat;
import com.raytheon.viz.core.gl.images.GLBufferCMTextureData;
import com.raytheon.viz.core.gl.images.GLCMTextureData;
/**
* Factory class for creation {@link GLDataMapping} objects that can convert
* between units
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 24, 2013 2492 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class GLDataMappingFactory {
/**
* Key for {@link GLDataMappingFactory#mappingCache}. Stores fields needed
* to generate unique mappings
*
* @author mschenke
*/
private static class GLDataMappingKey {
private final Unit<?> dataUnit;
private final Unit<?> colorMapUnit;
private final float colorMapMin;
private final float colorMapMax;
private final int colorMapSize;
public GLDataMappingKey(Unit<?> dataUnit, Unit<?> colorMapUnit,
float colorMapMin, float colorMapMax, int colorMapSize) {
this.dataUnit = dataUnit;
this.colorMapUnit = colorMapUnit;
this.colorMapMin = colorMapMin;
this.colorMapMax = colorMapMax;
this.colorMapSize = colorMapSize;
}
public Unit<?> getDataUnit() {
return dataUnit;
}
public Unit<?> getColorMapUnit() {
return colorMapUnit;
}
public float getColorMapMin() {
return colorMapMin;
}
public float getColorMapMax() {
return colorMapMax;
}
public int getColorMapSize() {
return colorMapSize;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(colorMapMax);
result = prime * result + Float.floatToIntBits(colorMapMin);
result = prime * result + colorMapSize;
result = prime * result
+ ((colorMapUnit == null) ? 0 : colorMapUnit.hashCode());
result = prime * result
+ ((dataUnit == null) ? 0 : dataUnit.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GLDataMappingKey other = (GLDataMappingKey) obj;
if (Float.floatToIntBits(colorMapMax) != Float
.floatToIntBits(other.colorMapMax))
return false;
if (Float.floatToIntBits(colorMapMin) != Float
.floatToIntBits(other.colorMapMin))
return false;
if (colorMapSize != other.colorMapSize)
return false;
if (colorMapUnit == null) {
if (other.colorMapUnit != null)
return false;
} else if (!colorMapUnit.equals(other.colorMapUnit))
return false;
if (dataUnit == null) {
if (other.dataUnit != null)
return false;
} else if (!dataUnit.equals(other.dataUnit))
return false;
return true;
}
}
/**
* GL data mapping object, represents a mapping between units for use in GL
*
* @author mschenke
*/
public static class GLDataMapping {
private final GLDataMappingKey key;
private GLCMTextureData colorMapping;
private GLCMTextureData dataMapping;
private int numMappingValues;
private int refCount = 1;
private boolean initialized = false;
public GLDataMapping(GLDataMappingKey key) {
this.key = key;
}
public GLCMTextureData getColorMapping() {
return colorMapping;
}
public GLCMTextureData getDataMapping() {
return dataMapping;
}
public int getNumMappingValues() {
return numMappingValues;
}
public boolean isValid() {
return numMappingValues > 0 && colorMapping != null
&& dataMapping != null;
}
public void dispose() {
synchronized (mappingCache) {
refCount -= 1;
if (refCount <= 0) {
mappingCache.remove(key);
if (colorMapping != null) {
colorMapping.dispose();
}
if (dataMapping != null) {
dataMapping.dispose();
}
refCount = 0;
}
}
}
private void use() {
if (refCount == 0) {
throw new IllegalStateException(
"GLDataMapping has already been disposed");
}
refCount += 1;
}
private synchronized void initialize(GL gl) {
if (initialized) {
return;
}
Unit<?> dataUnit = key.getDataUnit();
Unit<?> colorMapUnit = key.getColorMapUnit();
int colorMapSize = key.getColorMapSize();
double colorMapMin = key.getColorMapMin();
double colorMapMax = key.getColorMapMax();
int numMappings = 0;
if (dataUnit != null && colorMapUnit != null
&& dataUnit.equals(colorMapUnit) == false
&& dataUnit.isCompatible(colorMapUnit)) {
// Worst case scenario, one mapping per color
double[] colorMapping = new double[colorMapSize];
Arrays.fill(colorMapping, Float.NaN);
double[] dataMapping = new double[colorMapping.length];
Arrays.fill(dataMapping, Float.NaN);
UnitConverter colorMapToData = colorMapUnit
.getConverterTo(dataUnit);
double dataMin = colorMapToData.convert(colorMapMin);
double dataMax = colorMapToData.convert(colorMapMax);
colorMapping[0] = colorMapMin;
colorMapping[colorMapping.length - 1] = colorMapMax;
dataMapping[0] = dataMin;
dataMapping[dataMapping.length - 1] = dataMax;
numMappings = 2;
if (colorMapToData.isLinear() == false) {
// Populate the dataMapping/colorMapping arrays
double increment = (colorMapMax - colorMapMin)
/ (colorMapping.length - 1);
for (int i = 1; i < colorMapping.length - 1; ++i) {
colorMapping[i] = colorMapMin + (i * increment);
dataMapping[i] = colorMapToData
.convert(colorMapping[i]);
}
// Search for linearness in the dataMappings.
int currEndIndex = 1;
double currEndValue = dataMapping[currEndIndex];
float currDelta = (float) (currEndValue - dataMapping[0]);
for (int i = 2; i < dataMapping.length; ++i) {
double nextValue = dataMapping[i];
float nextDelta = (float) ((nextValue - currEndValue) / (i - currEndIndex));
if (nextDelta == currDelta) {
// Remove linear entries
dataMapping[currEndIndex] = colorMapping[currEndIndex] = Double.NaN;
currEndValue = nextValue;
currEndIndex = i;
} else {
// Non-linear entry found, add mapping
numMappings += 1;
currEndIndex = i;
currEndValue = nextValue;
currDelta = nextDelta;
}
}
}
// Condense the mapping arrays removing nans
float[] condensedColorMapping = new float[numMappings];
float[] condensedDataMapping = new float[numMappings];
int index = 0;
for (int i = 0; i < colorMapSize && index < numMappings; ++i) {
double colorMapVal = colorMapping[i];
double dataMapVal = dataMapping[i];
if (Double.isNaN(colorMapVal) == false
&& Double.isNaN(dataMapVal) == false) {
condensedColorMapping[index] = (float) colorMapVal;
condensedDataMapping[index] = (float) dataMapVal;
index += 1;
}
}
if (index == numMappings) {
this.numMappingValues = numMappings;
this.colorMapping = new GLBufferCMTextureData(
new GLBufferColorMapData(new ColorMapData(
FloatBuffer.wrap(condensedColorMapping),
new int[] { numMappings }),
new GLFloatDataFormat()));
this.dataMapping = new GLBufferCMTextureData(
new GLBufferColorMapData(new ColorMapData(
FloatBuffer.wrap(condensedDataMapping),
new int[] { numMappings }),
new GLFloatDataFormat()));
}
}
initialized = true;
}
}
private static Map<GLDataMappingKey, GLDataMapping> mappingCache = new HashMap<GLDataMappingKey, GLDataMapping>();
/**
* Creates a {@link GLDataMapping} object given the dataUnit, colorMapUnit,
* color map min/max, and size of the colormap. Object must be disposed of
* when no longer used
*
* @param gl
* @param dataUnit
* @param colorMapUnit
* @param colorMapMin
* @param colorMapMax
* @param colorMapSize
* @return
*/
public static GLDataMapping constructGLDataMapping(GL gl, Unit<?> dataUnit,
Unit<?> colorMapUnit, float colorMapMin, float colorMapMax,
int colorMapSize) {
GLDataMapping mapping;
synchronized (mappingCache) {
GLDataMappingKey key = new GLDataMappingKey(dataUnit, colorMapUnit,
colorMapMin, colorMapMax, colorMapSize);
mapping = mappingCache.get(key);
if (mapping == null) {
mapping = new GLDataMapping(key);
mappingCache.put(key, mapping);
} else {
mapping.use();
}
}
mapping.initialize(gl);
return mapping;
}
}

View file

@ -22,6 +22,7 @@ package com.raytheon.viz.core.gl.ext.imaging;
import com.raytheon.uf.viz.core.drawables.IImage;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.core.gl.glsl.GLSLStructFactory;
import com.raytheon.viz.core.gl.glsl.GLShaderProgram;
import com.raytheon.viz.core.gl.images.AbstractGLImage;
@ -34,7 +35,9 @@ import com.raytheon.viz.core.gl.images.AbstractGLImage;
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 16, 2011 mschenke Initial creation
* Dec 16, 2011 mschenke Initial creation
* Nov 4, 2013 2492 mschenke Switched to use GLSLStructFactory for
* common shader structure use
*
* </pre>
*
@ -76,10 +79,10 @@ public class GLDefaultImagingExtension extends AbstractGLImagingExtension {
}
image = (AbstractGLImage) iimage;
program.setUniform("alpha", paintProps.getAlpha());
program.setUniform("brightness", image.getBrightness());
program.setUniform("contrast", image.getContrast());
program.setUniform("rawTex", 0);
GLSLStructFactory.createColorModifiers(program, "modifiers",
paintProps.getAlpha(), image.getBrightness(),
image.getContrast());
}
}

View file

@ -27,6 +27,7 @@ import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.drawables.ext.ISingleColorImageExtension;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.core.gl.glsl.AbstractGLSLImagingExtension;
import com.raytheon.viz.core.gl.glsl.GLSLStructFactory;
import com.raytheon.viz.core.gl.glsl.GLShaderProgram;
import com.raytheon.viz.core.gl.images.GLSingleColorImage;
@ -40,7 +41,9 @@ import com.raytheon.viz.core.gl.images.GLSingleColorImage;
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 15, 2011 mschenke Initial creation
* Dec 15, 2011 mschenke Initial creation
* Nov 4, 2013 2492 mschenke Switched to use GLSLStructFactory for
* common shader structure use
*
* </pre>
*
@ -97,11 +100,11 @@ public class GLSingleColorImageExtension extends AbstractGLSLImagingExtension
image = (GLSingleColorImage) iimage;
program.setUniform("brightness", image.getBrightness());
program.setUniform("contrast", image.getContrast());
program.setUniform("alpha", paintProps.getAlpha());
program.setUniform("color", image.getColor());
program.setUniform("rawTex", 0);
GLSLStructFactory.createColorModifiers(program, "modifiers",
paintProps.getAlpha(), image.getBrightness(),
image.getContrast());
}
}

View file

@ -0,0 +1,133 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.core.gl.glsl;
import com.raytheon.uf.common.colormap.prefs.ColorMapParameters;
import com.raytheon.viz.core.gl.dataformat.AbstractGLColorMapDataFormat;
/**
* Factory for creating GLSL struct mappings
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 31, 2013 2492 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class GLSLStructFactory {
private static final String FIELD_SEPERATOR = ".";
/**
* Creates a DataTexture structure in the program with the given name
*
* @param program
* @param name
* @param texBinding
* @param dataFormat
* @param noDataValue
*/
public static void createDataTexture(GLShaderProgram program, String name,
int texBinding, AbstractGLColorMapDataFormat dataFormat,
double noDataValue) {
setFieldUniform(program, name, "rawTex", texBinding);
setFieldUniform(program, name, "noDataValue", noDataValue);
setFieldUniform(program, name, "isScaled", dataFormat.isScaled());
if (dataFormat.isScaled()) {
setFieldUniform(program, name, "scaleMin",
dataFormat.getDataFormatMin());
setFieldUniform(program, name, "scaleMax",
dataFormat.getDataFormatMax());
}
}
/**
* Creates a DataMapping structure in the program with the given name
*
* @param program
* @param name
* @param dataMappingTexBinding
* @param colorMappingTexBinding
* @param numMappingValues
*/
public static void createDataMapping(GLShaderProgram program, String name,
int dataMappingTexBinding, int colorMappingTexBinding,
int numMappingValues) {
setFieldUniform(program, name, "dataMappingValues",
dataMappingTexBinding);
setFieldUniform(program, name, "colorMappingValues",
colorMappingTexBinding);
setFieldUniform(program, name, "numMappingValues", numMappingValues);
}
/**
* Creates a ColorMapping structure in the program with the given name
*
* @param program
* @param name
* @param colorMapTexBinding
* @param alphaMaskTexBinding
* @param parameters
*/
public static void createColorMapping(GLShaderProgram program, String name,
int colorMapTexBinding, int alphaMaskTexBinding,
ColorMapParameters parameters) {
setFieldUniform(program, name, "colorMap", colorMapTexBinding);
setFieldUniform(program, name, "cmapMin", parameters.getColorMapMin());
setFieldUniform(program, name, "cmapMax", parameters.getColorMapMax());
setFieldUniform(program, name, "applyMask", parameters.isUseMask());
setFieldUniform(program, name, "alphaMask", alphaMaskTexBinding);
setFieldUniform(program, name, "isMirrored", parameters.isMirror());
setFieldUniform(program, name, "isLogarithmic",
parameters.isLogarithmic());
setFieldUniform(program, name, "logFactor", parameters.getLogFactor());
}
/**
* Creates a ColorModifiers structure in the program with the given name
*
* @param program
* @param name
* @param alpha
* @param brightness
* @param contrast
*/
public static void createColorModifiers(GLShaderProgram program,
String name, float alpha, float brightness, float contrast) {
setFieldUniform(program, name, "alpha", alpha);
setFieldUniform(program, name, "brightness", brightness);
setFieldUniform(program, name, "contrast", contrast);
}
private static void setFieldUniform(GLShaderProgram program,
String structName, String fieldName, Object fieldValue) {
program.setUniform(structName + FIELD_SEPERATOR + fieldName, fieldValue);
}
}

View file

@ -155,7 +155,6 @@ public class GLShaderProgram {
gl.glUseProgram(0);
state = State.INITIALIZED;
}
loadedUniforms.clear();
}
/**
@ -358,6 +357,7 @@ public class GLShaderProgram {
glslContext = -1;
}
state = State.INVALID;
loadedUniforms.clear();
}
}

View file

@ -26,6 +26,8 @@ import com.raytheon.uf.common.colormap.prefs.ColorMapParameters;
import com.raytheon.uf.viz.core.drawables.IColormappedImage;
import com.raytheon.uf.viz.core.drawables.ext.IImagingExtension;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.core.gl.dataformat.AbstractGLColorMapDataFormat;
import com.raytheon.viz.core.gl.ext.imaging.GLDataMappingFactory.GLDataMapping;
import com.sun.opengl.util.texture.TextureCoords;
/**
@ -52,6 +54,8 @@ public abstract class AbstractGLColormappedImage extends AbstractGLImage
protected GLCMTextureData data;
private GLDataMapping dataMapping;
public AbstractGLColormappedImage(GLCMTextureData data,
ColorMapParameters params,
Class<? extends IImagingExtension> extensionClass) {
@ -143,6 +147,15 @@ public abstract class AbstractGLColormappedImage extends AbstractGLImage
return data.getTexId();
}
/**
* Returns the GL format of the texture data
*
* @return
*/
public AbstractGLColorMapDataFormat getDataFormat() {
return data.getDataFormat();
}
/**
* the absolute minimum value of a pixel in this image. {@link Double#NaN}
* if no absolute minimum exists
@ -233,6 +246,18 @@ public abstract class AbstractGLColormappedImage extends AbstractGLImage
data.dispose();
data = null;
}
if (dataMapping != null) {
dataMapping.dispose();
dataMapping = null;
}
}
public void setDataMapping(GLDataMapping dataMapping) {
this.dataMapping = dataMapping;
}
public GLDataMapping getDataMapping() {
return dataMapping;
}
/*

View file

@ -0,0 +1,138 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.core.gl.images;
import java.nio.Buffer;
import javax.measure.unit.Unit;
import javax.media.opengl.GL;
import com.raytheon.uf.common.colormap.image.ColorMapData;
import com.raytheon.viz.core.gl.dataformat.AbstractGLColorMapDataFormat;
import com.raytheon.viz.core.gl.dataformat.GLBufferColorMapData;
/**
* {@link GLCMTextureData} backed by a {@link Buffer}. The initial functions in
* here were moved here from {@link GLRetrievableCMTextureData}
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 23, 2013 2492 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class GLBufferCMTextureData extends GLCMTextureData {
public GLBufferCMTextureData(ColorMapData data,
AbstractGLColorMapDataFormat format) {
this(new GLBufferColorMapData(data, format));
}
public GLBufferCMTextureData(GLBufferColorMapData data) {
super(data);
}
@Override
protected GLBufferColorMapData getDataObject() {
return (GLBufferColorMapData) super.getDataObject();
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.core.gl.images.GLCMTextureData#isStaged()
*/
@Override
public boolean isStaged() {
GLBufferColorMapData data = getDataObject();
// Override since we have our required data
return data != null && data.getData() != null;
}
@Override
public void dispose() {
super.dispose();
if (isStaged()) {
data = null;
}
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.core.gl.images.GLCMTextureData#uploadTexture2D(javax
* .media.opengl.GL, int, int, int)
*/
@Override
protected void createTexture2D(GL gl, int type, int w, int h) {
gl.glTexImage2D(type, 0, getTextureInternalFormat(), w, h, 0,
getTextureFormat(), getTextureType(), getDataObject().getData()
.rewind());
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.core.gl.images.GLCMTextureData#uploadTexture1D(javax
* .media.opengl.GL, int, int)
*/
@Override
protected void createTexture1D(GL gl, int type, int w) {
gl.glTexImage1D(type, 0, getTextureInternalFormat(), w, 0,
getTextureFormat(), getTextureType(), getDataObject().getData()
.rewind());
}
/**
* Returns the data value at the given coordinates. TODO: Add support for 1D
* texture sampling, change x,y to int[] index?
*
* @param x
* @param y
* @return
*/
public double getValue(int x, int y) {
double value = Double.NaN;
if (isStaged()) {
value = getDataObject().getValue(x, y).doubleValue();
}
return value;
}
/**
* Returns the {@link Unit} associated with the data
*
* @return the dataUnit
*/
public Unit<?> getDataUnit() {
GLBufferColorMapData data = getDataObject();
return data != null ? data.getDataUnit() : null;
}
}

View file

@ -23,6 +23,7 @@ import javax.media.opengl.GL;
import com.raytheon.uf.common.colormap.image.ColorMapData.ColorMapDataType;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.core.gl.dataformat.AbstractGLColorMapDataFormat;
import com.raytheon.viz.core.gl.dataformat.GLColorMapData;
import com.raytheon.viz.core.gl.objects.GLTextureObject;
@ -40,6 +41,7 @@ import com.raytheon.viz.core.gl.objects.GLTextureObject;
* format for offscreen textures.
* Oct 16, 2013 2333 mschenke Moved retrievable/Buffer parts out and
* into separate class.
* Oct 23, 2013 2492 mschenke Added support for 1D textures
*
* </pre>
*
@ -116,6 +118,9 @@ public class GLCMTextureData {
return false;
}
int type = getTextureStorageType();
if (type == GL.GL_NONE) {
throw new VizException("Unsupported dimension size for texture");
}
tex = new GLTextureObject(this);
tex.bind(gl, type);
@ -125,7 +130,8 @@ public class GLCMTextureData {
gl.glTexParameteri(type, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(type, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
if (isDataFormatScaled() && isDataFormatSigned()) {
boolean changeScaleAndBias = isDataFormatScaled() && isDataFormatSigned();
if (changeScaleAndBias) {
// GL maps signed data into the range -1 to 1, but gl trims
// this to a valid range of 0 to 1, essentially removing
// negative values. Adding a scale and bias remaps this from
@ -135,20 +141,23 @@ public class GLCMTextureData {
gl.glPixelTransferf(GL.GL_RED_BIAS, 0.5f);
}
int w = getDimensionSize(0);
int h = getDimensionSize(1);
if (type == GL.GL_TEXTURE_1D) {
createTexture1D(gl, type, getDimensionSize(0));
} else if (type == GL.GL_TEXTURE_2D) {
createTexture2D(gl, type, getDimensionSize(0), getDimensionSize(1));
}
createTexture2D(gl, type, w, h);
gl.glPixelTransferf(GL.GL_RED_SCALE, 1.0f);
gl.glPixelTransferf(GL.GL_RED_BIAS, 0.0f);
if (changeScaleAndBias) {
gl.glPixelTransferf(GL.GL_RED_SCALE, 1.0f);
gl.glPixelTransferf(GL.GL_RED_BIAS, 0.0f);
}
return true;
}
/**
* Creates a 2D texture for type, with width/height w/h. Texture object must
* be bound for this call to work using
* Creates a 2D texture for type, with width/height, w/h. Texture object
* must be bound for this call to work using
* {@link GLTextureObject#bind(GL, int)}
*
* @param gl
@ -163,6 +172,22 @@ public class GLCMTextureData {
getTextureFormat(), getTextureType(), null);
}
/**
* Creates a 1D texture for type, with width, w. Texture object must be
* bound for this call to work using {@link GLTextureObject#bind(GL, int)}
*
* @param gl
* @param type
* @param w
* @param h
*/
protected void createTexture1D(GL gl, int type, int w) {
// Allocate our space on the graphics card, no buffer to upload so it
// will be filled with default values initially (0s)
gl.glTexImage1D(type, 0, getTextureInternalFormat(), w, 0,
getTextureFormat(), getTextureType(), null);
}
/**
* Checks if texture data is staged. If false, a call to
* {@link #stageTexture()} is needed before texture can be loaded
@ -184,6 +209,15 @@ public class GLCMTextureData {
return tex != null && tex.isValid();
}
/**
* Returns the GL format of the texture data
*
* @return
*/
public AbstractGLColorMapDataFormat getDataFormat() {
return data.getDataFormat();
}
/**
* Returns the size of the dimension index passed in (0=width,1=height)
*
@ -272,12 +306,20 @@ public class GLCMTextureData {
}
/**
* The texture storage type of the data (TEXTURE_2D)
* The texture storage type of the data. Will return {@link GL#GL_NONE} if
* unsupported dimension is detected
*
* @return
*/
public int getTextureStorageType() {
return GL.GL_TEXTURE_2D;
switch (data.getNumDimensions()) {
case 1:
return GL.GL_TEXTURE_1D;
case 2:
return GL.GL_TEXTURE_2D;
default:
return GL.GL_NONE;
}
}
/**

View file

@ -38,8 +38,9 @@ import com.raytheon.viz.core.gl.internal.cache.ImageCache;
import com.raytheon.viz.core.gl.internal.cache.ImageCache.CacheType;
/**
* Object that represents a colormapped texture that can be unloaded and
* reloaded from main/graphics memory using a retrieval callback
* {@link GLCMTextureData} that's backed by a {@link Buffer} that represents a
* colormapped texture that is cacheable and can be unloaded and reloaded from
* main/graphics memory using a retrieval callback
*
* <pre>
*
@ -47,7 +48,8 @@ import com.raytheon.viz.core.gl.internal.cache.ImageCache.CacheType;
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 24, 2013 mschenke Initial creation
* Jun 24, 2013 mschenke Initial creation
* Oct 23, 2013 2492 mschenke Extracted Buffer backing into super class
*
* </pre>
*
@ -55,7 +57,7 @@ import com.raytheon.viz.core.gl.internal.cache.ImageCache.CacheType;
* @version 1.0
*/
public class GLRetrievableCMTextureData extends GLCMTextureData implements
public class GLRetrievableCMTextureData extends GLBufferCMTextureData implements
IImageCacheable {
private static Map<IColorMapDataRetrievalCallback, GLRetrievableCMTextureData> texMap = new HashMap<IColorMapDataRetrievalCallback, GLRetrievableCMTextureData>();
@ -95,11 +97,6 @@ public class GLRetrievableCMTextureData extends GLCMTextureData implements
this.callback = callback;
}
@Override
protected GLBufferColorMapData getDataObject() {
return (GLBufferColorMapData) super.getDataObject();
}
/*
* (non-Javadoc)
*
@ -224,18 +221,6 @@ public class GLRetrievableCMTextureData extends GLCMTextureData implements
return 0;
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.core.gl.images.GLCMTextureData#isStaged()
*/
@Override
public boolean isStaged() {
GLBufferColorMapData data = getDataObject();
// Override since we have our required data
return data != null && data.getData() != null;
}
public double getValue(int x, int y) {
GLBufferColorMapData data = getDataObject();
double value = Double.NaN;
@ -265,27 +250,4 @@ public class GLRetrievableCMTextureData extends GLCMTextureData implements
return value;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.core.gl.images.GLCMTextureData#uploadTexture2D(javax
* .media.opengl.GL, int, int, int)
*/
@Override
protected void createTexture2D(GL gl, int type, int w, int h) {
gl.glTexImage2D(type, 0, getTextureInternalFormat(), w, h, 0,
getTextureFormat(), getTextureType(), getDataObject().getData()
.rewind());
}
/**
* Returns the {@link Unit} associated with the data
*
* @return the dataUnit
*/
public Unit<?> getDataUnit() {
GLBufferColorMapData data = getDataObject();
return data != null ? data.getDataUnit() : null;
}
}

View file

@ -53,6 +53,7 @@ import org.geotools.coverage.grid.GeneralGridGeometry;
import com.raytheon.uf.common.colormap.ColorMap;
import com.raytheon.uf.common.colormap.IColorMap;
import com.raytheon.uf.common.colormap.image.ColorMapData;
import com.raytheon.uf.common.colormap.image.ColorMapData.ColorMapDataType;
import com.raytheon.uf.common.colormap.prefs.ColorMapParameters;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
@ -67,7 +68,6 @@ import com.raytheon.uf.viz.core.DrawableString;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IView;
import com.raytheon.uf.viz.core.data.IColorMapDataRetrievalCallback;
import com.raytheon.uf.viz.core.data.IRenderedImageCallback;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.IFont;
@ -86,11 +86,12 @@ import com.raytheon.viz.core.gl.GLDisposalManager;
import com.raytheon.viz.core.gl.GLStats;
import com.raytheon.viz.core.gl.IGLFont;
import com.raytheon.viz.core.gl.IGLTarget;
import com.raytheon.viz.core.gl.ext.imaging.GLColormappedImageExtension;
import com.raytheon.viz.core.gl.dataformat.GLByteDataFormat;
import com.raytheon.viz.core.gl.ext.imaging.GLDefaultImagingExtension;
import com.raytheon.viz.core.gl.glsl.GLSLFactory;
import com.raytheon.viz.core.gl.glsl.GLSLStructFactory;
import com.raytheon.viz.core.gl.glsl.GLShaderProgram;
import com.raytheon.viz.core.gl.images.GLColormappedImage;
import com.raytheon.viz.core.gl.images.GLBufferCMTextureData;
import com.raytheon.viz.core.gl.images.GLImage;
import com.raytheon.viz.core.gl.objects.GLTextureObject;
import com.sun.opengl.util.Screenshot;
@ -128,6 +129,7 @@ import com.sun.opengl.util.j2d.TextRenderer;
* strings are always readable despite extent
* May 28, 2013 1638 mschenke Made sure {@link TextStyle#BLANKED} text is drawing correct size
* box around text
* Nov 4, 2013 2492 mschenke Switched colormap drawing to use 1D texture object for alpha mask
*
* </pre>
*
@ -517,28 +519,19 @@ public class GLTarget extends AbstractGraphicsTarget implements IGLTarget {
GLTextureObject i = getColorMapTexture(colorMapParams);
GLColormappedImage alphaMaskTexture = null;
GLBufferCMTextureData alphaMaskTexture = null;
if (colorMapParams.isUseMask() && capabilities.cardSupportsShaders) {
final byte[] mask = colorMapParams.getAlphaMask();
alphaMaskTexture = getExtension(
GLColormappedImageExtension.class).initializeRaster(
new IColorMapDataRetrievalCallback() {
@Override
public ColorMapData getColorMapData()
throws VizException {
return new ColorMapData(ByteBuffer.wrap(mask),
new int[] { mask.length, 1 });
}
}, colorMapParams);
alphaMaskTexture.stage();
alphaMaskTexture.target(this);
}
if (alphaMaskTexture != null) {
byte[] mask = colorMapParams.getAlphaMask();
alphaMaskTexture = new GLBufferCMTextureData(new ColorMapData(
ByteBuffer.wrap(mask), new int[] { mask.length },
ColorMapDataType.BYTE), new GLByteDataFormat());
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(alphaMaskTexture.getTextureStorageType(),
alphaMaskTexture.getTextureid());
if (alphaMaskTexture.loadTexture(gl)) {
gl.glBindTexture(alphaMaskTexture.getTextureStorageType(),
alphaMaskTexture.getTexId());
} else {
alphaMaskTexture.dispose();
}
}
gl.glPolygonMode(GL.GL_BACK, GL.GL_FILL);
@ -573,14 +566,11 @@ public class GLTarget extends AbstractGraphicsTarget implements IGLTarget {
"colormap");
if (program != null) {
program.startShader();
program.setUniform("alphaVal", blendAlpha);
program.setUniform("brightness", brightness);
program.setUniform("contrast", contrast);
program.setUniform("colorMap", 0);
program.setUniform("logFactor", logFactor);
program.setUniform("alphaMask", 1);
program.setUniform("applyMask",
colorMapParams.isUseMask() ? 1 : 0);
GLSLStructFactory.createColorMapping(program,
"colorMapping", 0, 1, colorMapParams);
GLSLStructFactory.createColorModifiers(program,
"modifiers", blendAlpha, brightness, contrast);
program.setUniform("bkgrndRed",
backgroundColor.red / 255.0f);
program.setUniform("bkgrndGreen",

View file

@ -25,7 +25,6 @@ import java.text.DecimalFormat;
import java.util.Arrays;
import javax.measure.converter.UnitConverter;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
@ -40,10 +39,10 @@ import com.raytheon.uf.common.datastorage.IDataStore;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
import com.raytheon.uf.common.style.LabelingPreferences;
import com.raytheon.uf.common.style.ParamLevelMatchCriteria;
import com.raytheon.uf.common.style.StyleException;
import com.raytheon.uf.common.style.StyleManager;
import com.raytheon.uf.common.style.StyleManager.StyleType;
import com.raytheon.uf.common.style.StyleRule;
import com.raytheon.uf.common.style.StyleException;
import com.raytheon.uf.common.style.image.DataScale;
import com.raytheon.uf.common.style.image.ImagePreferences;
import com.raytheon.uf.common.style.image.SamplePreferences;
@ -148,7 +147,8 @@ public class TopoResource extends
// Set data unit, specify in resource data? Look up in data record?
params.setDataUnit(SI.METER);
params.setDisplayUnit(NonSI.FOOT);
params.setDisplayUnit(SI.METER);
params.setColorMapUnit(SI.METER);
params.setColorMapMin(-19);
params.setColorMapMax(5000);
params.setDataMin(Short.MIN_VALUE);
@ -169,15 +169,17 @@ public class TopoResource extends
DataScale scale = prefs.getDataScale();
if (scale != null) {
UnitConverter displayToColorMap = params
.getDisplayToColorMapConverter();
Double minVal = scale.getMinValue();
Double maxVal = scale.getMaxValue();
if (minVal != null) {
params.setColorMapMin((float) params
.getDisplayToDataConverter().convert(minVal));
params.setColorMapMin((float) displayToColorMap
.convert(minVal));
}
if (maxVal != null) {
params.setColorMapMax((float) params
.getDisplayToDataConverter().convert(maxVal));
params.setColorMapMax((float) displayToColorMap
.convert(maxVal));
}
}

View file

@ -35,21 +35,27 @@ import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.List;
import javax.measure.converter.UnitConverter;
import javax.measure.unit.Unit;
import com.raytheon.uf.common.colormap.Color;
import com.raytheon.uf.common.colormap.IColorMap;
import com.raytheon.uf.common.colormap.image.ColorMapData.ColorMapDataType;
import com.raytheon.uf.common.colormap.prefs.ColorMapParameters;
/**
* Colormapper class
* Colormapper class, written to mimic colormapRaster.glsl in java. Any changes
* to files mapping.glsl or colormapRaster.glsl will probably need to be
* reflected here
*
* <pre>
*
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 13, 2010 mschenke Initial creation
* Aug 13, 2010 mschenke Initial creation
* Feb 15, 2013 1638 mschenke Moved IndexColorModel creation to common.colormap utility
* Nov 4, 2013 2492 mschenke Rewritten to model glsl equivalent
*
* </pre>
*
@ -77,39 +83,42 @@ public class Colormapper {
int width = cmapData.getDimensions()[0];
int height = cmapData.getDimensions()[1];
Buffer buf = cmapData.getBuffer();
ColorMapDataType dataType = cmapData.getDataType();
// Parameters ported from raster.glsl
boolean log = parameters.isLogarithmic();
double logFactor = parameters.getLogFactor();
boolean mirror = parameters.isMirror();
double cmapMin = parameters.getColorMapMin();
double cmapMax = parameters.getColorMapMax();
int colorMapSz = parameters.getColorMap().getSize();
double diff = Math.abs(cmapMax - cmapMin);
int dataSize = buf.capacity();
byte[] cmapedData = new byte[buf.capacity()];
ColorMapDataType dataType = cmapData.getDataType();
double noDataValue = parameters.getNoDataValue();
Unit<?> dataUnit = cmapData.getDataUnit();
if (dataUnit == null) {
dataUnit = parameters.getDataUnit();
}
Unit<?> colorMapUnit = parameters.getColorMapUnit();
UnitConverter converter = null;
if (dataUnit != null && colorMapUnit != null
&& parameters.getDataMapping() == null
&& dataUnit.equals(colorMapUnit) == false
&& dataUnit.isCompatible(colorMapUnit) == true) {
converter = dataUnit.getConverterTo(colorMapUnit);
}
int numColors = parameters.getColorMap().getSize();
byte[] indexArray = new byte[dataSize];
for (int i = 0; i < dataSize; ++i) {
double value = getValue(buf, i, dataType);
double index = 0.0f;
if (log) {
index = findIndexLog(value, logFactor, cmapMin, cmapMax, mirror);
} else {
index = ((value - cmapMin) / diff);
double dataValue = getDataValue(buf, i, dataType);
if (Double.isNaN(dataValue) || dataValue == noDataValue) {
// Skip, need equivalent of setting alpha to 0
continue;
}
if (index < 0.0) {
index = 0.0;
} else if (index > 1.0) {
index = 1.0;
double cmapValue = dataValue;
if (converter != null) {
cmapValue = converter.convert(dataValue);
}
cmapedData[i] = findColorIndex(index, logFactor, colorMapSz);
double index = getColorMappingIndex(cmapValue, parameters);
indexArray[i] = (byte) (capIndex(index) * (numColors - 1));
}
IndexColorModel cm = buildColorModel(parameters.getColorMap());
DataBufferByte byteArray = new DataBufferByte(cmapedData, width
DataBufferByte byteArray = new DataBufferByte(indexArray, width
* height);
MultiPixelPackedSampleModel sample = new MultiPixelPackedSampleModel(
@ -123,114 +132,6 @@ public class Colormapper {
return bi;
}
private static double getValue(Buffer buffer, int idx,
ColorMapDataType dataType) {
switch (dataType) {
case BYTE: {
return ((ByteBuffer) buffer).get(idx) & 0xFF;
}
case SIGNED_BYTE: {
return ((ByteBuffer) buffer).get(idx);
}
case SHORT: {
return ((ShortBuffer) buffer).get(idx);
}
case UNSIGNED_SHORT: {
return ((ShortBuffer) buffer).get(idx) & 0xFFFF;
}
case INT: {
return ((IntBuffer) buffer).get(idx);
}
case FLOAT: {
return ((FloatBuffer) buffer).get(idx);
}
}
return 0.0;
}
private static double findIndexLog(double value, double logFactor,
double cmapMin, double cmapMax, boolean mirror) {
double index = 0.0;
// is this strictly negative, strictly positive or neg to pos scaling?
if (cmapMin >= 0.0 && cmapMax >= 0.0 && mirror == false) {
if (value < cmapMin) {
index = 0.0;
} else {
// simple calculation
index = ((Math.log(value) - Math.log(cmapMin)) / Math.abs(Math
.log(cmapMax) - Math.log(cmapMin)));
}
} else if (cmapMin <= 0.0 && cmapMax <= 0.0 && mirror == false) {
index = ((Math.log(value) - Math.log(cmapMax)) / Math.abs(Math
.log(cmapMin) - Math.log(cmapMax)));
} else {
// special case, neg to pos:
double colorMapMin = cmapMin;
double colorMapMax = cmapMax;
double zeroVal = Math.max(colorMapMax, Math.abs(colorMapMin)) * 0.0001;
if (mirror && (colorMapMin > 0.0 || colorMapMax < 0.0)) {
if (colorMapMax < 0.0) {
colorMapMax = -cmapMax;
value = -value;
zeroVal = -colorMapMin;
} else {
zeroVal = cmapMin;
}
colorMapMin = -cmapMax;
}
double leftZero = 0.0;
double rightZero = 0.0;
double absLogZeroVal = Math.abs(Math.log(zeroVal));
rightZero = absLogZeroVal + Math.log(colorMapMax);
double cmapMax2 = Math.abs(colorMapMin);
leftZero = absLogZeroVal + Math.log(cmapMax2);
double zeroIndex = leftZero / (leftZero + rightZero);
// figure out index for texture val
double absTextureColor = Math.abs(value);
if (absTextureColor <= zeroVal) {
index = zeroIndex;
} else if (value > 0.0) {
// positive texture color value, find index from 0 to
// cmapMax:
double logTexColor = absLogZeroVal + Math.log(value);
double texIndex = logTexColor / rightZero;
index = (zeroIndex + ((1.0 - zeroIndex) * texIndex));
} else {
// negative texture color value, find index from 0 to
// cmapMax:
double logTexColor = absLogZeroVal + Math.log(absTextureColor);
double texIndex = logTexColor / leftZero;
index = (zeroIndex - (zeroIndex * texIndex));
}
}
return index;
}
private static byte findColorIndex(double index, double logFactor,
int colorMapSz) {
if (logFactor > 0.0) {
double minLog = Math.log(logFactor);
double maxLog = Math.log(logFactor + 1.0);
double lg = Math.log(logFactor + index);
index = (lg - minLog) / (maxLog - minLog);
if (index < 0.0) {
index = 0.0;
} else if (index > 1.0) {
index = 1.0;
}
}
return (byte) (index * (colorMapSz - 1));
}
/**
* Builds a color model from a color map
*
@ -256,4 +157,264 @@ public class Colormapper {
return new IndexColorModel(COLOR_MODEL_NUMBER_BITS, size, red, green,
blue, alpha);
}
/**
* Sets an index value into the indexArray for use in a Raster with
* IndexColorModel set
*
* @param dataArray
* @param idx
* @param idxValue
*/
public static void setIndexValue(Object dataArray, int idx, int idxValue) {
if (dataArray instanceof byte[]) {
((byte[]) dataArray)[idx] = (byte) idxValue;
} else if (dataArray instanceof short[]) {
((short[]) dataArray)[idx] = (short) idxValue;
} else if (dataArray instanceof int[]) {
((int[]) dataArray)[idx] = idxValue;
} else {
throw new IllegalArgumentException("Unsupported dataArray type: "
+ (dataArray != null ? dataArray.getClass() : null));
}
}
/**
* Returns the double representation of the data value for the Buffer at the
* given index
*
* @param buffer
* @param idx
* @param dataType
* @return
*/
public static double getDataValue(Buffer buffer, int idx,
ColorMapDataType dataType) {
switch (dataType) {
case BYTE: {
return ((ByteBuffer) buffer).get(idx) & 0xFF;
}
case SIGNED_BYTE: {
return ((ByteBuffer) buffer).get(idx);
}
case SHORT: {
return ((ShortBuffer) buffer).get(idx);
}
case UNSIGNED_SHORT: {
return ((ShortBuffer) buffer).get(idx) & 0xFFFF;
}
case INT: {
return ((IntBuffer) buffer).get(idx);
}
case FLOAT: {
return ((FloatBuffer) buffer).get(idx);
}
}
return 0.0;
}
/**
* This function takes an index value and caps it to the range 0-1
*/
public static double capIndex(double index) {
if (index < 0.0) {
index = 0.0;
} else if (index > 1.0) {
index = 1.0;
}
return index;
}
/**
* Given a colorMap value linearly determine the index into cmapMin/cmapMax
*
* @param cmapValue
* @param cmapMin
* @param cmapMax
* @return
*/
public static double getLinearIndex(double cmapValue, double cmapMin,
double cmapMax) {
return (cmapValue - cmapMin) / (cmapMax - cmapMin);
}
/**
* This function logarithmically finds the index for the cmapValue into
* cmapMin/cmapMax.
*
* @param cmapValue
* @param cmapMin
* @param cmapMax
* @param mirror
* @return
*/
public static double getLogIndex(double cmapValue, double cmapMin,
double cmapMax, boolean mirror) {
double index = 0.0;
// is this strictly negative, strictly positive or neg to pos scaling?
if (cmapMin >= 0.0 && cmapMax >= 0.0 && !mirror) {
if (cmapValue < cmapMin) {
index = 0.0;
} else {
// simple calculation
index = ((Math.log(cmapValue) - Math.log(cmapMin)) / Math
.abs(Math.log(cmapMax) - Math.log(cmapMin)));
}
} else if (cmapMin <= 0.0 && cmapMax <= 0.0 && !mirror) {
index = ((Math.log(cmapValue) - Math.log(cmapMax)) / Math.abs(Math
.log(cmapMin) - Math.log(cmapMax)));
} else {
// special case, neg to pos:
double colorMapMin = cmapMin;
double colorMapMax = cmapMax;
double zeroVal = Math.max(colorMapMax, Math.abs(colorMapMin)) * 0.0001;
if (mirror && (colorMapMin > 0.0 || colorMapMax < 0.0)) {
if (colorMapMax < 0.0) {
colorMapMax = -cmapMax;
cmapValue = -cmapValue;
zeroVal = -colorMapMin;
} else {
zeroVal = cmapMin;
}
colorMapMin = -cmapMax;
}
double leftZero = 0.0;
double rightZero = 0.0;
double absLogZeroVal = Math.abs(Math.log(zeroVal));
rightZero = absLogZeroVal + Math.log(colorMapMax);
double cmapMax2 = Math.abs(colorMapMin);
leftZero = absLogZeroVal + Math.log(cmapMax2);
double zeroIndex = leftZero / (leftZero + rightZero);
// figure out index for texture val
double absTextureColor = Math.abs(cmapValue);
if (absTextureColor <= zeroVal) {
index = zeroIndex;
} else if (cmapValue > 0.0) {
// positive texture color value, find index from 0 to
// cmapMax:
double logTexColor = absLogZeroVal + Math.log(cmapValue);
double texIndex = logTexColor / rightZero;
index = (zeroIndex + ((1.0 - zeroIndex) * texIndex));
} else {
// negative texture color value, find index from 0 to
// cmapMax:
double logTexColor = absLogZeroVal + Math.log(absTextureColor);
double texIndex = logTexColor / leftZero;
index = (zeroIndex - (zeroIndex * texIndex));
}
}
return index;
}
/**
* This function calculates a new index to use based on the logFactor and
* passed in index
*
* @param index
* @param logFactor
* @return
*/
public static double getLogFactorIndex(double index, double logFactor) {
if (logFactor > 0.0) {
double minLog = Math.log(logFactor);
double maxLog = Math.log(logFactor + 1.0);
double lg = Math.log(logFactor + index);
index = (lg - minLog) / (maxLog - minLog);
if (index < 0.0) {
index = 0.0;
} else if (index > 1.0) {
index = 1.0;
}
}
return index;
}
/**
* Returns an index for the cmapValue based on the
* {@link ColorMapParameters}
*
* @param cmapValue
* @param colorMapping
* @return
*/
public static double getColorMappingIndex(double cmapValue,
ColorMapParameters colorMapParameters) {
double logFactor = colorMapParameters.getLogFactor();
double cmapMin = colorMapParameters.getColorMapMin();
double cmapMax = colorMapParameters.getColorMapMax();
double index;
if (colorMapParameters.isLogarithmic()) {
index = getLogIndex(cmapValue, cmapMin, cmapMax,
colorMapParameters.isMirror());
} else {
index = getLinearIndex(cmapValue, cmapMin, cmapMax);
}
// Apply logFactor if set
if (logFactor > 0.0) {
index = getLogFactorIndex(index, logFactor);
}
return index;
}
/**
* Gets the {@link Color} from the color map in the parameters at the index
* (capped 0-1) passed in
*
* @param index
* @param colorMapParameters
* @return
*/
public static Color getColorByIndex(double index,
ColorMapParameters colorMapParameters) {
index = capIndex(index);
IColorMap colorMap = colorMapParameters.getColorMap();
if (colorMapParameters.isInterpolate()) {
index = 0.5f;
index = (index * (colorMap.getSize() - 1));
int lowIndex = (int) Math.floor(index);
int highIndex = (int) Math.ceil(index);
double lowWeight = highIndex - index;
double highWeight = 1.0f - lowWeight;
Color low = colorMap.getColors().get(lowIndex);
Color high = colorMap.getColors().get(highIndex);
float r = (float) (lowWeight * low.getRed() + highWeight
* high.getRed());
float g = (float) (lowWeight * low.getGreen() + highWeight
* high.getGreen());
float b = (float) (lowWeight * low.getBlue() + highWeight
* high.getBlue());
float a = (float) (lowWeight * low.getAlpha() + highWeight
* high.getAlpha());
return new Color(r, g, b, a);
} else {
return colorMap.getColors().get(
(int) (index * (colorMap.getSize() - 1)));
}
}
/**
* Returns a {@link Color} for the cmapValue based on the
* {@link ColorMapParameters}
*
* @param cmapValue
* @param colorMapParameters
* @return
*/
public static Color getColorByValue(double cmapValue,
ColorMapParameters colorMapParameters) {
return getColorByIndex(
getColorMappingIndex(cmapValue, colorMapParameters),
colorMapParameters);
}
}

View file

@ -22,7 +22,9 @@ package com.raytheon.uf.common.colormap.prefs;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.measure.converter.UnitConverter;
@ -35,8 +37,8 @@ import javax.xml.bind.annotation.XmlElement;
import com.raytheon.uf.common.colormap.AbstractColorMap;
import com.raytheon.uf.common.colormap.Color;
import com.raytheon.uf.common.colormap.IColorMap;
import com.raytheon.uf.common.colormap.image.Colormapper;
import com.raytheon.uf.common.colormap.prefs.DataMappingPreferences.DataMappingEntry;
import com.raytheon.uf.common.serialization.ISerializableObject;
/**
* Colormap Parameters
@ -56,14 +58,15 @@ import com.raytheon.uf.common.serialization.ISerializableObject;
* Jun 14, 2013 DR 16070 jgerth Utilize data mapping
* Aug 2, 2013 2211 mschenke Backed out 16070 changes, made
* dataUnit/imageUnit properly commutative.
*
* Nov 4, 2013 2492 mschenke Cleaned up variable naming to make purpose
* clear (image->colormap)
* </pre>
*
* @author chammack
* @version 1
*/
@XmlAccessorType(XmlAccessType.NONE)
public class ColorMapParameters implements Cloneable, ISerializableObject {
public class ColorMapParameters {
@XmlAccessorType(XmlAccessType.NONE)
public static class PersistedParameters {
@ -123,13 +126,13 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
protected Set<IColorMapParametersListener> listeners = new HashSet<IColorMapParametersListener>();
/** Units of the colormap parameters (min/max) */
/** Units colormapping should be displayed in */
protected Unit<?> displayUnit;
/** Units of the image pixel values */
protected Unit<?> imageUnit;
/** Units colormapping will occur in */
protected Unit<?> colorMapUnit;
/** Units of the data values */
/** Units of the data values to colormap */
protected Unit<?> dataUnit;
/** The maximum value used to apply the colormap */
@ -158,23 +161,23 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
@XmlAttribute
protected String colorMapName;
/** The converter that converts data values to display values * */
/** The converter that converts data values to {@link #displayUnit} * */
protected UnitConverter dataToDisplayConverter;
/** The converter that converts display values to data values * */
/** The converter that converts display values to {@link #dataUnit} * */
protected UnitConverter displayToDataConverter;
/** The converter that converts data values to image pixels */
protected UnitConverter dataToImageConverter;
/** The converter that converts data values to {@link #colorMapUnit} */
protected UnitConverter dataToColorMapConverter;
/** The converter that converts image pixels to data values */
protected UnitConverter imageToDataConverter;
/** The converter that converts color map unit values to {@link #dataUnit} */
protected UnitConverter colorMapToDataConverter;
/** The converter that converts image pixels to display values */
protected UnitConverter imageToDisplayConverter;
/** The converter that converts color map unit values to {@link #displayUnit} */
protected UnitConverter colorMapToDisplayConverter;
/** The converter that converts display values to image pixels */
protected UnitConverter displayToImageConverter;
/** The converter that converts display values to {@link #colorMapUnit} */
protected UnitConverter displayToColorMapConverter;
protected DataMappingPreferences dataMapping;
@ -201,7 +204,7 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
/** Specify whether the colormap should be interpolated */
protected boolean interpolate = false;
public static class LabelEntry {
public static class LabelEntry implements Comparable<LabelEntry> {
private float location;
private String text;
@ -258,8 +261,25 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(LabelEntry o) {
return Double.compare(getLocation(), o.getLocation());
}
}
/**
* Creates a {@link UnitConverter} converting the from unit to the to unit.
*
* @param from
* @param to
* @return The unit converter or null of units are not compatible
*/
private UnitConverter constructConverter(Unit<?> from, Unit<?> to) {
UnitConverter converter = null;
@ -270,22 +290,34 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
return converter;
}
private void addLabel(float dispValue, String s) {
float index = getIndexByValue(dispValue);
/**
* Adds a label for a {@link #displayUnit} value
*
* @param colorMapValue
* @param s
*/
private void addDisplayValueLabel(float dispValue, String s) {
float colorMapValue = dispValue;
UnitConverter displayToColorMap = getDisplayToColorMapConverter();
if (displayToColorMap != null) {
colorMapValue = (float) displayToColorMap.convert(dispValue);
}
addColorMapValueLabel(colorMapValue, s);
}
/**
* Adds a label for a {@link #colorMapUnit} value
*
* @param colorMapValue
* @param s
*/
private void addColorMapValueLabel(float colorMapValue, String s) {
double index = Colormapper.getColorMappingIndex(colorMapValue, this);
if (index > 1.0 || index < 0.0) {
return;
}
labels.add(new LabelEntry(s, index));
}
private void addLabel(int pixelValue, String s) {
float location = (float) pixelValue / 255;
if (location > 1.0 || location < 0.0) {
return;
}
labels.add(new LabelEntry(s, location));
labels.add(new LabelEntry(s, (float) index));
}
private void computeLabels() {
@ -298,7 +330,7 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
if (colorMapRange != 0.0) {
for (float label : colorBarIntervals) {
String s = format.format(label);
addLabel(label, s);
addDisplayValueLabel(label, s);
}
}
} else if (dataMapping != null) {
@ -309,14 +341,15 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
if (s == null) {
s = format.format(dispValue);
}
addLabel(dispValue, s);
addDisplayValueLabel(dispValue, s);
} else if (entry.getPixelValue() != null
&& !"NO DATA".equals(s)) {
double pixelValue = entry.getPixelValue();
addLabel((int) pixelValue, s);
float pixelValue = entry.getPixelValue().floatValue();
addColorMapValueLabel(pixelValue, s);
}
}
}
Collections.sort(labels);
recomputeLabels = false;
}
@ -357,22 +390,26 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* @return the unit
* Returns the display unit
*
* @return The unit colormapping should be displayed in
*/
public Unit<?> getDisplayUnit() {
return displayUnit;
}
/**
* Sets the display units
*
* @param unit
* the unit to set
* The unit colormapping should be displayed in
*/
public void setDisplayUnit(Unit<?> unit) {
this.displayUnit = unit;
recomputeLabels = true;
displayToImageConverter = null;
imageToDisplayConverter = null;
displayToColorMapConverter = null;
colorMapToDisplayConverter = null;
displayToDataConverter = null;
dataToDisplayConverter = null;
@ -380,25 +417,32 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* @return the colorMapMax
* Returns the maximum range value the colormapping occurs over
*
* @return the maximum range value in {@link #colorMapUnit}
*/
public float getColorMapMax() {
return colorMapMax;
}
/**
* Sets the maximum range value the colormapping occurs over
*
* @param colorMapMax
* the colorMapMax to set
* the maximum range value in {@link #colorMapUnit}
*/
public void setColorMapMax(float colorMapMax) {
setColorMapMax(colorMapMax, false);
}
/**
* Sets the maximum range value the colormapping occurs over
*
* @param colorMapMax
* the colorMapMax to set
* the maximum range value in {@link #colorMapUnit}
* @param persist
* specifies whether to persist this value when saved
* true indicates colorMapMax should be persisted through
* serialization
*/
public void setColorMapMax(float colorMapMax, boolean persist) {
this.colorMapMax = colorMapMax;
@ -410,25 +454,32 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* @return the colorMapMin
* Returns the minimum range value the colormapping occurs over
*
* @return the minimum range value in {@link #colorMapUnit}
*/
public float getColorMapMin() {
return colorMapMin;
}
/**
* Sets the minimum range value the colormapping occurs over
*
* @param colorMapMin
* the colorMapMin to set
* the minimum range value in {@link #colorMapUnit}
*/
public void setColorMapMin(float colorMapMin) {
setColorMapMin(colorMapMin, false);
}
/**
* Sets the minimum range value the colormapping occurs over
*
* @param colorMapMin
* the colorMapMin to set
* the minimum range value in {@link #colorMapUnit}
* @param persist
* specifies whether to persist this value when saved
* true indicates colorMapMin should be persisted through
* serialization
*/
public void setColorMapMin(float colorMapMin, boolean persist) {
this.colorMapMin = colorMapMin;
@ -510,6 +561,8 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* Returns the unit data values to be colormapped are in
*
* @return the dataUnit
*/
public Unit<?> getDataUnit() {
@ -517,18 +570,20 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* Sets the unit data values to be colormapped are in
*
* @param dataUnit
* the dataUnit to set
*/
public void setDataUnit(Unit<?> dataUnit) {
this.dataUnit = dataUnit;
if (dataUnit != null && imageUnit == null) {
setImageUnit(dataUnit);
if (dataUnit != null && colorMapUnit == null) {
setColorMapUnit(dataUnit);
}
dataToImageConverter = null;
imageToDataConverter = null;
dataToColorMapConverter = null;
colorMapToDataConverter = null;
dataToDisplayConverter = null;
displayToDataConverter = null;
@ -537,6 +592,9 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* Returns the {@link UnitConverter} from {@link #dataUnit} to
* {@link #displayUnit}
*
* @return the dataToDisplayConverter
*/
public UnitConverter getDataToDisplayConverter() {
@ -550,6 +608,9 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* Returns the {@link UnitConverter} from {@link #displayUnit} to
* {@link #dataUnit}
*
* @return the displayToDataConverter
*/
public UnitConverter getDisplayToDataConverter() {
@ -562,7 +623,7 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
return displayToDataConverter;
}
public java.util.List<LabelEntry> getLabels() {
public List<LabelEntry> getLabels() {
if (recomputeLabels) {
computeLabels();
notifyListener();
@ -570,10 +631,22 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
return labels;
}
/**
* Returns true if the colormapping is logarithmically scaled from
* {@link #colorMapMin} to {@link #colorMapMax}
*
* @return
*/
public boolean isLogarithmic() {
return logarithmic;
}
/**
* Set to true if the colormapping should logarithmically scaled from
* {@link #colorMapMin} to {@link #colorMapMax}
*
* @param logarithmic
*/
public void setLogarithmic(boolean logarithmic) {
recomputeLabels = recomputeLabels | this.logarithmic != logarithmic;
this.logarithmic = logarithmic;
@ -581,83 +654,162 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
}
/**
* @return the dataToImageConverter
* @deprecated Use {@link #getDataToColorMapConverter()} instead
*
* @return the dataToColorMapConverter
*/
@Deprecated
public UnitConverter getDataToImageConverter() {
if (dataToImageConverter == null) {
dataToImageConverter = constructConverter(dataUnit, imageUnit);
if (dataToImageConverter != null) {
notifyListener();
}
}
return dataToImageConverter;
return getDataToColorMapConverter();
}
/**
* @return the imageToDisplayConverter
* Returns a {@link UnitConverter} converting {@link #dataUnit} values to
* the {@link #colorMapUnit} if compatible or null otherwise
*
* @return
*/
public UnitConverter getDataToColorMapConverter() {
if (dataToColorMapConverter == null) {
dataToColorMapConverter = constructConverter(dataUnit, colorMapUnit);
if (dataToColorMapConverter != null) {
notifyListener();
}
}
return dataToColorMapConverter;
}
/**
* @deprecated Use {@link #getColorMapToDisplayConverter()} instead
*
* @return the colorMapToDisplayConverter
*/
@Deprecated
public UnitConverter getImageToDisplayConverter() {
if (imageToDisplayConverter == null) {
imageToDisplayConverter = constructConverter(imageUnit, displayUnit);
if (imageToDisplayConverter != null) {
return getColorMapToDisplayConverter();
}
/**
* Returns a {@link UnitConverter} converting {@link #colorMapUnit} values
* to the {@link #displayUnit} if compatible or null otherwise
*
* @return
*/
public UnitConverter getColorMapToDisplayConverter() {
if (colorMapToDisplayConverter == null) {
colorMapToDisplayConverter = constructConverter(colorMapUnit,
displayUnit);
if (colorMapToDisplayConverter != null) {
notifyListener();
}
}
return imageToDisplayConverter;
return colorMapToDisplayConverter;
}
/**
* @return the imageUnit
* @deprecated Use {@link #getColorMapUnit()} instead
*
* @return the colorMapUnit
*/
@Deprecated
public Unit<?> getImageUnit() {
return imageUnit;
return getColorMapUnit();
}
/**
* @param imageUnit
* the imageUnit to set
* Returns the unit colormapping will occur in
*
* @return
*/
public void setImageUnit(Unit<?> imageUnit) {
this.imageUnit = imageUnit;
public Unit<?> getColorMapUnit() {
return colorMapUnit;
}
if (imageUnit != null && dataUnit == null) {
setDataUnit(imageUnit);
/**
* @deprecated Use {@link #setColorMapUnit(Unit)} instead
*
* @param imageUnit
* the colorMapUnit to set
*/
@Deprecated
public void setImageUnit(Unit<?> imageUnit) {
setColorMapUnit(imageUnit);
}
/**
* Sets the unit colormapping will occur in. {@link #colorMapMin} and
* {@link #colorMapMax} are expected to be in this unit
*
* @param colorMapUnit
*/
public void setColorMapUnit(Unit<?> colorMapUnit) {
this.colorMapUnit = colorMapUnit;
if (colorMapUnit != null && dataUnit == null) {
setDataUnit(colorMapUnit);
}
recomputeLabels = true;
imageToDataConverter = null;
dataToImageConverter = null;
colorMapToDataConverter = null;
dataToColorMapConverter = null;
imageToDisplayConverter = null;
displayToImageConverter = null;
colorMapToDisplayConverter = null;
displayToColorMapConverter = null;
notifyListener();
}
/**
* @return the imageToDataConverter
* @deprecated Use {@link #getColorMapToDataConverter()} instead
*
* @return the colorMapToDataConverter
*/
@Deprecated
public UnitConverter getImageToDataConverter() {
if (imageToDataConverter == null) {
imageToDataConverter = constructConverter(imageUnit, dataUnit);
if (imageToDataConverter != null) {
notifyListener();
}
}
return imageToDataConverter;
return getColorMapToDataConverter();
}
/**
* @return the displayToImageConverter
* Returns a {@link UnitConverter} converting {@link #colorMapUnit} values
* to the {@link #dataUnit} if compatible or null otherwise
*
* @return
*/
public UnitConverter getDisplayToImageConverter() {
if (displayToImageConverter == null) {
displayToImageConverter = constructConverter(displayUnit, imageUnit);
if (displayToImageConverter != null) {
public UnitConverter getColorMapToDataConverter() {
if (colorMapToDataConverter == null) {
colorMapToDataConverter = constructConverter(colorMapUnit, dataUnit);
if (colorMapToDataConverter != null) {
notifyListener();
}
}
return displayToImageConverter;
return colorMapToDataConverter;
}
/**
* @deprecated Use {@link #getDisplayToColorMapConverter()} instead
*
* @return the displayToColorMapConverter
*/
@Deprecated
public UnitConverter getDisplayToImageConverter() {
return getDisplayToColorMapConverter();
}
/**
* Returns a {@link UnitConverter} converting {@link #displayUnit} values to
* the {@link #colorMapUnit} if compatible or null otherwise
*
* @return
*/
public UnitConverter getDisplayToColorMapConverter() {
if (displayToColorMapConverter == null) {
displayToColorMapConverter = constructConverter(displayUnit,
colorMapUnit);
if (displayToColorMapConverter != null) {
notifyListener();
}
}
return displayToColorMapConverter;
}
/**
@ -668,7 +820,7 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
this.dataMapping = dataMapping;
recomputeLabels = true;
if (dataMapping != null && displayUnit != null) {
setImageUnit(dataMapping.getImageUnit(displayUnit));
setColorMapUnit(dataMapping.getImageUnit(displayUnit));
}
notifyListener();
}
@ -707,12 +859,12 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
cmp.dataMax = dataMax;
cmp.dataMin = dataMin;
cmp.dataToDisplayConverter = dataToDisplayConverter;
cmp.dataToImageConverter = dataToImageConverter;
cmp.dataToColorMapConverter = dataToColorMapConverter;
cmp.dataUnit = dataUnit;
cmp.formatString = formatString;
cmp.imageToDataConverter = imageToDataConverter;
cmp.imageToDisplayConverter = imageToDisplayConverter;
cmp.imageUnit = imageUnit;
cmp.colorMapToDataConverter = colorMapToDataConverter;
cmp.colorMapToDisplayConverter = colorMapToDisplayConverter;
cmp.colorMapUnit = colorMapUnit;
cmp.labels = labels;
cmp.recomputeLabels = recomputeLabels;
cmp.persisted = persisted.clone();
@ -779,196 +931,19 @@ public class ColorMapParameters implements Cloneable, ISerializableObject {
return mirror;
}
public static void main(String[] args) {
float[] testValues = { -3.622782E-10f, -1.6778468E-8f, -3.3110254E-8f,
-3.7973948E-8f, -2.7633986E-8f, -9.904648E-9f, -7.5229956E-10f,
7.5189455E-10f, 1.20609505E-8f, 2.9364383E-8f, 2.7346168E-8f,
1.3699442E-10f, -2.9937E-8f, -3.6884128E-8f, -1.6217847E-8f,
1.1931893E-8f, 2.252287E-8f, 1.3290519E-8f, -2.4843547E-9f,
-1.0386745E-8f, -6.1300884E-9f, 3.266153E-9f, 1.078073E-8f,
1.2696603E-8f, 4.315502E-9f, -7.2422215E-9f, -1.2054819E-8f,
-9.100724E-9f, -2.2520505E-9f, 4.031535E-9f, 6.908326E-9f,
4.5536255E-9f, 5.3565863E-10f, -6.767242E-11f, 1.7508048E-9f,
1.2594312E-9f, -3.5915362E-9f, -6.42762E-9f, -6.295979E-9f,
-3.857202E-9f, -2.2052171E-9f, 6.51567E-10f, 3.1020404E-9f,
3.2179108E-9f, 2.080211E-9f, 2.5326197E-10f, -2.181367E-9f,
-3.2126295E-9f, -2.2162419E-9f, -5.682299E-10f, 7.367537E-11f,
1.7393351E-9f, 3.2000091E-9f, 2.82146E-9f, 1.9214115E-9f,
1.2637547E-9f, 1.1449517E-9f, 9.570286E-10f, 2.4719807E-10f,
7.4189294E-11f, -1.9902982E-10f, -1.6482227E-9f, -2.645924E-9f,
-1.4331059E-9f, -2.6336155E-10f, -1.5215194E-9f,
-1.5059594E-9f, 2.014035E-10f, 3.565288E-10f, -1.5582429E-9f,
-1.3200975E-9f, 1.2632209E-10f, 1.0882992E-9f, 1.5682087E-9f,
4.24027E-9f, 3.1678654E-10f, -3.9073598E-9f, -3.4730894E-9f,
-9.989449E-10f, -4.0364423E-9f, -6.3506116E-9f, -2.8335263E-9f,
-3.1253768E-9f, -1.1271912E-9f, -3.0793168E-10f, 2.2091975E-9f,
4.9031823E-9f, 1.0454194E-8f, 1.6462849E-8f, 1.339688E-8f,
6.4451755E-9f, 3.820775E-9f, 1.7874671E-9f, -4.5423043E-9f,
-4.7599173E-9f, -3.718451E-9f, 2.086526E-10f, 4.4792867E-9f,
-5.1967337E-9f, -1.0332604E-8f };
ColorMapParameters colorMapParameters = new ColorMapParameters();
float min = -2.6945168e-7f;
float max = 2.1146788e-7f;
colorMapParameters.setColorMapMin(-2.6945168e-7f);
colorMapParameters.setColorMapMax(2.1146788e-7f);
colorMapParameters.setLogarithmic(true);
DecimalFormat format = new DecimalFormat("0.###");
int i = 0;
for (float dispValue : testValues) {
String s = format.format(dispValue);
colorMapParameters.addLabel(dispValue, s);
if (dispValue > max) {
System.out.println(colorMapParameters.labels.get(i).location
+ "to high");
} else if (dispValue < min) {
System.out.println(colorMapParameters.labels.get(i).location
+ "to low");
} else {
System.out.println(colorMapParameters.labels.get(i).location);
}
i++;
}
}
private float getIndexByValue(float dispValue) {
UnitConverter displayToImage = getDisplayToImageConverter();
float colorMapRange;
if (logarithmic) {
// float colorMapMin = this.colorMapMin * 1.25f;
// float colorMapMax = this.colorMapMax * 1.25f;
if (displayToImage != null) {
dispValue = (float) displayToImage.convert(dispValue);
}
float index = 0.0f;
// is this strictly negative, strictly positive or neg to pos
// scaling?
if (colorMapMin >= 0.0 && colorMapMax >= 0.0 && !isMirror()) {
// simple calculation
index = ((float) Math.log(dispValue) - (float) Math
.log(colorMapMin))
/ (float) Math.abs((float) Math.log(colorMapMax)
- (float) Math.log(colorMapMin));
} else if (colorMapMin <= 0.0 && colorMapMax <= 0.0 && !isMirror()) {
index = ((float) (Math.log(dispValue) - (float) Math
.log(colorMapMax)) / (float) Math.abs((float) Math
.log(colorMapMin) - (float) Math.log(colorMapMax)));
} else {
// special case, neg to pos:
float cmapMin = colorMapMin;
float cmapMax = colorMapMax;
float zeroVal = (float) Math.max(cmapMax,
(float) Math.abs(cmapMin)) * 0.0001f;
;
if (isMirror() && (cmapMin > 0 || cmapMax < 0)) {
if (cmapMax < 0) {
cmapMax = -cmapMax;
dispValue = -dispValue;
zeroVal = -cmapMin;
} else {
zeroVal = cmapMin;
}
cmapMin = -cmapMax;
}
float leftZero = 0;
float rightZero = 0;
float absLogZeroVal = (float) Math.abs((float) Math
.log(zeroVal));
rightZero = absLogZeroVal + (float) Math.log(cmapMax);
float cmapMax2 = -1 * cmapMin;
leftZero = absLogZeroVal + (float) Math.log(cmapMax2);
float zeroIndex = leftZero / (leftZero + rightZero);
// figure out index for texture val
float absTextureColor = (float) Math.abs(dispValue);
if (dispValue == 0) {
index = zeroIndex;
} else if (absTextureColor <= zeroVal) {
index = (float) zeroIndex;
} else if (dispValue > 0.0) {
// positive texture color value, find index from 0 to
// cmapMax:
float logTexColor = absLogZeroVal
+ (float) Math.log(dispValue);
float texIndex = logTexColor / rightZero;
index = (float) (zeroIndex + ((1.0 - zeroIndex) * texIndex));
} else {
// negative texture color value, find index from 0 to
// cmapMax:
float logTexColor = absLogZeroVal
+ (float) Math.log(absTextureColor);
float texIndex = logTexColor / leftZero;
index = (zeroIndex - (zeroIndex * texIndex));
}
}
return index;
} else {
colorMapRange = colorMapMax - colorMapMin;
if (colorMapRange != 0.0) {
double pixelValue;
if (displayToImage != null) {
pixelValue = displayToImage.convert(dispValue);
} else {
pixelValue = dispValue;
}
float location;
if (logarithmic) {
location = (float) ((Math.log(pixelValue) - Math
.log(colorMapMin)) / colorMapRange);
} else {
location = ((float) pixelValue - colorMapMin)
/ colorMapRange;
}
return location;
} else {
return 0.0f;
}
}
}
/**
* Get the Color object of the value
*
* @param value
* value to get Color for in {@link #displayUnit}
* @return
*/
public Color getColorByValue(float value) {
float index = getIndexByValue(value);
if (index < 0.0f) {
index = 0.0f;
} else if (index > 1.0f) {
index = 1.0f;
}
if (isInterpolate()) {
index = 0.5f;
index = (index * (colorMap.getSize() - 1));
int lowIndex = (int) Math.floor(index);
int highIndex = (int) Math.ceil(index);
float lowWeight = highIndex - index;
float highWeight = 1.0f - lowWeight;
Color low = colorMap.getColors().get(lowIndex);
Color high = colorMap.getColors().get(highIndex);
float r = lowWeight * low.getRed() + highWeight * high.getRed();
float g = lowWeight * low.getGreen() + highWeight * high.getGreen();
float b = lowWeight * low.getBlue() + highWeight * high.getBlue();
float a = lowWeight * low.getAlpha() + highWeight * high.getAlpha();
return new Color(r, g, b, a);
} else {
return colorMap.getColors().get(
(int) (index * (colorMap.getSize() - 1)));
UnitConverter displayToColorMap = getDisplayToColorMapConverter();
if (displayToColorMap != null) {
value = (float) displayToColorMap.convert(value);
}
return Colormapper.getColorByValue(value, this);
}
/**

View file

@ -378,7 +378,7 @@ public class FileLocker {
// locking, can't do any locking
if (parentDir.canWrite() == false) {
UFStatus.getHandler()
.handle(Priority.PROBLEM,
.handle(Priority.DEBUG,
"Cannot write to directory: "
+ parentDir.getAbsolutePath());
return false;

View file

@ -27,7 +27,7 @@
<displayUnits>kft</displayUnits>
<range scale="LINEAR">
<!-- This kft range matches original -19m to 5000m from code -->
<minValue>-0.0062</minValue>
<minValue>-0.062</minValue>
<maxValue>16.404</maxValue>
</range>
<defaultColormap>topo</defaultColormap>