001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.image.colormodel; 020 021import java.awt.image.BandedSampleModel; 022import java.awt.image.ColorModel; 023import java.awt.image.ComponentSampleModel; 024import java.awt.image.DataBufferByte; 025import java.awt.image.DataBufferDouble; 026import java.awt.image.DataBufferFloat; 027import java.awt.image.DataBufferInt; 028import java.awt.image.DataBufferShort; 029import java.awt.image.DataBufferUShort; 030import java.awt.image.Raster; 031import java.awt.image.SampleModel; 032import java.awt.image.WritableRaster; 033import java.lang.reflect.Field; 034import java.util.ArrayList; 035import java.util.List; 036 037import icy.common.CollapsibleEvent; 038import icy.common.UpdateEventHandler; 039import icy.common.listener.ChangeListener; 040import icy.image.colormap.IcyColorMap; 041import icy.image.colormodel.IcyColorModelEvent.IcyColorModelEventType; 042import icy.image.colorspace.IcyColorSpace; 043import icy.image.colorspace.IcyColorSpaceEvent; 044import icy.image.colorspace.IcyColorSpaceListener; 045import icy.image.lut.LUT; 046import icy.math.Scaler; 047import icy.math.ScalerEvent; 048import icy.math.ScalerListener; 049import icy.type.DataType; 050import icy.type.TypeUtil; 051import icy.type.collection.array.Array1DUtil; 052import icy.type.collection.array.ArrayUtil; 053import icy.util.ReflectionUtil; 054 055/** 056 * @author stephane 057 */ 058public abstract class IcyColorModel extends ColorModel implements ScalerListener, IcyColorSpaceListener, ChangeListener 059{ 060 /** 061 * scalers for normalization 062 */ 063 protected final Scaler[] normalScalers; 064 /** 065 * scalers for colorMap 066 */ 067 protected final Scaler[] colormapScalers; 068 069 /** 070 * data type 071 */ 072 protected final DataType dataType; 073 074 /** 075 * overridden variables 076 */ 077 protected final int numComponents; 078 079 /** 080 * listeners 081 */ 082 private final List<IcyColorModelListener> listeners; 083 084 /** 085 * internal updater 086 */ 087 private final UpdateEventHandler updater; 088 089 /** 090 * Default constructor 091 */ 092 IcyColorModel(int numComponents, DataType dataType, int[] bits) 093 { 094 super(dataType.getBitSize(), bits, new IcyColorSpace(numComponents), true, false, TRANSLUCENT, 095 dataType.toDataBufferType()); 096 097 if (numComponents == 0) 098 throw new IllegalArgumentException("Number of components should be > 0"); 099 100 // overridden variable 101 this.numComponents = numComponents; 102 103 listeners = new ArrayList<IcyColorModelListener>(); 104 updater = new UpdateEventHandler(this, false); 105 106 // data type information 107 this.dataType = dataType; 108 109 // get default min and max for datatype 110 final double[] defaultBounds = dataType.getDefaultBounds(); 111 final double min = defaultBounds[0]; 112 final double max = defaultBounds[1]; 113 // float type flag 114 final boolean isFloat = dataType.isFloat(); 115 116 // allocating scalers 117 normalScalers = new Scaler[numComponents]; 118 colormapScalers = new Scaler[numComponents]; 119 // defining scalers 120 for (int i = 0; i < numComponents; i++) 121 { 122 // scale for normalization 123 normalScalers[i] = new Scaler(min, max, 0f, 1f, !isFloat); 124 // scale for colormap 125 colormapScalers[i] = new Scaler(min, max, 0f, IcyColorMap.MAX_INDEX, !isFloat); 126 // add listener to the colormap scalers only 127 colormapScalers[i].addListener(this); 128 } 129 130 // add the listener to colorSpace 131 getIcyColorSpace().addListener(this); 132 } 133 134 /** 135 * @deprecated use {@link #IcyColorModel(int, DataType, int[])} instead 136 */ 137 @Deprecated 138 IcyColorModel(int numComponents, int dataType, boolean signed, int[] bits) 139 { 140 this(numComponents, DataType.getDataType(dataType, signed), bits); 141 } 142 143 /** 144 * Creates a new ColorModel with the given color component and image data type 145 * 146 * @param numComponents 147 * number of component 148 * @param dataType 149 * the type of image data (see {@link DataType}) 150 * @return a IcyColorModel object 151 */ 152 public static IcyColorModel createInstance(int numComponents, DataType dataType) 153 { 154 // define bits size 155 final int bits = dataType.getBitSize(); 156 // we have to fake one more extra component for alpha in ColorModel class 157 final int numComponentFixed = numComponents + 1; 158 final int[] componentBits = new int[numComponentFixed]; 159 160 for (int i = 0; i < numComponentFixed; i++) 161 componentBits[i] = bits; 162 163 switch (dataType) 164 { 165 case UBYTE: 166 return new UByteColorModel(numComponents, componentBits); 167 case BYTE: 168 return new ByteColorModel(numComponents, componentBits); 169 case USHORT: 170 return new UShortColorModel(numComponents, componentBits); 171 case SHORT: 172 return new ShortColorModel(numComponents, componentBits); 173 case UINT: 174 return new UIntColorModel(numComponents, componentBits); 175 case INT: 176 return new IntColorModel(numComponents, componentBits); 177 case ULONG: 178 return new ULongColorModel(numComponents, componentBits); 179 case LONG: 180 return new LongColorModel(numComponents, componentBits); 181 case FLOAT: 182 return new FloatColorModel(numComponents, componentBits); 183 case DOUBLE: 184 return new DoubleColorModel(numComponents, componentBits); 185 default: 186 throw new IllegalArgumentException("Unsupported data type !"); 187 } 188 } 189 190 /** 191 * @deprecated use {@link #createInstance(int, DataType)} instead 192 */ 193 @Deprecated 194 public static IcyColorModel createInstance(int numComponents, int dataType, boolean signed) 195 { 196 return createInstance(numComponents, DataType.getDataType(dataType, signed)); 197 } 198 199 /** 200 * Creates a new ColorModel from a given icyColorModel 201 * 202 * @param colorModel 203 * icyColorModel 204 * @param copyColormap 205 * flag to indicate if we want to copy colormaps from the given icyColorModel 206 * @param copyBounds 207 * flag to indicate if we want to copy bounds from the given icyColorModel 208 * @return a IcyColorModel object 209 */ 210 public static IcyColorModel createInstance(IcyColorModel colorModel, boolean copyColormap, boolean copyBounds) 211 { 212 final IcyColorModel result = IcyColorModel.createInstance(colorModel.getNumComponents(), 213 colorModel.getDataType_()); 214 215 result.beginUpdate(); 216 try 217 { 218 // copy colormaps from colorModel ? 219 if (copyColormap) 220 result.setColorMaps(colorModel); 221 // copy bounds from colorModel ? 222 if (copyBounds) 223 result.setBounds(colorModel); 224 } 225 finally 226 { 227 result.endUpdate(); 228 } 229 230 return result; 231 } 232 233 /** 234 * Create default ColorModel : 4 components, unsigned byte data type 235 */ 236 public static IcyColorModel createInstance() 237 { 238 return createInstance(4, DataType.UBYTE); 239 } 240 241 public static BandedSampleModel createCompatibleSampleModel(int transferType, int w, int h, int numComponent) 242 { 243 return new BandedSampleModel(transferType, w, h, numComponent); 244 } 245 246 /** 247 * Create a writable raster from specified data and size.<br> 248 * The Object data is internally a 2D array [][] 249 */ 250 public static WritableRaster createWritableRaster(Object data, int w, int h) 251 { 252 if (ArrayUtil.getDim(data) != 2) 253 throw new IllegalArgumentException( 254 "IcyColorModel.createWritableRaster(..) error: 'data' argument should be a 2D array !"); 255 256 final DataType dataType = ArrayUtil.getDataType(data); 257 final int sizeC = ArrayUtil.getLength(data); 258 259 final SampleModel sm = createCompatibleSampleModel(dataType.toDataBufferType(), w, h, sizeC); 260 261 switch (dataType) 262 { 263 case UBYTE: 264 case BYTE: 265 return Raster.createWritableRaster(sm, new DataBufferByte((byte[][]) data, w * h), null); 266 case SHORT: 267 return Raster.createWritableRaster(sm, new DataBufferShort((short[][]) data, w * h), null); 268 case USHORT: 269 return Raster.createWritableRaster(sm, new DataBufferUShort((short[][]) data, w * h), null); 270 case UINT: 271 case INT: 272 return Raster.createWritableRaster(sm, new DataBufferInt((int[][]) data, w * h), null); 273 case FLOAT: 274 return Raster.createWritableRaster(sm, new DataBufferFloat((float[][]) data, w * h), null); 275 case DOUBLE: 276 return Raster.createWritableRaster(sm, new DataBufferDouble((double[][]) data, w * h), null); 277 default: 278 throw new IllegalArgumentException( 279 "IcyColorModel.createWritableRaster(..) error: unsupported data type : " + dataType); 280 } 281 } 282 283 @Override 284 public SampleModel createCompatibleSampleModel(int w, int h) 285 { 286 return createCompatibleSampleModel(transferType, w, h, getNumComponents()); 287 } 288 289 @Override 290 public WritableRaster createCompatibleWritableRaster(int w, int h) 291 { 292 final SampleModel sm = createCompatibleSampleModel(w, h); 293 294 return Raster.createWritableRaster(sm, sm.createDataBuffer(), null); 295 } 296 297 /** 298 * Create a writable raster from specified data and size.<br> 299 */ 300 public WritableRaster createWritableRaster(Object[] data, int w, int h) 301 { 302 final SampleModel sm = createCompatibleSampleModel(w, h); 303 304 switch (dataType) 305 { 306 case UBYTE: 307 case BYTE: 308 return Raster.createWritableRaster(sm, new DataBufferByte((byte[][]) data, w * h), null); 309 case SHORT: 310 return Raster.createWritableRaster(sm, new DataBufferShort((short[][]) data, w * h), null); 311 case USHORT: 312 return Raster.createWritableRaster(sm, new DataBufferUShort((short[][]) data, w * h), null); 313 case UINT: 314 case INT: 315 return Raster.createWritableRaster(sm, new DataBufferInt((int[][]) data, w * h), null); 316 case FLOAT: 317 return Raster.createWritableRaster(sm, new DataBufferFloat((float[][]) data, w * h), null); 318 case DOUBLE: 319 return Raster.createWritableRaster(sm, new DataBufferDouble((double[][]) data, w * h), null); 320 default: 321 throw new IllegalArgumentException( 322 "IcyColorModel.createWritableRaster(..) error : unsupported data type : " + dataType); 323 } 324 } 325 326 /** 327 * Set bounds from specified {@link IcyColorModel} 328 */ 329 public void setBounds(IcyColorModel source) 330 { 331 beginUpdate(); 332 try 333 { 334 for (int i = 0; i < numComponents; i++) 335 { 336 final Scaler srcNormalScaler = source.getNormalScalers()[i]; 337 final Scaler dstNormalScaler = normalScalers[i]; 338 final Scaler srcColorMapScaler = source.getColormapScalers()[i]; 339 final Scaler dstColorMapScaler = colormapScalers[i]; 340 341 dstNormalScaler.beginUpdate(); 342 try 343 { 344 dstNormalScaler.setAbsLeftRightIn(srcNormalScaler.getAbsLeftIn(), srcNormalScaler.getAbsRightIn()); 345 dstNormalScaler.setLeftRightIn(srcNormalScaler.getLeftIn(), srcNormalScaler.getRightIn()); 346 dstNormalScaler.setLeftRightOut(srcNormalScaler.getLeftOut(), srcNormalScaler.getRightOut()); 347 } 348 finally 349 { 350 dstNormalScaler.endUpdate(); 351 } 352 353 dstColorMapScaler.beginUpdate(); 354 try 355 { 356 dstColorMapScaler.setAbsLeftRightIn(srcColorMapScaler.getAbsLeftIn(), 357 srcColorMapScaler.getAbsRightIn()); 358 dstColorMapScaler.setLeftRightIn(srcColorMapScaler.getLeftIn(), srcColorMapScaler.getRightIn()); 359 dstColorMapScaler.setLeftRightOut(srcColorMapScaler.getLeftOut(), srcColorMapScaler.getRightOut()); 360 } 361 finally 362 { 363 dstColorMapScaler.endUpdate(); 364 } 365 } 366 } 367 finally 368 { 369 endUpdate(); 370 } 371 } 372 373 /** 374 * @deprecated Use {@link #setBounds(IcyColorModel)} instead. 375 */ 376 @Deprecated 377 public void copyBounds(IcyColorModel source) 378 { 379 setBounds(source); 380 } 381 382 /** 383 * Return the toRGB colormap of specified RGB component 384 */ 385 public IcyColorMap getColorMap(int component) 386 { 387 return getIcyColorSpace().getColorMap(component); 388 } 389 390 /** 391 * @deprecated Use {@link #getColorMap(int)} instead (different case). 392 */ 393 @Deprecated 394 public IcyColorMap getColormap(int component) 395 { 396 return getColorMap(component); 397 } 398 399 /** 400 * Set the toRGB colormaps from a compatible colorModel. 401 * 402 * @param source 403 * source ColorModel to copy colormap from 404 */ 405 public void setColorMaps(ColorModel source) 406 { 407 getIcyColorSpace().setColorMaps(source); 408 } 409 410 /** 411 * @deprecated Use {@link #setColorMaps(ColorModel)} instead (different case). 412 */ 413 @Deprecated 414 public void setColormaps(ColorModel source) 415 { 416 setColorMaps(source); 417 } 418 419 /** 420 * @deprecated Use {@link #setColorMaps(ColorModel)} instead. 421 */ 422 @Deprecated 423 public void copyColormap(ColorModel source) 424 { 425 setColorMaps(source); 426 } 427 428 /** 429 * Set the toRGB colormap of specified component (actually copy the content). 430 * 431 * @param component 432 * component we want to set the colormap 433 * @param map 434 * source colormap to copy 435 * @param setAlpha 436 * also set the alpha information 437 */ 438 public void setColorMap(int component, IcyColorMap map, boolean setAlpha) 439 { 440 getIcyColorSpace().setColorMap(component, map, setAlpha); 441 } 442 443 /** 444 * @deprecated Use {@link #setColorMap(int, IcyColorMap, boolean)} instead. 445 */ 446 @Deprecated 447 public void setColormap(int component, IcyColorMap map) 448 { 449 setColorMap(component, map, true); 450 } 451 452 /** 453 * @see java.awt.image.ColorModel#getAlpha(int) 454 */ 455 @Override 456 public int getAlpha(int pixel) 457 { 458 throw new IllegalArgumentException("Argument type not supported for this color model"); 459 } 460 461 /** 462 * @see java.awt.image.ColorModel#getBlue(int) 463 */ 464 @Override 465 public int getBlue(int pixel) 466 { 467 throw new IllegalArgumentException("Argument type not supported for this color model"); 468 } 469 470 /** 471 * @see java.awt.image.ColorModel#getGreen(int) 472 */ 473 @Override 474 public int getGreen(int pixel) 475 { 476 throw new IllegalArgumentException("Argument type not supported for this color model"); 477 } 478 479 /** 480 * @see java.awt.image.ColorModel#getRed(int) 481 */ 482 @Override 483 public int getRed(int pixel) 484 { 485 throw new IllegalArgumentException("Argument type not supported for this color model"); 486 } 487 488 @Override 489 public abstract int getRGB(Object inData); 490 491 public abstract int getRGB(Object pixel, LUT lut); 492 493 /** 494 * 495 */ 496 @Override 497 public int getBlue(Object pixel) 498 { 499 return getRGB(pixel) & 0xFF; 500 } 501 502 /** 503 * 504 */ 505 @Override 506 public int getGreen(Object pixel) 507 { 508 return (getRGB(pixel) >> 8) & 0xFF; 509 } 510 511 /** 512 * 513 */ 514 @Override 515 public int getRed(Object pixel) 516 { 517 return (getRGB(pixel) >> 16) & 0xFF; 518 } 519 520 /** 521 * 522 */ 523 @Override 524 public int getAlpha(Object pixel) 525 { 526 return (getRGB(pixel) >> 24) & 0xFF; 527 } 528 529 /** 530 * @see java.awt.image.ColorModel#getComponents(int, int[], int) 531 */ 532 @Override 533 public int[] getComponents(int pixel, int[] components, int offset) 534 { 535 throw new IllegalArgumentException("Not supported in this ColorModel"); 536 } 537 538 /** 539 * @see java.awt.image.ColorModel#getComponents(Object, int[], int) 540 */ 541 @Override 542 public abstract int[] getComponents(Object pixel, int[] components, int offset); 543 544 /** 545 * @see java.awt.image.ColorModel#getNormalizedComponents(Object, float[], int) 546 */ 547 @Override 548 public abstract float[] getNormalizedComponents(Object pixel, float[] normComponents, int normOffset); 549 550 /** 551 * @see java.awt.image.ColorModel#getNormalizedComponents(int[], int, float[], int) 552 */ 553 @Override 554 public float[] getNormalizedComponents(int[] components, int offset, float[] normComponents, int normOffset) 555 { 556 if ((components.length - offset) < numComponents) 557 throw new IllegalArgumentException("Incorrect number of components. Expecting " + numComponents); 558 559 final float[] result = Array1DUtil.allocIfNull(normComponents, numComponents + normOffset); 560 561 for (int i = 0; i < numComponents; i++) 562 result[normOffset + i] = (float) normalScalers[i].scale(components[offset + i]); 563 564 return result; 565 } 566 567 /** 568 * @see java.awt.image.ColorModel#getUnnormalizedComponents(float[], int, int[], int) 569 */ 570 @Override 571 public int[] getUnnormalizedComponents(float[] normComponents, int normOffset, int[] components, int offset) 572 { 573 if ((normComponents.length - normOffset) < numComponents) 574 throw new IllegalArgumentException("Incorrect number of components. Expecting " + numComponents); 575 576 final int[] result = Array1DUtil.allocIfNull(components, numComponents + offset); 577 578 for (int i = 0; i < numComponents; i++) 579 result[offset + i] = (int) normalScalers[i].unscale(normComponents[normOffset + i]); 580 581 return result; 582 } 583 584 /** 585 * @see java.awt.image.ColorModel#getDataElement(int[], int) 586 */ 587 @Override 588 public int getDataElement(int[] components, int offset) 589 { 590 throw new IllegalArgumentException("Not supported in this ColorModel"); 591 } 592 593 /** 594 * @see java.awt.image.ColorModel#getDataElement(float[], int) 595 */ 596 @Override 597 public int getDataElement(float[] normComponents, int normOffset) 598 { 599 throw new IllegalArgumentException("Not supported in this ColorModel"); 600 } 601 602 /** 603 * @see java.awt.image.ColorModel#getDataElements(int[], int, Object) 604 */ 605 @Override 606 public abstract Object getDataElements(int[] components, int offset, Object obj); 607 608 /** 609 * @see java.awt.image.ColorModel#getDataElements(int, Object) 610 */ 611 @Override 612 public Object getDataElements(int rgb, Object pixel) 613 { 614 return getDataElements(getIcyColorSpace().fromRGB(rgb), 0, pixel); 615 } 616 617 /** 618 * @see java.awt.image.ColorModel#getDataElements(float[], int, Object) 619 */ 620 @Override 621 public abstract Object getDataElements(float[] normComponents, int normOffset, Object obj); 622 623 /** 624 * 625 */ 626 @Override 627 public ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) 628 { 629 // nothing to do 630 return this; 631 } 632 633 /** 634 * Scale input value for colormap indexing 635 */ 636 protected double colormapScale(int component, double value) 637 { 638 return colormapScalers[component].scale(value); 639 } 640 641 /** 642 * Tests if the specified <code>Object</code> is an instance of <code>ColorModel</code> and if 643 * it equals this <code>ColorModel</code>. 644 * 645 * @param obj 646 * the <code>Object</code> to test for equality 647 * @return <code>true</code> if the specified <code>Object</code> is an instance of <code>ColorModel</code> and 648 * equals this <code>ColorModel</code>; <code>false</code> otherwise. 649 */ 650 @Override 651 public boolean equals(Object obj) 652 { 653 if (obj instanceof IcyColorModel) 654 return isCompatible((IcyColorModel) obj); 655 656 return false; 657 } 658 659 /** 660 * 661 */ 662 public boolean isCompatible(IcyColorModel cm) 663 { 664 return (getNumComponents() == cm.getNumComponents()) && (getDataType_() == cm.getDataType_()); 665 } 666 667 /** 668 * 669 */ 670 @Override 671 public boolean isCompatibleRaster(Raster raster) 672 { 673 final SampleModel sm = raster.getSampleModel(); 674 final int[] bits = getComponentSize(); 675 676 if (sm instanceof ComponentSampleModel) 677 { 678 if (sm.getNumBands() != numComponents) 679 return false; 680 681 for (int i = 0; i < bits.length; i++) 682 if (sm.getSampleSize(i) < bits[i]) 683 return false; 684 685 return (raster.getTransferType() == transferType); 686 } 687 688 return false; 689 } 690 691 /** 692 * 693 */ 694 @Override 695 public boolean isCompatibleSampleModel(SampleModel sm) 696 { 697 // Must have the same number of components 698 if (numComponents != sm.getNumBands()) 699 return false; 700 if (sm.getTransferType() != transferType) 701 return false; 702 703 return true; 704 } 705 706 /** 707 * @return the IcyColorSpace 708 */ 709 public IcyColorSpace getIcyColorSpace() 710 { 711 return (IcyColorSpace) getColorSpace(); 712 } 713 714 /** 715 * Change the colorspace of the color model.<br/> 716 * <b>You should never use this method directly (internal use only)</b> 717 */ 718 public void setColorSpace(IcyColorSpace colorSpace) 719 { 720 final IcyColorSpace cs = getIcyColorSpace(); 721 722 if (cs != colorSpace) 723 { 724 try 725 { 726 final Field csField = ReflectionUtil.getField(ColorModel.class, "colorSpace", true); 727 // set new colorSpace value 728 csField.set(this, colorSpace); 729 730 cs.removeListener(this); 731 colorSpace.addListener(this); 732 } 733 catch (Exception e) 734 { 735 System.err.println("Warning: Couldn't change colorspace of IcyColorModel..."); 736 } 737 } 738 739 // do not notify about the change here as this method is only called internally 740 } 741 742 /** 743 * @return the normalScalers 744 */ 745 public Scaler[] getNormalScalers() 746 { 747 return normalScalers; 748 } 749 750 /** 751 * @return the colormapScalers 752 */ 753 public Scaler[] getColormapScalers() 754 { 755 return colormapScalers; 756 } 757 758 /** 759 * Returns the number of components in this <code>ColorModel</code>.<br> 760 * Note that alpha is embedded so we always have NumColorComponent = NumComponent 761 * 762 * @return the number of components in this <code>ColorModel</code> 763 */ 764 @Override 765 public int getNumComponents() 766 { 767 return numComponents; 768 } 769 770 /** 771 * @deprecated use {@link #getDataType_()} instead 772 */ 773 @Deprecated 774 public int getDataType() 775 { 776 return TypeUtil.dataBufferTypeToDataType(transferType); 777 } 778 779 /** 780 * Return data type for this colormodel 781 * 782 * @see DataType 783 */ 784 public DataType getDataType_() 785 { 786 return dataType; 787 } 788 789 /** 790 * return default component bounds for this colormodel 791 */ 792 public double[] getDefaultComponentBounds() 793 { 794 return dataType.getDefaultBounds(); 795 } 796 797 /** 798 * Get component absolute minimum value 799 */ 800 public double getComponentAbsMinValue(int component) 801 { 802 // use the normal scaler 803 return normalScalers[component].getAbsLeftIn(); 804 } 805 806 /** 807 * Get component absolute maximum value 808 */ 809 public double getComponentAbsMaxValue(int component) 810 { 811 // use the normal scaler 812 return normalScalers[component].getAbsRightIn(); 813 } 814 815 /** 816 * Get component absolute bounds (min and max values) 817 */ 818 public double[] getComponentAbsBounds(int component) 819 { 820 final double[] result = new double[2]; 821 822 result[0] = getComponentAbsMinValue(component); 823 result[1] = getComponentAbsMaxValue(component); 824 825 return result; 826 } 827 828 /** 829 * Get component user minimum value 830 */ 831 public double getComponentUserMinValue(int component) 832 { 833 // use the normal scaler 834 return normalScalers[component].getLeftIn(); 835 } 836 837 /** 838 * Get user component user maximum value 839 */ 840 public double getComponentUserMaxValue(int component) 841 { 842 // use the normal scaler 843 return normalScalers[component].getRightIn(); 844 } 845 846 /** 847 * Get component user bounds (min and max values) 848 */ 849 public double[] getComponentUserBounds(int component) 850 { 851 final double[] result = new double[2]; 852 853 result[0] = getComponentUserMinValue(component); 854 result[1] = getComponentUserMaxValue(component); 855 856 return result; 857 } 858 859 /** 860 * Set component absolute minimum value 861 */ 862 public void setComponentAbsMinValue(int component, double min) 863 { 864 // update both scalers 865 normalScalers[component].setAbsLeftIn(min); 866 colormapScalers[component].setAbsLeftIn(min); 867 } 868 869 /** 870 * Set component absolute maximum value 871 */ 872 public void setComponentAbsMaxValue(int component, double max) 873 { 874 // update both scalers 875 normalScalers[component].setAbsRightIn(max); 876 colormapScalers[component].setAbsRightIn(max); 877 } 878 879 /** 880 * Set component absolute bounds (min and max values) 881 */ 882 public void setComponentAbsBounds(int component, double[] bounds) 883 { 884 setComponentAbsBounds(component, bounds[0], bounds[1]); 885 } 886 887 /** 888 * Set component absolute bounds (min and max values) 889 */ 890 public void setComponentAbsBounds(int component, double min, double max) 891 { 892 // update both scalers 893 normalScalers[component].setAbsLeftRightIn(min, max); 894 colormapScalers[component].setAbsLeftRightIn(min, max); 895 } 896 897 /** 898 * Set component user minimum value 899 */ 900 public void setComponentUserMinValue(int component, double min) 901 { 902 // update both scalers 903 normalScalers[component].setLeftIn(min); 904 colormapScalers[component].setLeftIn(min); 905 } 906 907 /** 908 * Set component user maximum value 909 */ 910 public void setComponentUserMaxValue(int component, double max) 911 { 912 // update both scalers 913 normalScalers[component].setRightIn(max); 914 colormapScalers[component].setRightIn(max); 915 } 916 917 /** 918 * Set component user bounds (min and max values) 919 */ 920 public void setComponentUserBounds(int component, double[] bounds) 921 { 922 setComponentUserBounds(component, bounds[0], bounds[1]); 923 } 924 925 /** 926 * Set component user bounds (min and max values) 927 */ 928 public void setComponentUserBounds(int component, double min, double max) 929 { 930 // update both scalers 931 normalScalers[component].setLeftRightIn(min, max); 932 colormapScalers[component].setLeftRightIn(min, max); 933 } 934 935 /** 936 * Set components absolute bounds (min and max values) 937 */ 938 public void setComponentsAbsBounds(double[][] bounds) 939 { 940 final int numComponents = getNumComponents(); 941 942 if (bounds.length != numComponents) 943 throw new IllegalArgumentException("bounds.length != ColorModel.numComponents"); 944 945 for (int component = 0; component < numComponents; component++) 946 setComponentAbsBounds(component, bounds[component]); 947 } 948 949 /** 950 * Set components user bounds (min and max values) 951 */ 952 public void setComponentsUserBounds(double[][] bounds) 953 { 954 final int numComponents = getNumComponents(); 955 956 if (bounds.length != numComponents) 957 throw new IllegalArgumentException("bounds.length != ColorModel.numComponents"); 958 959 for (int component = 0; component < numComponents; component++) 960 setComponentUserBounds(component, bounds[component]); 961 } 962 963 /** 964 * Return true if colorModel is float data type 965 */ 966 public boolean isFloatDataType() 967 { 968 return dataType.isFloat(); 969 } 970 971 /** 972 * Return true if colorModel data type is signed 973 */ 974 public boolean isSignedDataType() 975 { 976 return dataType.isSigned(); 977 } 978 979 /** 980 * Return true if color maps associated to this {@link IcyColorModel} are all linear map. 981 * 982 * @see IcyColorMap#isLinear() 983 */ 984 public boolean hasLinearColormaps() 985 { 986 for (int c = 0; c < numComponents; c++) 987 if (!getColorMap(c).isLinear()) 988 return false; 989 990 return true; 991 } 992 993 /** 994 * Returns the <code>String</code> representation of the contents of this <code>ColorModel</code>object. 995 * 996 * @return a <code>String</code> representing the contents of this <code>ColorModel</code> object. 997 */ 998 @Override 999 public String toString() 1000 { 1001 return new String("ColorModel: dataType = " + dataType + " numComponents = " + numComponents + " color space = " 1002 + getColorSpace()); 1003 } 1004 1005 /** 1006 * Add a listener 1007 * 1008 * @param listener 1009 */ 1010 public void addListener(IcyColorModelListener listener) 1011 { 1012 listeners.add(listener); 1013 } 1014 1015 /** 1016 * Remove a listener 1017 * 1018 * @param listener 1019 */ 1020 public void removeListener(IcyColorModelListener listener) 1021 { 1022 listeners.remove(listener); 1023 } 1024 1025 /** 1026 * fire event 1027 * 1028 * @param e 1029 */ 1030 public void fireEvent(IcyColorModelEvent e) 1031 { 1032 for (IcyColorModelListener listener : new ArrayList<IcyColorModelListener>(listeners)) 1033 listener.colorModelChanged(e); 1034 } 1035 1036 /** 1037 * process on colormodel change 1038 */ 1039 @Override 1040 public void onChanged(CollapsibleEvent compare) 1041 { 1042 final IcyColorModelEvent event = (IcyColorModelEvent) compare; 1043 1044 // notify listener we have changed 1045 fireEvent(event); 1046 } 1047 1048 @Override 1049 public void scalerChanged(ScalerEvent e) 1050 { 1051 // only listening colormapScalers 1052 final int ind = Scaler.indexOf(colormapScalers, e.getScaler()); 1053 1054 // handle changed via updater object 1055 if (ind != -1) 1056 updater.changed(new IcyColorModelEvent(this, IcyColorModelEventType.SCALER_CHANGED, ind)); 1057 } 1058 1059 @Override 1060 public void colorSpaceChanged(IcyColorSpaceEvent e) 1061 { 1062 // handle changed via updater object 1063 updater.changed(new IcyColorModelEvent(this, IcyColorModelEventType.COLORMAP_CHANGED, e.getComponent())); 1064 } 1065 1066 public void beginUpdate() 1067 { 1068 updater.beginUpdate(); 1069 } 1070 1071 public void endUpdate() 1072 { 1073 updater.endUpdate(); 1074 } 1075 1076 public boolean isUpdating() 1077 { 1078 return updater.isUpdating(); 1079 } 1080}