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; 020 021import java.awt.Dimension; 022import java.awt.Graphics; 023import java.awt.Point; 024import java.awt.Rectangle; 025import java.awt.image.BufferedImage; 026import java.awt.image.ComponentSampleModel; 027import java.awt.image.DataBuffer; 028import java.awt.image.DataBufferByte; 029import java.awt.image.DataBufferDouble; 030import java.awt.image.DataBufferFloat; 031import java.awt.image.DataBufferInt; 032import java.awt.image.DataBufferShort; 033import java.awt.image.DataBufferUShort; 034import java.awt.image.ImageObserver; 035import java.awt.image.Raster; 036import java.awt.image.SampleModel; 037import java.awt.image.WritableRaster; 038import java.io.IOException; 039import java.lang.ref.WeakReference; 040import java.lang.reflect.Array; 041import java.lang.reflect.Field; 042import java.nio.channels.ClosedByInterruptException; 043import java.util.ArrayList; 044import java.util.HashMap; 045import java.util.List; 046import java.util.Map; 047import java.util.concurrent.BlockingQueue; 048import java.util.concurrent.Callable; 049import java.util.concurrent.ExecutionException; 050import java.util.concurrent.FutureTask; 051import java.util.concurrent.LinkedBlockingQueue; 052import java.util.concurrent.ThreadPoolExecutor; 053import java.util.concurrent.TimeUnit; 054 055import javax.media.jai.PlanarImage; 056 057import icy.common.CollapsibleEvent; 058import icy.common.UpdateEventHandler; 059import icy.common.exception.TooLargeArrayException; 060import icy.common.exception.UnsupportedFormatException; 061import icy.common.listener.ChangeListener; 062import icy.image.IcyBufferedImageEvent.IcyBufferedImageEventType; 063import icy.image.cache.ImageCache; 064import icy.image.colormap.IcyColorMap; 065import icy.image.colormap.LinearColorMap; 066import icy.image.colormodel.IcyColorModel; 067import icy.image.colormodel.IcyColorModelEvent; 068import icy.image.colormodel.IcyColorModelListener; 069import icy.image.lut.LUT; 070import icy.math.ArrayMath; 071import icy.math.MathUtil; 072import icy.math.Scaler; 073import icy.preferences.GeneralPreferences; 074import icy.sequence.Sequence; 075import icy.sequence.SequenceIdImporter; 076import icy.system.SystemUtil; 077import icy.type.DataType; 078import icy.type.TypeUtil; 079import icy.type.collection.array.Array1DUtil; 080import icy.type.collection.array.Array2DUtil; 081import icy.type.collection.array.ArrayUtil; 082import icy.type.collection.array.ByteArrayConvert; 083import icy.util.ReflectionUtil; 084import icy.util.StringUtil; 085import loci.formats.FormatException; 086import loci.formats.IFormatReader; 087import loci.formats.gui.SignedByteBuffer; 088import loci.formats.gui.SignedShortBuffer; 089import loci.formats.gui.UnsignedIntBuffer; 090import plugins.kernel.importer.LociImporterPlugin; 091 092/** 093 * @author stephane 094 */ 095public class IcyBufferedImage extends BufferedImage implements IcyColorModelListener, ChangeListener 096{ 097 static class WeakIcyBufferedImageReference extends WeakReference<IcyBufferedImage> 098 { 099 final int hc; 100 101 WeakIcyBufferedImageReference(IcyBufferedImage image) 102 { 103 super(image); 104 105 hc = System.identityHashCode(image); 106 } 107 108 @Override 109 public int hashCode() 110 { 111 return hc; 112 } 113 114 @Override 115 public boolean equals(Object obj) 116 { 117 if (obj instanceof WeakIcyBufferedImageReference) 118 return obj.hashCode() == hashCode(); 119 120 return super.equals(obj); 121 } 122 } 123 124 public static class ImageSourceInfo 125 { 126 // importer 127 public final SequenceIdImporter imp; 128 // series index 129 public final int series; 130 // resolution 131 public final int resolution; 132 // region 133 public final Rectangle region; 134 // T, Z, C position 135 public final int t; 136 public final int z; 137 public final int c; 138 139 public ImageSourceInfo(SequenceIdImporter imp, int series, int resolution, Rectangle region, int t, int z, 140 int c) 141 { 142 super(); 143 144 this.imp = imp; 145 this.series = series; 146 this.resolution = resolution; 147 this.region = region; 148 this.t = t; 149 this.z = z; 150 this.c = c; 151 } 152 153 @Override 154 public String toString() 155 { 156 return imp.toString() + " s=" + series + " r=" + resolution + " t=" + t + " z=" + z + " c=" + c; 157 } 158 } 159 160 private static class ImageDataLoaderWorker implements Callable<Object> 161 { 162 final WeakReference<IcyBufferedImage> imageRef; 163 164 ImageDataLoaderWorker(IcyBufferedImage image) 165 { 166 super(); 167 168 this.imageRef = new WeakReference<IcyBufferedImage>(image); 169 } 170 171 @Override 172 public Object call() throws Exception 173 { 174 final IcyBufferedImage image = imageRef.get(); 175 176 // image has been released, we probably don't need its data anymore... 177 if (image == null) 178 return null; 179 180 // not null here 181 final ImageSourceInfo imageSourceInfo = image.imageSourceInfo; 182 final SequenceIdImporter imp = imageSourceInfo.imp; 183 184 // importer not opened ? --> cannot load 185 if (StringUtil.isEmpty(imp.getOpened())) 186 throw new IOException("Cannot load image data: Sequence importer is closed."); 187 188 final int sizeC = image.getSizeC(); 189 // create the result array (always 2D native type) 190 final Object[] result = Array2DUtil.createArray(image.getDataType_(), sizeC); 191 192 // all channels ? 193 if ((imageSourceInfo.c == -1) && (sizeC > 1)) 194 { 195 // better to directly load image 196 final IcyBufferedImage newImage = imp.getImage(imageSourceInfo.series, imageSourceInfo.resolution, 197 imageSourceInfo.region, imageSourceInfo.z, imageSourceInfo.t); 198 // then get data 199 for (int c = 0; c < sizeC; c++) 200 result[c] = newImage.getDataXY(c); 201 } 202 else 203 { 204 // all channel for single channel image --> channel 0 205 final int startC = (imageSourceInfo.c == -1) ? 0 : imageSourceInfo.c; 206 // directly load pixel data 207 for (int c = 0; c < sizeC; c++) 208 result[c] = imp.getPixels(imageSourceInfo.series, imageSourceInfo.resolution, 209 imageSourceInfo.region, imageSourceInfo.z, imageSourceInfo.t, startC + c); 210 } 211 212 return result; 213 } 214 215 IcyBufferedImage getImage() 216 { 217 return imageRef.get(); 218 } 219 } 220 221 private static class ImageDataLoaderTask extends FutureTask<Object> 222 { 223 final ImageDataLoaderWorker worker; 224 225 ImageDataLoaderTask(ImageDataLoaderWorker worker) 226 { 227 super(worker); 228 229 this.worker = worker; 230 } 231 232 IcyBufferedImage getImage() 233 { 234 return worker.getImage(); 235 } 236 } 237 238 private static class ImageDataLoader 239 { 240 final ThreadPoolExecutor executor; 241 242 public ImageDataLoader() 243 { 244 super(); 245 246 int numWorker = SystemUtil.getNumberOfCPUs(); 247 executor = new ThreadPoolExecutor(numWorker, numWorker * 2, 5L, TimeUnit.SECONDS, 248 new LinkedBlockingQueue<Runnable>()); 249 } 250 251 Object loadImageData(IcyBufferedImage image) throws ExecutionException, InterruptedException 252 { 253 final ImageDataLoaderTask task = new ImageDataLoaderTask(new ImageDataLoaderWorker(image)); 254 255 executor.execute(task); 256 257 try 258 { 259 return task.get(); 260 } 261 catch (InterruptedException e) 262 { 263 // process interrupted ? remove task from executor queue if possible 264 executor.remove(task); 265 // cancel the task (without interrupting current running task as this close the importer) 266 task.cancel(false); 267 268 // re throw interrupt 269 throw e; 270 } 271 } 272 273 void cancelTasks(IcyBufferedImage image) 274 { 275 final List<ImageDataLoaderTask> tasks = new ArrayList<ImageDataLoaderTask>(); 276 final BlockingQueue<Runnable> queue = executor.getQueue(); 277 278 synchronized (queue) 279 { 280 for (Runnable task : queue) 281 { 282 final ImageDataLoaderTask imgTask = (ImageDataLoaderTask) task; 283 final IcyBufferedImage imgImage = imgTask.getImage(); 284 285 if ((imgImage == null) || (imgImage == image)) 286 tasks.add(imgTask); 287 } 288 } 289 290 // remove pending tasks for that image 291 for (ImageDataLoaderTask task : tasks) 292 { 293 executor.remove(task); 294 task.cancel(false); 295 } 296 } 297 } 298 299 /** 300 * Used for image / data loading from importer 301 */ 302 static ImageDataLoader imageDataLoader = new ImageDataLoader(); 303 304 /** 305 * Used internally to find out an image from its identity hash code 306 */ 307 static Map<Integer, WeakIcyBufferedImageReference> images = new HashMap<Integer, WeakIcyBufferedImageReference>(); 308 // static Map<Integer, Object> imagesMax = new HashMap<Integer, Object>(); 309 310 /** 311 * Retrieve an {@link IcyBufferedImage} from its identity hash code 312 */ 313 public static IcyBufferedImage getIcyBufferedImage(Integer idHashCode) 314 { 315 final WeakIcyBufferedImageReference ref; 316 317 synchronized (images) 318 { 319 ref = images.get(idHashCode); 320 } 321 322 if (ref != null) 323 return ref.get(); 324 325 return null; 326 } 327 328 /** 329 * Retrieve an {@link IcyBufferedImage} from its identity hash code 330 */ 331 public static IcyBufferedImage getIcyBufferedImage(int idHashCode) 332 { 333 return getIcyBufferedImage(Integer.valueOf(idHashCode)); 334 } 335 336 /** 337 * @deprecated 338 */ 339 @Deprecated 340 public static int TYPE_BYTE = TypeUtil.TYPE_BYTE; 341 /** 342 * @deprecated 343 */ 344 @Deprecated 345 public static int TYPE_DOUBLE = TypeUtil.TYPE_DOUBLE; 346 /** 347 * @deprecated 348 */ 349 @Deprecated 350 public static int TYPE_FLOAT = TypeUtil.TYPE_FLOAT; 351 /** 352 * @deprecated 353 */ 354 @Deprecated 355 public static int TYPE_INT = TypeUtil.TYPE_INT; 356 /** 357 * @deprecated 358 */ 359 @Deprecated 360 public static int TYPE_SHORT = TypeUtil.TYPE_SHORT; 361 /** 362 * @deprecated 363 */ 364 @Deprecated 365 public static int TYPE_UNDEFINED = TypeUtil.TYPE_UNDEFINED; 366 367 /** 368 * @deprecated Use {@link IcyBufferedImageUtil.FilterType} instead. 369 */ 370 @Deprecated 371 public static enum FilterType 372 { 373 NEAREST, BILINEAR, BICUBIC 374 }; 375 376 /** 377 * @deprecated 378 */ 379 @Deprecated 380 protected static IcyBufferedImageUtil.FilterType getNewFilterType(FilterType ft) 381 { 382 switch (ft) 383 { 384 default: 385 case NEAREST: 386 return IcyBufferedImageUtil.FilterType.NEAREST; 387 case BILINEAR: 388 return IcyBufferedImageUtil.FilterType.BILINEAR; 389 case BICUBIC: 390 return IcyBufferedImageUtil.FilterType.BICUBIC; 391 } 392 } 393 394 /** 395 * Convert a list of BufferedImage to an IcyBufferedImage (multi component).<br> 396 * IMPORTANT : source images can be used as part or as the whole result<br> 397 * so consider them as "lost" 398 * 399 * @param imageList 400 * list of {@link BufferedImage} 401 * @return {@link IcyBufferedImage} 402 * @deprecated 403 * use {@link #createFrom} instead 404 */ 405 @Deprecated 406 public static IcyBufferedImage convert(List<BufferedImage> imageList) 407 { 408 return createFrom(imageList); 409 } 410 411 /** 412 * Create an IcyBufferedImage (multi component) from a list of BufferedImage.<br> 413 * IMPORTANT: source images can be used as part or as the whole result so consider them as 'lost'. 414 * 415 * @param imageList 416 * list of {@link BufferedImage} 417 * @return {@link IcyBufferedImage} 418 * @throws IllegalArgumentException 419 * if imageList is empty or contains incompatible images. 420 */ 421 public static IcyBufferedImage createFrom(List<? extends BufferedImage> imageList) throws IllegalArgumentException 422 { 423 if (imageList.size() == 0) 424 throw new IllegalArgumentException("imageList should contains at least 1 image"); 425 426 final List<IcyBufferedImage> icyImageList = new ArrayList<IcyBufferedImage>(); 427 428 // transform images to icy images 429 for (BufferedImage image : imageList) 430 icyImageList.add(IcyBufferedImage.createFrom(image)); 431 432 final IcyBufferedImage firstImage = icyImageList.get(0); 433 434 if (icyImageList.size() == 1) 435 return firstImage; 436 437 final DataType dataType = firstImage.getDataType_(); 438 final int width = firstImage.getWidth(); 439 final int height = firstImage.getHeight(); 440 441 // calculate channel number 442 int numChannel = 0; 443 for (IcyBufferedImage image : icyImageList) 444 numChannel += image.getSizeC(); 445 446 final Object[] data = Array2DUtil.createArray(dataType, numChannel); 447 final IcyColorMap[] colormaps = new IcyColorMap[numChannel]; 448 449 // get data from all images 450 int destC = 0; 451 for (IcyBufferedImage image : icyImageList) 452 { 453 if (dataType != image.getDataType_()) 454 throw new IllegalArgumentException("All images contained in imageList should have the same dataType"); 455 if ((width != image.getWidth()) || (height != image.getHeight())) 456 throw new IllegalArgumentException("All images contained in imageList should have the same dimension"); 457 458 for (int c = 0; c < image.getSizeC(); c++) 459 { 460 data[destC] = image.getDataXY(c); 461 colormaps[destC++] = image.getColorMap(c); 462 } 463 } 464 465 // create result image 466 final IcyBufferedImage result = new IcyBufferedImage(width, height, data, dataType.isSigned()); 467 468 // restore colormaps 469 for (int c = 0; c < result.getSizeC(); c++) 470 result.setColorMap(c, colormaps[c], false); 471 472 return result; 473 } 474 475 /** 476 * Convert a BufferedImage to an IcyBufferedImage.<br> 477 * IMPORTANT : source image can be used as part or as the whole result<br> 478 * so consider it as "lost" 479 * 480 * @param image 481 * {@link BufferedImage} 482 * @return {@link IcyBufferedImage} 483 * @deprecated 484 * use {@link #createFrom} instead 485 */ 486 @Deprecated 487 public static IcyBufferedImage convert(BufferedImage image) 488 { 489 return createFrom(image); 490 } 491 492 /** 493 * Create an IcyBufferedImage from a {@link PlanarImage}.<br> 494 * IMPORTANT : source image can be used as part or as the whole result<br> 495 * so consider it as lost. 496 * 497 * @param image 498 * {@link PlanarImage} 499 * @return {@link IcyBufferedImage} 500 */ 501 public static IcyBufferedImage createFrom(PlanarImage image, boolean signedDataType) 502 { 503 final DataBuffer db = image.getData().getDataBuffer(); 504 final int w = image.getWidth(); 505 final int h = image.getHeight(); 506 507 if (db instanceof DataBufferByte) 508 return new IcyBufferedImage(w, h, ((DataBufferByte) db).getBankData(), signedDataType); 509 else if (db instanceof DataBufferShort) 510 return new IcyBufferedImage(w, h, ((DataBufferShort) db).getBankData(), signedDataType); 511 else if (db instanceof DataBufferUShort) 512 return new IcyBufferedImage(w, h, ((DataBufferUShort) db).getBankData(), signedDataType); 513 else if (db instanceof DataBufferInt) 514 return new IcyBufferedImage(w, h, ((DataBufferInt) db).getBankData(), signedDataType); 515 else if (db instanceof DataBufferFloat) 516 return new IcyBufferedImage(w, h, ((DataBufferFloat) db).getBankData(), true); 517 else if (db instanceof javax.media.jai.DataBufferFloat) 518 return new IcyBufferedImage(w, h, ((javax.media.jai.DataBufferFloat) db).getBankData(), true); 519 else if (db instanceof DataBufferDouble) 520 return new IcyBufferedImage(w, h, ((DataBufferDouble) db).getBankData(), true); 521 else if (db instanceof javax.media.jai.DataBufferDouble) 522 return new IcyBufferedImage(w, h, ((javax.media.jai.DataBufferDouble) db).getBankData(), true); 523 else 524 // JAI keep dataType and others stuff in their BufferedImage 525 return IcyBufferedImage.createFrom(image.getAsBufferedImage()); 526 } 527 528 /** 529 * Create an IcyBufferedImage from a {@link PlanarImage}.<br> 530 * IMPORTANT : source image can be used as part or as the whole result<br> 531 * so consider it as lost. 532 * 533 * @param image 534 * {@link PlanarImage} 535 * @return {@link IcyBufferedImage} 536 */ 537 public static IcyBufferedImage createFrom(PlanarImage image) 538 { 539 return createFrom(image, false); 540 } 541 542 /** 543 * Create an IcyBufferedImage from a BufferedImage.<br> 544 * IMPORTANT : source image can be used as part or as the whole result<br> 545 * so consider it as lost. 546 * 547 * @param image 548 * {@link BufferedImage} 549 * @return {@link IcyBufferedImage} 550 */ 551 public static IcyBufferedImage createFrom(BufferedImage image) 552 { 553 // IcyBufferedImage --> no conversion needed 554 if (image instanceof IcyBufferedImage) 555 return (IcyBufferedImage) image; 556 557 // sort of IcyBufferedImage (JAI can return that type) --> no conversion needed 558 if (image.getColorModel() instanceof IcyColorModel) 559 return new IcyBufferedImage( 560 IcyColorModel.createInstance((IcyColorModel) image.getColorModel(), false, false), 561 image.getRaster()); 562 563 final int w = image.getWidth(); 564 final int h = image.getHeight(); 565 final int type = image.getType(); 566 final BufferedImage temp; 567 final Graphics g; 568 569 // we first want a component based image 570 switch (type) 571 { 572 case BufferedImage.TYPE_INT_RGB: 573 case BufferedImage.TYPE_INT_BGR: 574 case BufferedImage.TYPE_USHORT_555_RGB: 575 case BufferedImage.TYPE_USHORT_565_RGB: 576 temp = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); 577 g = temp.createGraphics(); 578 g.drawImage(image, 0, 0, null); 579 g.dispose(); 580 break; 581 582 case BufferedImage.TYPE_INT_ARGB: 583 case BufferedImage.TYPE_INT_ARGB_PRE: 584 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 585 temp = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR); 586 g = temp.createGraphics(); 587 g.drawImage(image, 0, 0, null); 588 g.dispose(); 589 break; 590 591 case BufferedImage.TYPE_3BYTE_BGR: 592 case BufferedImage.TYPE_4BYTE_ABGR: 593 temp = image; 594 break; 595 596 default: 597 // if we have severals components with an unknown / incompatible sampleModel 598 if ((image.getColorModel().getNumComponents() > 1) 599 && (!(image.getSampleModel() instanceof ComponentSampleModel))) 600 { 601 // change it to a basic ABGR components image 602 temp = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR); 603 g = temp.createGraphics(); 604 g.drawImage(image, 0, 0, null); 605 g.dispose(); 606 } 607 else 608 temp = image; 609 break; 610 } 611 612 // convert initial data type in our data type 613 final DataType dataType = DataType.getDataTypeFromDataBufferType(temp.getColorModel().getTransferType()); 614 // get number of components 615 final int numComponents = temp.getRaster().getNumBands(); 616 617 // create a compatible image in our format 618 final IcyBufferedImage result = new IcyBufferedImage(w, h, numComponents, dataType); 619 620 // copy data from the source image 621 result.copyData(temp); 622 623 // in some case we want to restore colormaps from source image 624 switch (type) 625 { 626 case BufferedImage.TYPE_BYTE_BINARY: 627 case BufferedImage.TYPE_BYTE_INDEXED: 628 if (numComponents == 2) 629 result.setColorMaps(image); 630 break; 631 632 case BufferedImage.TYPE_INT_ARGB: 633 case BufferedImage.TYPE_INT_ARGB_PRE: 634 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 635 case BufferedImage.TYPE_4BYTE_ABGR: 636 if (numComponents == 4) 637 result.setColorMap(3, LinearColorMap.alpha_, true); 638 break; 639 } 640 641 return result; 642 } 643 644 /** 645 * @deprecated Use 646 * {@link LociImporterPlugin#getThumbnailCompatible(IFormatReader, int, int, int)} 647 * instead. 648 */ 649 @Deprecated 650 public static IcyBufferedImage createCompatibleThumbnailFrom(IFormatReader reader, int z, int t) 651 throws FormatException, IOException 652 { 653 return LociImporterPlugin.getThumbnailCompatible(reader, z, t, -1); 654 } 655 656 /** 657 * @deprecated Use {@link LociImporterPlugin#getThumbnail(IFormatReader, int, int, int)} 658 * instead. 659 */ 660 @Deprecated 661 public static IcyBufferedImage createThumbnailFrom(IFormatReader reader, int z, int t) 662 throws FormatException, IOException 663 { 664 return LociImporterPlugin.getThumbnail(reader, z, t, -1); 665 } 666 667 /** 668 * @deprecated Use {@link LociImporterPlugin#getImage(IFormatReader, Rectangle, int, int, int, int)} 669 * instead. 670 */ 671 @Deprecated 672 public static IcyBufferedImage createFrom(IFormatReader reader, int x, int y, int w, int h, int z, int t, int c) 673 throws FormatException, IOException 674 { 675 return LociImporterPlugin.getImage(reader, new Rectangle(x, y, w, h), z, t, c, 0); 676 } 677 678 /** 679 * @deprecated Use {@link LociImporterPlugin#getImage(IFormatReader, Rectangle, int, int)} 680 * instead. 681 */ 682 @Deprecated 683 public static IcyBufferedImage createFrom(IFormatReader reader, int z, int t) throws FormatException, IOException 684 { 685 return LociImporterPlugin.getImage(reader, null, z, t); 686 } 687 688 /** 689 * @deprecated Use {@link #IcyBufferedImage(int, int, IcyColorModel)} instead. 690 */ 691 @Deprecated 692 public static IcyBufferedImage createEmptyImage(int width, int height, IcyColorModel cm) 693 { 694 return new IcyBufferedImage(width, height, cm); 695 } 696 697 /** 698 * Image source information used for delayed image loading 699 */ 700 protected ImageSourceInfo imageSourceInfo; 701 702 /** 703 * automatic update of channel bounds 704 */ 705 protected boolean autoUpdateChannelBounds; 706 707 /** 708 * required cached field as raster is volatile 709 */ 710 protected final int width; 711 protected final int height; 712 protected final int minX; 713 protected final int minY; 714 protected final int offsetX; 715 protected final int offsetY; 716 717 /** 718 * parent <i>raster</i> field (required for volatile data) 719 */ 720 protected Field rasterField; 721 722 /** 723 * data initialized state 724 */ 725 protected boolean dataInitialized; 726 727 // internal lock counter 728 protected int lockedCount = 0; 729 // internal constructed state (needed for proper data initialization) 730 private boolean constructed = false; 731 732 /** 733 * internal updater 734 */ 735 protected final UpdateEventHandler updater; 736 /** 737 * listeners 738 */ 739 protected final List<IcyBufferedImageListener> listeners; 740 741 /** 742 * Build an Icy formatted BufferedImage, takes an IcyColorModel and a WritableRaster as input 743 * 744 * @param cm 745 * {@link IcyColorModel} 746 * @param wr 747 * {@link WritableRaster} 748 * @param autoUpdateChannelBounds 749 * If true then channel bounds are automatically calculated. 750 * @param dataInitialized 751 * When set to <code>true</code> (default), we assume the image is created with initialized data (stored in the {@link WritableRaster} object) 752 * otherwise data will be initialized / loaded on first data request (lazy loading). 753 * @param forceVolatileData 754 * If set to <code>true</code> then image data is volatile regardless of {@link GeneralPreferences#getVirtualMode()} state and can be lost if not 755 * specifically stored using <code>setDataxx(..)</code> methods.<br> 756 * Image cache is used to handle data storage and can move data on disk when memory is getting low.<br> 757 * Note that Default value for this parameter is <code>false</code>. 758 */ 759 protected IcyBufferedImage(IcyColorModel cm, WritableRaster wr, boolean autoUpdateChannelBounds, 760 boolean dataInitialized, boolean forceVolatileData) 761 { 762 super(cm, wr, false, null); 763 764 // store it in the hashmap (weak reference) 765 synchronized (images) 766 { 767 images.put(Integer.valueOf(System.identityHashCode(this)), new WeakIcyBufferedImageReference(this)); 768 } 769 770 imageSourceInfo = null; 771 width = wr.getWidth(); 772 height = wr.getHeight(); 773 minX = wr.getMinX(); 774 minY = wr.getMinY(); 775 offsetX = wr.getSampleModelTranslateX(); 776 offsetY = wr.getSampleModelTranslateY(); 777 778 // automatic update of channel bounds 779 this.autoUpdateChannelBounds = autoUpdateChannelBounds; 780 781 updater = new UpdateEventHandler(this, false); 782 listeners = new ArrayList<IcyBufferedImageListener>(); 783 784 // default 785 rasterField = null; 786 787 // we want volatile data ? 788 if (forceVolatileData || GeneralPreferences.getVirtualMode()) 789 { 790 try 791 { 792 // we need access to parent raster field 793 rasterField = ReflectionUtil.getField(BufferedImage.class, "raster", true); 794 // set it to null so it won't retain data 795 if (rasterField != null) 796 rasterField.set(this, null); 797 } 798 catch (Exception e) 799 { 800 System.out.println(e.getMessage()); 801 System.out.println("Warning: cannot get access to internal BufferedImage.raster field."); 802 System.out.println(" Image caching cannot be used for this image."); 803 804 // no access to parent raster... 805 rasterField = null; 806 } 807 } 808 809 this.dataInitialized = dataInitialized; 810 811 // we have initialized data ? --> save them in cache 812 if (dataInitialized) 813 { 814 // save data in cache (for volatile image) 815 saveRasterInCache(wr, true); 816 // update image components bounds 817 if (autoUpdateChannelBounds) 818 updateChannelsBounds(); 819 } 820 // else 821 // { 822 // // we want to force loading from Importer if needed so we don't reference data 823 // volatileRaster = null; 824 // volatileData = null; 825 // } 826 827 // add listener to colorModel 828 cm.addListener(this); 829 830 constructed = true; 831 } 832 833 /** 834 * Build an Icy formatted BufferedImage, takes an IcyColorModel and a WritableRaster as input 835 * 836 * @param cm 837 * {@link IcyColorModel} 838 * @param wr 839 * {@link WritableRaster} 840 * @param autoUpdateChannelBounds 841 * If true then channel bounds are automatically calculated.<br> 842 */ 843 protected IcyBufferedImage(IcyColorModel cm, WritableRaster wr, boolean autoUpdateChannelBounds) 844 { 845 this(cm, wr, autoUpdateChannelBounds, true, false); 846 } 847 848 /** 849 * Create an Icy formatted BufferedImage, takes an IcyColorModel and a WritableRaster as input 850 * 851 * @param cm 852 * {@link IcyColorModel} 853 * @param wr 854 * {@link WritableRaster} 855 */ 856 protected IcyBufferedImage(IcyColorModel cm, WritableRaster wr) 857 { 858 this(cm, wr, false, true, false); 859 860 } 861 862 /** 863 * Create an Icy formatted BufferedImage with specified IcyColorModel, width and height.<br> 864 * Private version, {@link IcyColorModel} is directly used internally. 865 */ 866 protected IcyBufferedImage(IcyColorModel cm, int width, int height, boolean forceVolatileData) 867 { 868 this(cm, cm.createCompatibleWritableRaster(width, height), false, false, forceVolatileData); 869 } 870 871 /** 872 * Create an Icy formatted BufferedImage with specified IcyColorModel, width and height.<br> 873 * Private version, {@link IcyColorModel} is directly used internally. 874 */ 875 protected IcyBufferedImage(IcyColorModel cm, int width, int height) 876 { 877 this(cm, cm.createCompatibleWritableRaster(width, height), false, false, false); 878 } 879 880 /** 881 * Create an Icy formatted BufferedImage with specified colorModel, width, height and input data.<br> 882 * ex : <code>img = new IcyBufferedImage(640, 480, new byte[3][640 * 480], true);</code><br> 883 * <br> 884 * This constructor provides the best performance for massive image creation and computation as it allow you to 885 * directly send the data array and disable the channel bounds calculation. 886 * 887 * @param cm 888 * the color model 889 * @param width 890 * @param height 891 * @param data 892 * image data<br> 893 * Should be a 2D array with first dimension giving the number of component<br> 894 * and second dimension equals to <code>width * height</code><br> 895 * The array data type specify the internal data type and should match the given color 896 * model parameter. 897 * @param autoUpdateChannelBounds 898 * If true then channel bounds are automatically calculated.<br> 899 * When set to false, you have to set bounds manually by calling 900 * {@link #updateChannelsBounds()} or #setC 901 */ 902 protected IcyBufferedImage(IcyColorModel cm, Object[] data, int width, int height, boolean autoUpdateChannelBounds) 903 { 904 this(cm, cm.createWritableRaster(data, width, height), autoUpdateChannelBounds); 905 } 906 907 /** 908 * Create an Icy formatted BufferedImage with specified width, height and input data.<br> 909 * ex : <code>img = new IcyBufferedImage(640, 480, new byte[3][640 * 480], true);</code><br> 910 * <br> 911 * This constructor provides the best performance for massive image creation and computation as 912 * it allow you to directly send the data array and disable the channel bounds calculation. 913 * 914 * @param width 915 * @param height 916 * @param data 917 * image data<br> 918 * Should be a 2D array with first dimension giving the number of component<br> 919 * and second dimension equals to <code>width * height</code><br> 920 * The array data type specify the internal data type. 921 * @param signed 922 * use signed data for data type 923 * @param autoUpdateChannelBounds 924 * If true then channel bounds are automatically calculated.<br> 925 * When set to false, you have to set bounds manually by calling 926 * {@link #updateChannelsBounds()} or #setC 927 */ 928 public IcyBufferedImage(int width, int height, Object[] data, boolean signed, boolean autoUpdateChannelBounds) 929 { 930 this(IcyColorModel.createInstance(data.length, ArrayUtil.getDataType(data[0], signed)), data, width, height, 931 autoUpdateChannelBounds); 932 } 933 934 /** 935 * Create an Icy formatted BufferedImage with specified width, height and input data.<br> 936 * ex : <code>img = new IcyBufferedImage(640, 480, new byte[3][640 * 480]);</code> 937 * 938 * @param width 939 * @param height 940 * @param data 941 * image data<br> 942 * Should be a 2D array with first dimension giving the number of component<br> 943 * and second dimension equals to <code>width * height</code><br> 944 * The array data type specify the internal data type. 945 * @param signed 946 * use signed data for data type 947 */ 948 public IcyBufferedImage(int width, int height, Object[] data, boolean signed) 949 { 950 this(IcyColorModel.createInstance(data.length, ArrayUtil.getDataType(data[0], signed)), data, width, height, 951 false); 952 } 953 954 /** 955 * Create an Icy formatted BufferedImage with specified width, height and input data.<br> 956 * ex : <code>img = new IcyBufferedImage(640, 480, new byte[3][640 * 480]);</code> 957 * 958 * @param width 959 * @param height 960 * @param data 961 * image data<br> 962 * Should be a 2D array with first dimension giving the number of component<br> 963 * and second dimension equals to <code>width * height</code><br> 964 * The array data type specify the internal data type. 965 */ 966 public IcyBufferedImage(int width, int height, Object[] data) 967 { 968 this(width, height, data, false); 969 } 970 971 /** 972 * Create a single channel Icy formatted BufferedImage with specified width, height and input data.<br> 973 * ex : <code>img = new IcyBufferedImage(640, 480, new byte[640 * 480], true);</code><br> 974 * <br> 975 * This constructor provides the best performance for massive image creation and computation as it allow you to 976 * directly send the data array and disable the channel bounds calculation. 977 * 978 * @param width 979 * @param height 980 * @param data 981 * image data array.<br> 982 * The length of the array should be equals to <code>width * height</code>.<br> 983 * The array data type specify the internal data type. 984 * @param signed 985 * use signed data for data type 986 * @param autoUpdateChannelBounds 987 * If true then channel bounds are automatically calculated.<br> 988 * When set to false, you have to set bounds manually by calling 989 * {@link #updateChannelsBounds()} or #setC 990 * @see #IcyBufferedImage(int, int, Object[], boolean, boolean) 991 */ 992 public IcyBufferedImage(int width, int height, Object data, boolean signed, boolean autoUpdateChannelBounds) 993 { 994 this(width, height, ArrayUtil.encapsulate(data), signed, autoUpdateChannelBounds); 995 } 996 997 /** 998 * Create a single channel Icy formatted BufferedImage with specified width, height and input 999 * data.<br> 1000 * ex : <code>img = new IcyBufferedImage(640, 480, new byte[640 * 480]);</code> 1001 * 1002 * @param width 1003 * @param height 1004 * @param data 1005 * image data<br> 1006 * The length of the array should be equals to <code>width * height</code>.<br> 1007 * The array data type specify the internal data type. 1008 * @param signed 1009 * use signed data for data type 1010 */ 1011 public IcyBufferedImage(int width, int height, Object data, boolean signed) 1012 { 1013 this(width, height, ArrayUtil.encapsulate(data), signed); 1014 } 1015 1016 /** 1017 * Create a single channel Icy formatted BufferedImage with specified width, height and input 1018 * data.<br> 1019 * ex : <code>img = new IcyBufferedImage(640, 480, new byte[640 * 480]);</code> 1020 * 1021 * @param width 1022 * @param height 1023 * @param data 1024 * image data<br> 1025 * The length of the array should be equals to <code>width * height</code>.<br> 1026 * The array data type specify the internal data type. 1027 */ 1028 public IcyBufferedImage(int width, int height, Object data) 1029 { 1030 this(width, height, ArrayUtil.encapsulate(data)); 1031 } 1032 1033 /** 1034 * Create an ICY formatted BufferedImage with specified width, height,<br> 1035 * number of component and dataType. 1036 * 1037 * @param width 1038 * @param height 1039 * @param numComponents 1040 * @param dataType 1041 * image data type {@link DataType} 1042 */ 1043 public IcyBufferedImage(int width, int height, int numComponents, DataType dataType, boolean forceVolatileData) 1044 { 1045 this(IcyColorModel.createInstance(numComponents, dataType), width, height, forceVolatileData); 1046 } 1047 1048 /** 1049 * Create an ICY formatted BufferedImage with specified width, height,<br> 1050 * number of component and dataType. 1051 * 1052 * @param width 1053 * @param height 1054 * @param numComponents 1055 * @param dataType 1056 * image data type {@link DataType} 1057 */ 1058 public IcyBufferedImage(int width, int height, int numComponents, DataType dataType) 1059 { 1060 this(IcyColorModel.createInstance(numComponents, dataType), width, height, false); 1061 } 1062 1063 /** 1064 * Create an ICY formatted BufferedImage with specified width, height and IcyColorModel 1065 * type.<br> 1066 */ 1067 public IcyBufferedImage(int width, int height, IcyColorModel cm) 1068 { 1069 this(width, height, cm.getNumComponents(), cm.getDataType_()); 1070 } 1071 1072 /** 1073 * @deprecated use {@link #IcyBufferedImage(int, int, int, DataType)} instead 1074 */ 1075 @Deprecated 1076 public IcyBufferedImage(int width, int height, int numComponents, int dataType, boolean signed) 1077 { 1078 this(IcyColorModel.createInstance(numComponents, dataType, signed), width, height); 1079 } 1080 1081 /** 1082 * @deprecated use {@link #IcyBufferedImage(int, int, int, DataType)} instead 1083 */ 1084 @Deprecated 1085 public IcyBufferedImage(int width, int height, int numComponents, int dataType) 1086 { 1087 this(IcyColorModel.createInstance(numComponents, dataType, false), width, height); 1088 } 1089 1090 @Override 1091 protected void finalize() throws Throwable 1092 { 1093 // cancel any pending loading tasks for this image 1094 imageDataLoader.cancelTasks(this); 1095 // image has been released, be sure to clear cache 1096 ImageCache.remove(this); 1097 1098 // remove it from hashmap 1099 synchronized (images) 1100 { 1101 images.remove(Integer.valueOf(System.identityHashCode(this))); 1102 } 1103 1104 super.finalize(); 1105 } 1106 1107 public ImageSourceInfo getImageSourceInfo() 1108 { 1109 return imageSourceInfo; 1110 } 1111 1112 /** 1113 * Set the image source information that will be used later for lazy image data loading. 1114 */ 1115 public void setImageSourceInfo(SequenceIdImporter imp, int series, int resolution, Rectangle region, int t, int z, 1116 int c) 1117 { 1118 imageSourceInfo = new ImageSourceInfo(imp, series, resolution, region, t, z, c); 1119 } 1120 1121 /** 1122 * Returns <code>true</code> if data is initialized 1123 */ 1124 public boolean isDataInitialized() 1125 { 1126 return dataInitialized; 1127 } 1128 1129 /** 1130 * Returns <code>true</code> if data is currently loaded in memory.<br> 1131 * It returns <code>false</code> if data has not yet be initialized (see {@link #isDataInitialized()}) or if data is cached on disk (not anymore in memory) 1132 */ 1133 public boolean isDataInMemory() 1134 { 1135 return ImageCache.isOnMemoryCache(this); 1136 } 1137 1138 /** 1139 * Returns <code>true</code> if image data is volatile.<br> 1140 * Volatile data means <b>there is no strong reference on the internal data arrays</b> (data can be cached on disk) and so <b>any <i>external</i> changes on 1141 * them can be lost</b> if they has not been specifically set using setDataxx() methods. 1142 * Volatile is useful when you want to load many images with low memory consumption but you should use it carefully as it has some limitations.<br> 1143 * 1144 * @see #setVolatile(boolean) 1145 */ 1146 public boolean isVolatile() 1147 { 1148 return rasterField != null; 1149 } 1150 1151 /** 1152 * Sets the <i>volatile</i> state for this image.<br> 1153 * Volatile data means <b>there is no strong reference on the internal data arrays</b> (data can be cached on disk) and so <b>any <i>external</i> changes on 1154 * them can be lost</b> if they has not been specifically set using setDataxx() methods. 1155 * Volatile is useful when you want to load many images with low memory consumption but you should use it carefully as it has some limitations.<br> 1156 * Setting the image to volatile <b>immediately release internal strong reference on data arrays</b> (see {@link #lockRaster()} and 1157 * {@link #releaseRaster(boolean)} methods).<br> 1158 * 1159 * @throws OutOfMemoryError 1160 * if there is not enough memory available to store image 1161 * data when setting back to <i>non volatile</i> state 1162 * @throws UnsupportedOperationException 1163 * if cache engine is not initialized (error at initialization). 1164 */ 1165 public void setVolatile(boolean value) throws OutOfMemoryError, UnsupportedOperationException 1166 { 1167 // we want volatile data ? 1168 if (value) 1169 { 1170 if (rasterField == null) 1171 { 1172 // cache engine couldn't be used 1173 if (!ImageCache.isEnabled()) 1174 throw new UnsupportedOperationException( 1175 "IcyBufferedImage.setVolatile(..) error: Image cache is disabled !"); 1176 1177 try 1178 { 1179 // we need access to parent raster field 1180 rasterField = ReflectionUtil.getField(BufferedImage.class, "raster", true); 1181 1182 if (rasterField != null) 1183 { 1184 // save data in cache as we will release the strong reference 1185 saveDataInCache(); 1186 // immediately remove strong reference to data 1187 rasterField.set(this, null); 1188 } 1189 } 1190 catch (Exception e) 1191 { 1192 System.out.println(e.getMessage()); 1193 System.out.println("Warning: cannot get access to internal BufferedImage.raster field."); 1194 System.out.println(" Image caching cannot be used for this image."); 1195 1196 // no access to parent raster... 1197 rasterField = null; 1198 } 1199 } 1200 } 1201 // no volatile anymore ? 1202 else 1203 { 1204 // were we volatile ? 1205 if (rasterField != null) 1206 { 1207 try 1208 { 1209 // need to set back raster strong reference 1210 if (super.getRaster() == null) 1211 { 1212 // data not yet initialized ? 1213 if (!isDataInitialized()) 1214 // we don't want to force data loading for that 1215 rasterField.set(this, buildRaster(createEmptyRasterData())); 1216 else 1217 rasterField.set(this, getRaster()); 1218 } 1219 1220 // set it to null so it won't retain data 1221 rasterField = null; 1222 // clear data set in cache (no more used) 1223 ImageCache.remove(this); 1224 } 1225 catch (OutOfMemoryError e) 1226 { 1227 System.err.println(e.getMessage()); 1228 System.err.println( 1229 "IcyBufferedImage.setVolatile(false) error: not enough memory to set image data back in memory."); 1230 throw e; 1231 } 1232 catch (Throwable e) 1233 { 1234 System.err.println(e.getMessage()); 1235 System.err.println( 1236 "IcyBufferedImage.setVolatile(..) error: cannot set parent raster field (data lost)."); 1237 } 1238 } 1239 } 1240 } 1241 1242 /** 1243 * Force loading data for this image (so channel bounds can be correctly computed even with lazy loading) 1244 */ 1245 public void loadData() 1246 { 1247 if (!isDataInitialized()) 1248 // that is enough to get data loaded 1249 getRaster(); 1250 } 1251 1252 protected synchronized WritableRaster getRaster(boolean retain) 1253 { 1254 // always try first from parent 1255 WritableRaster result = super.getRaster(); 1256 1257 // data not yet initialized ? 1258 if (constructed && !isDataInitialized()) 1259 { 1260 // initialize data 1261 final Object rasterData = initializeData(); 1262 1263 // could not initialize data ? --> use temporary empty data (we want to retry data initialization later) 1264 if (rasterData == null) 1265 return buildRaster(createEmptyRasterData()); 1266 1267 // save them in cache (for volatile image) but don't need to be eternal 1268 saveRasterDataInCache(rasterData, false); 1269 // we have the parent raster ? --> update its data (important to do it before setting data initialized) 1270 if (result != null) 1271 setRasterData(result, rasterData); 1272 1273 // data is initialized (important to set it before updating channel bounds) 1274 dataInitialized = true; 1275 1276 // update image channels bounds 1277 if (autoUpdateChannelBounds) 1278 updateChannelsBounds(); 1279 } 1280 1281 // we don't have the parent raster ? (mean we have a volatile image) 1282 if (result == null) 1283 { 1284 // get it from cache 1285 result = loadRasterFromCache(); 1286 1287 try 1288 { 1289 // store it in the original strong reference if asked 1290 if (retain && (rasterField != null)) 1291 rasterField.set(this, result); 1292 } 1293 catch (Exception e) 1294 { 1295 System.out.println(e.getMessage()); 1296 System.out.println("Warning: cannot set internal BufferedImage.raster field."); 1297 } 1298 } 1299 1300 return result; 1301 } 1302 1303 @Override 1304 public WritableRaster getRaster() 1305 { 1306 return getRaster(false); 1307 } 1308 1309 @Override 1310 public WritableRaster getAlphaRaster() 1311 { 1312 return getColorModel().getAlphaRaster(getRaster()); 1313 } 1314 1315 /** 1316 * Return <code>true</code> if raster data is strongly referenced, <code>false</code> otherwise.<br> 1317 * Note that it doesn't necessary mean that we called {@link #lockRaster()} method. 1318 * 1319 * @see #lockRaster() 1320 * @see #isVolatile() 1321 */ 1322 public synchronized boolean isRasterLocked() 1323 { 1324 try 1325 { 1326 return (rasterField == null) || (rasterField.get(this) != null); 1327 } 1328 catch (Exception e) 1329 { 1330 // something bad happened, just assume false then.. 1331 return false; 1332 } 1333 } 1334 1335 /** 1336 * Ensure raster data remains strongly referenced until we call {@link #releaseRaster(boolean)}.<br> 1337 * This is important to lock / release raster for Volatile image when you are modifying data externally otherwise data could be lost. 1338 * 1339 * @see #releaseRaster(boolean) 1340 * @see #isVolatile() 1341 */ 1342 public synchronized void lockRaster() 1343 { 1344 if (lockedCount++ != 0) 1345 return; 1346 1347 getRaster(true); 1348 } 1349 1350 /** 1351 * Release the raster object. 1352 * 1353 * @param saveInCache 1354 * force to save raster data in cache (for volatile image only) 1355 */ 1356 public synchronized void releaseRaster(boolean saveInCache) 1357 { 1358 if (--lockedCount != 0) 1359 return; 1360 1361 try 1362 { 1363 // force saving changed data in cache before releasing raster 1364 if (saveInCache) 1365 saveDataInCache(); 1366 // set parent raster to null (release raster object) 1367 if (rasterField != null) 1368 rasterField.set(this, null); 1369 } 1370 catch (Exception e) 1371 { 1372 System.out.println(e.getMessage()); 1373 System.out.println("Warning: cannot set internal BufferedImage.raster field."); 1374 } 1375 } 1376 1377 @Override 1378 public int getWidth() 1379 { 1380 return width; 1381 } 1382 1383 @Override 1384 public int getHeight() 1385 { 1386 return height; 1387 } 1388 1389 @Override 1390 public int getWidth(ImageObserver observer) 1391 { 1392 return width; 1393 } 1394 1395 @Override 1396 public int getHeight(ImageObserver observer) 1397 { 1398 return height; 1399 } 1400 1401 @Override 1402 public int getMinX() 1403 { 1404 return minX; 1405 } 1406 1407 @Override 1408 public int getMinY() 1409 { 1410 return minY; 1411 } 1412 1413 @Override 1414 public int getTileWidth() 1415 { 1416 return getWidth(); 1417 } 1418 1419 @Override 1420 public int getTileHeight() 1421 { 1422 return getHeight(); 1423 } 1424 1425 @Override 1426 public int getTileGridXOffset() 1427 { 1428 return offsetX; 1429 } 1430 1431 @Override 1432 public int getTileGridYOffset() 1433 { 1434 return offsetY; 1435 } 1436 1437 @Override 1438 public SampleModel getSampleModel() 1439 { 1440 return getRaster().getSampleModel(); 1441 } 1442 1443 @Override 1444 public void coerceData(boolean isAlphaPremultiplied) 1445 { 1446 // don't need to do any conversion here... 1447 } 1448 1449 @Override 1450 public WritableRaster copyData(WritableRaster outRaster) 1451 { 1452 lockRaster(); 1453 try 1454 { 1455 return super.copyData(outRaster); 1456 } 1457 finally 1458 { 1459 releaseRaster(true); 1460 } 1461 } 1462 1463 @Override 1464 public Raster getTile(int tileX, int tileY) 1465 { 1466 lockRaster(); 1467 try 1468 { 1469 return super.getTile(tileX, tileY); 1470 } 1471 finally 1472 { 1473 releaseRaster(false); 1474 } 1475 } 1476 1477 @Override 1478 public WritableRaster getWritableTile(int tileX, int tileY) 1479 { 1480 lockRaster(); 1481 try 1482 { 1483 return super.getWritableTile(tileX, tileY); 1484 } 1485 finally 1486 { 1487 releaseRaster(false); 1488 } 1489 } 1490 1491 @Override 1492 public Raster getData() 1493 { 1494 lockRaster(); 1495 try 1496 { 1497 return super.getData(); 1498 } 1499 finally 1500 { 1501 releaseRaster(false); 1502 } 1503 } 1504 1505 @Override 1506 public Raster getData(Rectangle rect) 1507 { 1508 lockRaster(); 1509 try 1510 { 1511 return super.getData(rect); 1512 } 1513 finally 1514 { 1515 releaseRaster(false); 1516 } 1517 } 1518 1519 @Override 1520 public void setData(Raster r) 1521 { 1522 lockRaster(); 1523 try 1524 { 1525 super.setData(r); 1526 } 1527 finally 1528 { 1529 releaseRaster(false); 1530 } 1531 } 1532 1533 @Override 1534 public int getRGB(int x, int y) 1535 { 1536 lockRaster(); 1537 try 1538 { 1539 return super.getRGB(x, y); 1540 } 1541 finally 1542 { 1543 releaseRaster(false); 1544 } 1545 } 1546 1547 @Override 1548 public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) 1549 { 1550 lockRaster(); 1551 try 1552 { 1553 return super.getRGB(startX, startY, w, h, rgbArray, offset, scansize); 1554 } 1555 finally 1556 { 1557 releaseRaster(false); 1558 } 1559 } 1560 1561 @Override 1562 public synchronized void setRGB(int x, int y, int rgb) 1563 { 1564 lockRaster(); 1565 try 1566 { 1567 super.setRGB(x, y, rgb); 1568 } 1569 finally 1570 { 1571 // FIXME: implement delayed cache saving to avoid very poor performance here 1572 releaseRaster(true); 1573 } 1574 } 1575 1576 @Override 1577 public void setRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) 1578 { 1579 lockRaster(); 1580 try 1581 { 1582 super.setRGB(startX, startY, w, h, rgbArray, offset, scansize); 1583 } 1584 finally 1585 { 1586 releaseRaster(true); 1587 } 1588 } 1589 1590 /** 1591 * Return the owner sequence (can be null if the image is not owned in any Sequence) 1592 */ 1593 public Sequence getOwnerSequence() 1594 { 1595 // just use the listeners to find it (Sequence always listen image event) 1596 for (IcyBufferedImageListener listener : listeners) 1597 if (listener instanceof Sequence) 1598 return (Sequence) listener; 1599 1600 return null; 1601 } 1602 1603 protected WritableRaster loadRasterFromCache() 1604 { 1605 Object rasterData = null; 1606 boolean datalost = false; 1607 1608 try 1609 { 1610 // get data from cache 1611 rasterData = ImageCache.get(this); 1612 } 1613 catch (Throwable e) 1614 { 1615 datalost = true; 1616 System.err.println(e.getMessage()); 1617 } 1618 1619 // should happen only for unmodified data 1620 if (rasterData == null) 1621 { 1622 // we should be able to initialize data back 1623 rasterData = initializeData(); 1624 1625 // couldn't initialize data ? create empty data without saving in cache (we want to retry later) 1626 if (rasterData == null) 1627 { 1628 rasterData = createEmptyRasterData(); 1629 1630 //// don't notify twice 1631 // if (!datalost) 1632 // System.err.println("IcyBufferedImage.loadRasterFromCache: cannot get image data (data lost)"); 1633 } 1634 else 1635 { 1636 // data could not be loaded from cache but was correctly restored 1637 if (datalost) 1638 System.out.println("Data re-initialized (changes are lost)"); 1639 1640 // save it in cache (not eternal here as this is default data) 1641 saveRasterDataInCache(rasterData, false); 1642 } 1643 } 1644 1645 return buildRaster(rasterData); 1646 } 1647 1648 /** 1649 * Explicitly save the image data in cache (only for volatile image) 1650 * 1651 * @see #isVolatile() 1652 */ 1653 public void saveDataInCache() 1654 { 1655 // need to be saved only if initialized 1656 if (isDataInitialized()) 1657 saveRasterInCache(getRaster()); 1658 } 1659 1660 protected void saveRasterInCache(WritableRaster wr, boolean eternal) 1661 { 1662 saveRasterDataInCache(getRasterData(wr), eternal); 1663 } 1664 1665 protected void saveRasterInCache(WritableRaster wr) 1666 { 1667 saveRasterInCache(wr, true); 1668 } 1669 1670 protected void saveRasterDataInCache(Object rasterData, boolean eternal) 1671 { 1672 // save data in cache (volatile image only) 1673 if (isVolatile()) 1674 { 1675 try 1676 { 1677 ImageCache.set(this, rasterData, eternal); 1678 } 1679 catch (Throwable e) 1680 { 1681 System.err.println(e.getMessage()); 1682 } 1683 } 1684 } 1685 1686 protected void saveRasterDataInCache(Object rasterData) 1687 { 1688 saveRasterDataInCache(rasterData, true); 1689 } 1690 1691 /** 1692 * Build raster from an Object (internally the Object is always a 2D array of native data type) 1693 */ 1694 protected WritableRaster buildRaster(Object data) 1695 { 1696 return IcyColorModel.createWritableRaster(data, getWidth(), getHeight()); 1697 } 1698 1699 /** 1700 * Returns raster data in Object format (internally we always have a 2D array of native data type) 1701 */ 1702 protected static Object getRasterData(WritableRaster wr) 1703 { 1704 final DataBuffer db = wr.getDataBuffer(); 1705 1706 switch (db.getDataType()) 1707 { 1708 case DataBuffer.TYPE_BYTE: 1709 return ((DataBufferByte) db).getBankData(); 1710 case DataBuffer.TYPE_USHORT: 1711 return ((DataBufferUShort) db).getBankData(); 1712 case DataBuffer.TYPE_SHORT: 1713 return ((DataBufferShort) db).getBankData(); 1714 case DataBuffer.TYPE_INT: 1715 return ((DataBufferInt) db).getBankData(); 1716 case DataBuffer.TYPE_FLOAT: 1717 return ((DataBufferFloat) db).getBankData(); 1718 case DataBuffer.TYPE_DOUBLE: 1719 return ((DataBufferDouble) db).getBankData(); 1720 default: 1721 return null; 1722 } 1723 } 1724 1725 /** 1726 * Set raster data (Object is always a 2D array of native data type) 1727 */ 1728 protected void setRasterData(WritableRaster wr, Object data) 1729 { 1730 final Object dest; 1731 final DataBuffer db = wr.getDataBuffer(); 1732 1733 switch (db.getDataType()) 1734 { 1735 case DataBuffer.TYPE_BYTE: 1736 dest = ((DataBufferByte) db).getBankData(); 1737 break; 1738 case DataBuffer.TYPE_USHORT: 1739 dest = ((DataBufferUShort) db).getBankData(); 1740 break; 1741 case DataBuffer.TYPE_SHORT: 1742 dest = ((DataBufferShort) db).getBankData(); 1743 break; 1744 case DataBuffer.TYPE_INT: 1745 dest = ((DataBufferInt) db).getBankData(); 1746 break; 1747 case DataBuffer.TYPE_FLOAT: 1748 dest = ((DataBufferFloat) db).getBankData(); 1749 break; 1750 case DataBuffer.TYPE_DOUBLE: 1751 dest = ((DataBufferDouble) db).getBankData(); 1752 break; 1753 default: 1754 dest = null; 1755 break; 1756 } 1757 1758 if (dest != null) 1759 { 1760 final int len = Array.getLength(dest); 1761 for (int i = 0; i < len; i++) 1762 { 1763 final Object destSub = Array.get(dest, i); 1764 System.arraycopy(Array.get(data, i), 0, destSub, 0, Array.getLength(destSub)); 1765 } 1766 } 1767 } 1768 1769 protected Object initializeData() 1770 { 1771 try 1772 { 1773 // load data from importer 1774 return loadDataFromImporter(); 1775 } 1776 catch (InterruptedException e) 1777 { 1778 System.err.println( 1779 "IcyBufferedImage.loadDataFromImporter() warning: image loading from ImageProvider was interrupted (image data not retrieved)."); 1780 1781 // we want to keep the interrupted state here 1782 Thread.currentThread().interrupt(); 1783 1784 return null; 1785 } 1786 catch (ClosedByInterruptException e) 1787 { 1788 // this one should never happen as loading is done in a separate thread (executor) 1789 System.err.println( 1790 "IcyBufferedImage.loadDataFromImporter() error: image loading from ImageProvider was interrupted (further image won't be loaded) !"); 1791 1792 // we want to keep the interrupted state here 1793 Thread.currentThread().interrupt(); 1794 1795 return null; 1796 } 1797 catch (Exception e) 1798 { 1799 System.err.println(e); 1800 System.err.println( 1801 "IcyBufferedImage.loadDataFromImporter() warning: cannot get image from ImageProvider (possible data loss)."); 1802 1803 return null; 1804 } 1805 } 1806 1807 protected Object createEmptyRasterData() 1808 { 1809 final DataType dataType = getDataType_(); 1810 final int sizeC = getSizeC(); 1811 final int sizeXY = getSizeX() * getSizeY(); 1812 1813 // create the result array (always 2D native type) 1814 final Object[] result = Array2DUtil.createArray(dataType, sizeC); 1815 1816 for (int c = 0; c < sizeC; c++) 1817 result[c] = Array1DUtil.createArray(dataType, sizeXY); 1818 1819 return result; 1820 } 1821 1822 protected Object loadDataFromImporter() throws UnsupportedFormatException, IOException, InterruptedException 1823 { 1824 // image source information not defined (not attached to importer) ? --> create empty data 1825 if (imageSourceInfo == null) 1826 return createEmptyRasterData(); 1827 1828 try 1829 { 1830 // get data from importer using 1831 return imageDataLoader.loadImageData(this); 1832 } 1833 catch (ExecutionException e) 1834 { 1835 final Throwable cause = e.getCause(); 1836 1837 if (cause instanceof UnsupportedFormatException) 1838 throw ((UnsupportedFormatException) cause); 1839 if (cause instanceof IOException) 1840 throw ((IOException) cause); 1841 throw new IOException(cause); 1842 } 1843 } 1844 1845 /** 1846 * @return true is channel bounds are automatically updated when image data is modified. 1847 * @see #setAutoUpdateChannelBounds(boolean) 1848 */ 1849 public boolean getAutoUpdateChannelBounds() 1850 { 1851 return autoUpdateChannelBounds; 1852 } 1853 1854 /** 1855 * If set to <code>true</code> (default) then channel bounds will be automatically recalculated 1856 * when image data is modified.<br> 1857 * This can consume some time if you make many updates on a large image.<br> 1858 * In this case you should do your updates in a {@link #beginUpdate()} ... {@link #endUpdate()} 1859 * block to avoid 1860 * severals recalculation. 1861 */ 1862 public void setAutoUpdateChannelBounds(boolean value) 1863 { 1864 if (autoUpdateChannelBounds != value) 1865 { 1866 if (value) 1867 updateChannelsBounds(); 1868 1869 autoUpdateChannelBounds = value; 1870 } 1871 } 1872 1873 /** 1874 * @deprecated Uses 1875 * {@link IcyBufferedImageUtil#toBufferedImage(IcyBufferedImage, BufferedImage, LUT)} 1876 * instead. 1877 */ 1878 @Deprecated 1879 public BufferedImage convertToBufferedImage(BufferedImage out, LUT lut) 1880 { 1881 return IcyBufferedImageUtil.toBufferedImage(this, out, lut); 1882 } 1883 1884 /** 1885 * @deprecated Uses 1886 * {@link IcyBufferedImageUtil#toBufferedImage(IcyBufferedImage, BufferedImage)} 1887 * instead. 1888 */ 1889 @Deprecated 1890 public BufferedImage convertToBufferedImage(BufferedImage out) 1891 { 1892 return IcyBufferedImageUtil.toBufferedImage(this, out); 1893 } 1894 1895 /** 1896 * @deprecated Uses 1897 * {@link IcyBufferedImageUtil#toBufferedImage(IcyBufferedImage, BufferedImage, LUT)} 1898 * instead. 1899 */ 1900 @Deprecated 1901 public BufferedImage getARGBImage(LUT lut, BufferedImage out) 1902 { 1903 return IcyBufferedImageUtil.getARGBImage(this, lut, out); 1904 } 1905 1906 /** 1907 * @deprecated Use {@link IcyBufferedImageUtil#toBufferedImage(IcyBufferedImage, BufferedImage)} 1908 * instead. 1909 */ 1910 @Deprecated 1911 public BufferedImage getARGBImage(BufferedImage out) 1912 { 1913 return IcyBufferedImageUtil.getARGBImage(this, out); 1914 } 1915 1916 /** 1917 * @deprecated Use {@link IcyBufferedImageUtil#getARGBImage(IcyBufferedImage, LUT)} instead. 1918 */ 1919 @Deprecated 1920 public BufferedImage getARGBImage(LUT lut) 1921 { 1922 return IcyBufferedImageUtil.getARGBImage(this, lut); 1923 } 1924 1925 /** 1926 * @deprecated Use {@link IcyBufferedImageUtil#getARGBImage(IcyBufferedImage)} instead. 1927 */ 1928 @Deprecated 1929 public BufferedImage getARGBImage() 1930 { 1931 return IcyBufferedImageUtil.getARGBImage(this); 1932 } 1933 1934 /** 1935 * @deprecated Uses 1936 * {@link IcyBufferedImageUtil#convertType(IcyBufferedImage, DataType, Scaler[])} 1937 * instead. 1938 */ 1939 @Deprecated 1940 public IcyBufferedImage convertToType(DataType dataType, Scaler scaler) 1941 { 1942 return IcyBufferedImageUtil.convertToType(this, dataType, scaler); 1943 } 1944 1945 /** 1946 * @deprecated Uses 1947 * {@link IcyBufferedImageUtil#convertType(IcyBufferedImage,DataType, Scaler[])} 1948 * instead. 1949 */ 1950 @Deprecated 1951 public IcyBufferedImage convertToType(int dataType, boolean signed, Scaler scaler) 1952 { 1953 return IcyBufferedImageUtil.convertToType(this, DataType.getDataType(dataType, signed), scaler); 1954 } 1955 1956 /** 1957 * @deprecated Uses 1958 * {@link IcyBufferedImageUtil#convertToType(IcyBufferedImage, DataType, boolean)} 1959 * instead. 1960 */ 1961 @Deprecated 1962 public IcyBufferedImage convertToType(DataType dataType, boolean rescale) 1963 { 1964 return IcyBufferedImageUtil.convertToType(this, dataType, rescale); 1965 } 1966 1967 /** 1968 * @deprecated Uses 1969 * {@link IcyBufferedImageUtil#convertToType(IcyBufferedImage,DataType, boolean)} 1970 * instead 1971 */ 1972 @Deprecated 1973 public IcyBufferedImage convertToType(int dataType, boolean signed, boolean rescale) 1974 { 1975 return convertToType(DataType.getDataType(dataType, signed), rescale); 1976 } 1977 1978 /** 1979 * @deprecated Use {@link IcyBufferedImageUtil#toBufferedImage(IcyBufferedImage, int, LUT)} 1980 * instead 1981 */ 1982 @Deprecated 1983 public BufferedImage convertToBufferedImage(LUT lut, int imageType) 1984 { 1985 return IcyBufferedImageUtil.toBufferedImage(this, imageType, lut); 1986 } 1987 1988 /** 1989 * @deprecated Use {@link IcyBufferedImageUtil#toBufferedImage(IcyBufferedImage, int, LUT)} 1990 * instead 1991 */ 1992 @Deprecated 1993 public BufferedImage convertToBufferedImage(int imageType, LUT lut) 1994 { 1995 return IcyBufferedImageUtil.toBufferedImage(this, imageType, lut); 1996 } 1997 1998 /** 1999 * @deprecated Use {@link IcyBufferedImageUtil#getCopy(IcyBufferedImage)} instead 2000 */ 2001 @Deprecated 2002 public IcyBufferedImage getCopy() 2003 { 2004 return IcyBufferedImageUtil.getCopy(this); 2005 } 2006 2007 /** 2008 * @deprecated Uses 2009 * {@link IcyBufferedImageUtil#getSubImage(IcyBufferedImage, int, int, int, int)} 2010 * instead 2011 */ 2012 @Deprecated 2013 public IcyBufferedImage getSubImageCopy(int x, int y, int w, int h) 2014 { 2015 return IcyBufferedImageUtil.getSubImage(this, x, y, w, h); 2016 } 2017 2018 /** 2019 * Not supported on IcyBufferedImage, use getSubImageCopy instead. 2020 */ 2021 @Deprecated 2022 @Override 2023 public IcyBufferedImage getSubimage(int x, int y, int w, int h) 2024 { 2025 // IcyBufferedImage doesn't support subImaging (incorrect draw and copy operation) 2026 throw new UnsupportedOperationException( 2027 "IcyBufferedImage doesn't support getSubimage method, use getSubImageCopy instead."); 2028 2029 // return new IcyBufferedImage(getIcyColorModel(), getRaster().createWritableChild(x, y, w, 2030 // h, 0, 0, null)); 2031 } 2032 2033 /** 2034 * Return a single component image corresponding to the component c of current image.<br> 2035 * This actually create a new image which share its data with internal image 2036 * so any modifications to one affect the other.<br> 2037 * if <code>(c == -1)</code> then current image is directly returned<br> 2038 * if <code>((c == 0) || (sizeC == 1))</code> then current image is directly returned<br> 2039 * if <code>((c < 0) || (c >= sizeC))</code> then it returns <code>null</code> 2040 * 2041 * @see IcyBufferedImageUtil#extractChannel(IcyBufferedImage, int) 2042 * @since version 1.0.3.3b 2043 */ 2044 public IcyBufferedImage getImage(int c) 2045 { 2046 if (c == -1) 2047 return this; 2048 2049 final int sizeC = getSizeC(); 2050 2051 if ((c < 0) || (c >= sizeC)) 2052 return null; 2053 if (sizeC == 1) 2054 return this; 2055 2056 return new IcyBufferedImage(getWidth(), getHeight(), getDataXY(c), isSignedDataType()); 2057 } 2058 2059 /** 2060 * @deprecated Use {@link IcyBufferedImageUtil#extractChannel(IcyBufferedImage, int)} instead. 2061 */ 2062 @Deprecated 2063 public IcyBufferedImage extractChannel(int channelNumber) 2064 { 2065 return IcyBufferedImageUtil.extractChannel(this, channelNumber); 2066 } 2067 2068 /** 2069 * @deprecated Use {@link IcyBufferedImageUtil#extractChannels(IcyBufferedImage, List)} instead. 2070 */ 2071 @Deprecated 2072 public IcyBufferedImage extractChannels(List<Integer> channelNumbers) 2073 { 2074 return IcyBufferedImageUtil.extractChannels(this, channelNumbers); 2075 } 2076 2077 /** 2078 * @deprecated Use {@link IcyBufferedImageUtil#extractChannel(IcyBufferedImage, int)} instead 2079 */ 2080 @Deprecated 2081 public IcyBufferedImage extractBand(int bandNumber) 2082 { 2083 return IcyBufferedImageUtil.extractChannel(this, bandNumber); 2084 } 2085 2086 /** 2087 * @deprecated Use {@link IcyBufferedImageUtil#extractChannels(IcyBufferedImage, List)} instead 2088 */ 2089 @Deprecated 2090 public IcyBufferedImage extractBands(List<Integer> bandNumbers) 2091 { 2092 return IcyBufferedImageUtil.extractChannels(this, bandNumbers); 2093 } 2094 2095 /** 2096 * @deprecated Uses 2097 * {@link IcyBufferedImageUtil#scale(IcyBufferedImage, int, int, boolean, int, int, IcyBufferedImageUtil.FilterType)} 2098 * instead. 2099 */ 2100 @Deprecated 2101 public IcyBufferedImage getScaledCopy(int width, int height, boolean resizeContent, int xAlign, int yAlign, 2102 FilterType filterType) 2103 { 2104 return IcyBufferedImageUtil.scale(this, width, height, resizeContent, xAlign, yAlign, 2105 getNewFilterType(filterType)); 2106 } 2107 2108 /** 2109 * @deprecated Uses 2110 * {@link IcyBufferedImageUtil#scale(IcyBufferedImage, int, int, boolean, int, int)} 2111 * instead. 2112 */ 2113 @Deprecated 2114 public IcyBufferedImage getScaledCopy(int width, int height, boolean resizeContent, int xAlign, int yAlign) 2115 { 2116 return IcyBufferedImageUtil.scale(this, width, height, resizeContent, xAlign, yAlign); 2117 } 2118 2119 /** 2120 * @deprecated Uses 2121 * {@link IcyBufferedImageUtil#scale(IcyBufferedImage, int, int, IcyBufferedImageUtil.FilterType)} 2122 * instead. 2123 */ 2124 @Deprecated 2125 public IcyBufferedImage getScaledCopy(int width, int height, FilterType filterType) 2126 { 2127 return IcyBufferedImageUtil.scale(this, width, height, getNewFilterType(filterType)); 2128 } 2129 2130 /** 2131 * @deprecated Use {@link IcyBufferedImageUtil#scale(IcyBufferedImage, int, int)} instead. 2132 */ 2133 @Deprecated 2134 public IcyBufferedImage getScaledCopy(int width, int height) 2135 { 2136 return IcyBufferedImageUtil.scale(this, width, height); 2137 } 2138 2139 /** 2140 * @deprecated Use {@link IcyBufferedImageUtil#translate(IcyBufferedImage, int, int, int)} 2141 * instead. 2142 */ 2143 @Deprecated 2144 public void translate(int dx, int dy, int channel) 2145 { 2146 IcyBufferedImageUtil.translate(this, dx, dy, channel); 2147 } 2148 2149 /** 2150 * @deprecated Use {@link IcyBufferedImageUtil#translate(IcyBufferedImage, int, int)} instead. 2151 */ 2152 @Deprecated 2153 public void translate(int dx, int dy) 2154 { 2155 IcyBufferedImageUtil.translate(this, dx, dy); 2156 } 2157 2158 /** 2159 * Get calculated image channel bounds (min and max values) 2160 */ 2161 protected double[] getCalculatedChannelBounds(int channel) 2162 { 2163 // don't load data for that, just wait that data is loaded naturally 2164 if (!isDataInitialized()) 2165 return new double[] {0d, 0d}; 2166 2167 final DataType dataType = getDataType_(); 2168 2169 final boolean signed = dataType.isSigned(); 2170 final Object data = getDataXY(channel); 2171 2172 final double min = ArrayMath.min(data, signed); 2173 final double max = ArrayMath.max(data, signed); 2174 2175 return new double[] {min, max}; 2176 } 2177 2178 /** 2179 * Adjust specified bounds depending internal data type 2180 */ 2181 protected double[] adjustBoundsForDataType(double[] bounds) 2182 { 2183 double min, max; 2184 2185 min = bounds[0]; 2186 max = bounds[1]; 2187 2188 // only for integer data type 2189 if (!isFloatDataType()) 2190 { 2191 // we force min to 0 if > 0 2192 if (min > 0d) 2193 min = 0d; 2194 // we force max to 0 if < 0 2195 if (max < 0d) 2196 max = 0d; 2197 } 2198 2199 final DataType dataType = getDataType_(); 2200 2201 switch (dataType.getJavaType()) 2202 { 2203 default: 2204 case BYTE: 2205 // return default bounds ([0..255] / [-128..127]) 2206 return dataType.getDefaultBounds(); 2207 2208 case SHORT: 2209 case INT: 2210 case LONG: 2211 min = MathUtil.prevPow2((long) min + 1); 2212 max = MathUtil.nextPow2Mask((long) max); 2213 break; 2214 2215 case FLOAT: 2216 case DOUBLE: 2217 // if [min..max] is included in [-1..1] 2218 if ((min >= -1d) && (max <= 1d)) 2219 { 2220 min = MathUtil.prevPow10(min); 2221 max = MathUtil.nextPow10(max); 2222 } 2223 break; 2224 } 2225 2226 return new double[] {min, max}; 2227 } 2228 2229 /** 2230 * Get the data type minimum value. 2231 */ 2232 public double getDataTypeMin() 2233 { 2234 return getDataType_().getMinValue(); 2235 } 2236 2237 /** 2238 * Get the data type maximum value. 2239 */ 2240 public double getDataTypeMax() 2241 { 2242 return getDataType_().getMaxValue(); 2243 } 2244 2245 /** 2246 * Get data type bounds (min and max values) 2247 */ 2248 public double[] getDataTypeBounds() 2249 { 2250 return new double[] {getDataTypeMin(), getDataTypeMax()}; 2251 } 2252 2253 /** 2254 * Get the minimum type value for the specified channel. 2255 */ 2256 public double getChannelTypeMin(int channel) 2257 { 2258 return getIcyColorModel().getComponentAbsMinValue(channel); 2259 } 2260 2261 /** 2262 * Get the maximum type value for the specified channel. 2263 */ 2264 public double getChannelTypeMax(int channel) 2265 { 2266 return getIcyColorModel().getComponentAbsMaxValue(channel); 2267 } 2268 2269 /** 2270 * Get type bounds (min and max values) for the specified channel. 2271 */ 2272 public double[] getChannelTypeBounds(int channel) 2273 { 2274 return getIcyColorModel().getComponentAbsBounds(channel); 2275 } 2276 2277 /** 2278 * Get type bounds (min and max values) for all channels. 2279 */ 2280 public double[][] getChannelsTypeBounds() 2281 { 2282 final int sizeC = getSizeC(); 2283 final double[][] result = new double[sizeC][]; 2284 2285 for (int c = 0; c < sizeC; c++) 2286 result[c] = getChannelTypeBounds(c); 2287 2288 return result; 2289 } 2290 2291 /** 2292 * Get global type bounds (min and max values) for all channels. 2293 */ 2294 public double[] getChannelsGlobalTypeBounds() 2295 { 2296 final int sizeC = getSizeC(); 2297 final double[] result = getChannelTypeBounds(0); 2298 2299 for (int c = 1; c < sizeC; c++) 2300 { 2301 final double[] bounds = getChannelTypeBounds(c); 2302 result[0] = Math.min(bounds[0], result[0]); 2303 result[1] = Math.max(bounds[1], result[1]); 2304 } 2305 2306 return result; 2307 } 2308 2309 /** 2310 * @deprecated Use {@link #getChannelsGlobalTypeBounds()} instead. 2311 */ 2312 @Deprecated 2313 public double[] getChannelTypeGlobalBounds() 2314 { 2315 return getChannelsGlobalTypeBounds(); 2316 } 2317 2318 /** 2319 * @deprecated Use {@link #getChannelTypeGlobalBounds()} instead. 2320 */ 2321 @Deprecated 2322 public double[] getGlobalChannelTypeBounds() 2323 { 2324 return getChannelTypeGlobalBounds(); 2325 } 2326 2327 /** 2328 * @deprecated Use {@link #getChannelTypeMin(int)} instead. 2329 */ 2330 @Deprecated 2331 public double getComponentAbsMinValue(int component) 2332 { 2333 return getChannelTypeMin(component); 2334 } 2335 2336 /** 2337 * @deprecated Use {@link #getChannelTypeMax(int)} instead. 2338 */ 2339 @Deprecated 2340 public double getComponentAbsMaxValue(int component) 2341 { 2342 return getChannelTypeMax(component); 2343 } 2344 2345 /** 2346 * @deprecated Use {@link #getChannelTypeBounds(int)} instead. 2347 */ 2348 @Deprecated 2349 public double[] getComponentAbsBounds(int component) 2350 { 2351 return getChannelTypeBounds(component); 2352 } 2353 2354 /** 2355 * @deprecated Use {@link #getChannelsTypeBounds()} instead. 2356 */ 2357 @Deprecated 2358 public double[][] getComponentsAbsBounds() 2359 { 2360 return getChannelsTypeBounds(); 2361 } 2362 2363 /** 2364 * @deprecated Use {@link #getGlobalChannelTypeBounds()} instead. 2365 */ 2366 @Deprecated 2367 public double[] getGlobalComponentAbsBounds() 2368 { 2369 return getChannelTypeGlobalBounds(); 2370 } 2371 2372 /** 2373 * Get the minimum value for the specified channel. 2374 */ 2375 public double getChannelMin(int channel) 2376 { 2377 return getIcyColorModel().getComponentUserMinValue(channel); 2378 } 2379 2380 /** 2381 * Get maximum value for the specified channel. 2382 */ 2383 public double getChannelMax(int channel) 2384 { 2385 return getIcyColorModel().getComponentUserMaxValue(channel); 2386 } 2387 2388 /** 2389 * Get bounds (min and max values) for the specified channel. 2390 */ 2391 public double[] getChannelBounds(int channel) 2392 { 2393 return getIcyColorModel().getComponentUserBounds(channel); 2394 } 2395 2396 /** 2397 * Get bounds (min and max values) for all channels. 2398 */ 2399 public double[][] getChannelsBounds() 2400 { 2401 final int sizeC = getSizeC(); 2402 final double[][] result = new double[sizeC][]; 2403 2404 for (int c = 0; c < sizeC; c++) 2405 result[c] = getChannelBounds(c); 2406 2407 return result; 2408 } 2409 2410 /** 2411 * Get global bounds (min and max values) for all channels. 2412 */ 2413 public double[] getChannelsGlobalBounds() 2414 { 2415 final int sizeC = getSizeC(); 2416 final double[] result = new double[2]; 2417 2418 result[0] = Double.MAX_VALUE; 2419 result[1] = -Double.MAX_VALUE; 2420 2421 for (int c = 0; c < sizeC; c++) 2422 { 2423 final double[] bounds = getChannelBounds(c); 2424 2425 if (bounds[0] < result[0]) 2426 result[0] = bounds[0]; 2427 if (bounds[1] > result[1]) 2428 result[1] = bounds[1]; 2429 } 2430 2431 return result; 2432 } 2433 2434 /** 2435 * @deprecated Use {@link #getChannelMin(int)} instead. 2436 */ 2437 @Deprecated 2438 public double getComponentUserMinValue(int component) 2439 { 2440 return getChannelMin(component); 2441 } 2442 2443 /** 2444 * @deprecated Use {@link #getChannelMax(int)} instead. 2445 */ 2446 @Deprecated 2447 public double getComponentUserMaxValue(int component) 2448 { 2449 return getChannelMax(component); 2450 } 2451 2452 /** 2453 * @deprecated Use {@link #getChannelBounds(int)} instead. 2454 */ 2455 @Deprecated 2456 public double[] getComponentUserBounds(int component) 2457 { 2458 return getChannelBounds(component); 2459 } 2460 2461 /** 2462 * @deprecated Use {@link #getChannelsBounds()} instead. 2463 */ 2464 @Deprecated 2465 public double[][] getComponentsUserBounds() 2466 { 2467 return getChannelsBounds(); 2468 } 2469 2470 /** 2471 * Set the preferred data type minimum value for the specified channel. 2472 */ 2473 public void setChannelTypeMin(int channel, double min) 2474 { 2475 getIcyColorModel().setComponentAbsMinValue(channel, min); 2476 } 2477 2478 /** 2479 * Set the preferred data type maximum value for the specified channel. 2480 */ 2481 public void setChannelTypeMax(int channel, double max) 2482 { 2483 getIcyColorModel().setComponentAbsMaxValue(channel, max); 2484 } 2485 2486 /** 2487 * /** 2488 * Set the preferred data type min and max values for the specified channel. 2489 */ 2490 public void setChannelTypeBounds(int channel, double min, double max) 2491 { 2492 getIcyColorModel().setComponentAbsBounds(channel, min, max); 2493 } 2494 2495 /** 2496 * Set the preferred data type bounds (min and max values) for all channels. 2497 */ 2498 public void setChannelsTypeBounds(double[][] bounds) 2499 { 2500 getIcyColorModel().setComponentsAbsBounds(bounds); 2501 } 2502 2503 /** 2504 * @deprecated Use {@link #setChannelTypeMin(int, double)} instead. 2505 */ 2506 @Deprecated 2507 public void setComponentAbsMinValue(int component, double min) 2508 { 2509 setChannelTypeMin(component, min); 2510 } 2511 2512 /** 2513 * @deprecated Use {@link #setChannelTypeMax(int, double)} instead. 2514 */ 2515 @Deprecated 2516 public void setComponentAbsMaxValue(int component, double max) 2517 { 2518 setChannelTypeMax(component, max); 2519 } 2520 2521 /** 2522 * @deprecated Use {@link #setChannelTypeBounds(int, double, double)} instead. 2523 */ 2524 @Deprecated 2525 public void setComponentAbsBounds(int component, double[] bounds) 2526 { 2527 setChannelTypeBounds(component, bounds[0], bounds[1]); 2528 } 2529 2530 /** 2531 * @deprecated Use {@link #setChannelTypeBounds(int, double, double)} instead. 2532 */ 2533 @Deprecated 2534 public void setComponentAbsBounds(int component, double min, double max) 2535 { 2536 setChannelTypeBounds(component, min, max); 2537 } 2538 2539 /** 2540 * @deprecated Use {@link #setChannelsTypeBounds(double[][])} instead. 2541 */ 2542 @Deprecated 2543 public void setComponentsAbsBounds(double[][] bounds) 2544 { 2545 setChannelsTypeBounds(bounds); 2546 } 2547 2548 /** 2549 * Set channel minimum value. 2550 */ 2551 public void setChannelMin(int channel, double min) 2552 { 2553 final IcyColorModel cm = getIcyColorModel(); 2554 2555 if ((min < cm.getComponentAbsMinValue(channel))) 2556 cm.setComponentAbsMinValue(channel, min); 2557 cm.setComponentUserMinValue(channel, min); 2558 } 2559 2560 /** 2561 * Set channel maximum value. 2562 */ 2563 public void setChannelMax(int channel, double max) 2564 { 2565 final IcyColorModel cm = getIcyColorModel(); 2566 2567 if ((max > cm.getComponentAbsMaxValue(channel))) 2568 cm.setComponentAbsMinValue(channel, max); 2569 cm.setComponentUserMaxValue(channel, max); 2570 } 2571 2572 /** 2573 * Set channel bounds (min and max values) 2574 */ 2575 public void setChannelBounds(int channel, double min, double max) 2576 { 2577 final IcyColorModel cm = getIcyColorModel(); 2578 final double[] typeBounds = cm.getComponentAbsBounds(channel); 2579 2580 if ((min < typeBounds[0]) || (max > typeBounds[1])) 2581 cm.setComponentAbsBounds(channel, min, max); 2582 cm.setComponentUserBounds(channel, min, max); 2583 } 2584 2585 /** 2586 * Set all channel bounds (min and max values) 2587 */ 2588 public void setChannelsBounds(double[][] bounds) 2589 { 2590 // we use the setChannelBounds(..) method so we do range check 2591 for (int c = 0; c < bounds.length; c++) 2592 { 2593 final double[] b = bounds[c]; 2594 setChannelBounds(c, b[0], b[1]); 2595 } 2596 } 2597 2598 /** 2599 * @deprecated Use {@link #setChannelMin(int, double)} instead. 2600 */ 2601 @Deprecated 2602 public void setComponentUserMinValue(int component, double min) 2603 { 2604 setChannelMin(component, min); 2605 } 2606 2607 /** 2608 * @deprecated Use {@link #setChannelMax(int, double)} instead. 2609 */ 2610 @Deprecated 2611 public void setComponentUserMaxValue(int component, double max) 2612 { 2613 setChannelMax(component, max); 2614 } 2615 2616 /** 2617 * @deprecated Use {@link #setChannelBounds(int, double, double)} instead. 2618 */ 2619 @Deprecated 2620 public void setComponentUserBounds(int component, double[] bounds) 2621 { 2622 setChannelBounds(component, bounds[0], bounds[1]); 2623 } 2624 2625 /** 2626 * @deprecated Use {@link #setChannelBounds(int, double, double)} instead 2627 */ 2628 @Deprecated 2629 public void setComponentUserBounds(int component, double min, double max) 2630 { 2631 setChannelBounds(component, min, max); 2632 } 2633 2634 /** 2635 * @deprecated Use {@link #setChannelsBounds(double[][])} instead. 2636 */ 2637 @Deprecated 2638 public void setComponentsUserBounds(double[][] bounds) 2639 { 2640 setChannelsBounds(bounds); 2641 } 2642 2643 /** 2644 * Update channels bounds (min and max values). 2645 */ 2646 public void updateChannelsBounds() 2647 { 2648 final IcyColorModel cm = getIcyColorModel(); 2649 2650 if (cm != null) 2651 { 2652 final int sizeC = getSizeC(); 2653 2654 for (int c = 0; c < sizeC; c++) 2655 { 2656 // get data type bounds 2657 final double[] bounds = getCalculatedChannelBounds(c); 2658 2659 cm.setComponentAbsBounds(c, adjustBoundsForDataType(bounds)); 2660 cm.setComponentUserBounds(c, bounds); 2661 2662 // we do user bounds adjustment on "non ALPHA" component only 2663 // if (cm.getColorMap(c).getType() != IcyColorMapType.ALPHA) 2664 // cm.setComponentUserBounds(c, bounds); 2665 } 2666 } 2667 } 2668 2669 /** 2670 * @deprecated Use {@link #updateChannelsBounds()} instead. 2671 */ 2672 @SuppressWarnings("unused") 2673 @Deprecated 2674 public void updateComponentsBounds(boolean updateChannelBounds, boolean adjustByteToo) 2675 { 2676 updateChannelsBounds(); 2677 } 2678 2679 /** 2680 * @deprecated Use {@link #updateChannelsBounds()} instead. 2681 */ 2682 @SuppressWarnings("unused") 2683 @Deprecated 2684 public void updateComponentsBounds(boolean updateUserBounds) 2685 { 2686 updateChannelsBounds(); 2687 } 2688 2689 /** 2690 * Return true if point is inside the image 2691 */ 2692 public boolean isInside(Point p) 2693 { 2694 return isInside(p.x, p.y); 2695 } 2696 2697 /** 2698 * Return true if point of coordinate (x, y) is inside the image 2699 */ 2700 public boolean isInside(int x, int y) 2701 { 2702 return (x >= 0) && (x < getSizeX()) && (y >= 0) && (y < getSizeY()); 2703 } 2704 2705 /** 2706 * Return true if point of coordinate (x, y) is inside the image 2707 */ 2708 public boolean isInside(double x, double y) 2709 { 2710 return (x >= 0) && (x < getSizeX()) && (y >= 0) && (y < getSizeY()); 2711 } 2712 2713 /** 2714 * Return the IcyColorModel 2715 * 2716 * @return IcyColorModel 2717 */ 2718 public IcyColorModel getIcyColorModel() 2719 { 2720 return (IcyColorModel) getColorModel(); 2721 } 2722 2723 /** 2724 * Return the data type of this image 2725 * 2726 * @return dataType 2727 * @see DataType 2728 */ 2729 public DataType getDataType_() 2730 { 2731 return getIcyColorModel().getDataType_(); 2732 } 2733 2734 /** 2735 * @deprecated use {@link #getDataType_()} instead 2736 */ 2737 @Deprecated 2738 public int getDataType() 2739 { 2740 return getIcyColorModel().getDataType(); 2741 } 2742 2743 /** 2744 * Return true if this is a float data type image 2745 */ 2746 public boolean isFloatDataType() 2747 { 2748 return getDataType_().isFloat(); 2749 } 2750 2751 /** 2752 * Return true if this is a signed data type image 2753 */ 2754 public boolean isSignedDataType() 2755 { 2756 return getDataType_().isSigned(); 2757 } 2758 2759 /** 2760 * @deprecated Use {@link #getSizeC()} instead. 2761 */ 2762 @Deprecated 2763 public int getNumComponents() 2764 { 2765 return getSizeC(); 2766 } 2767 2768 /** 2769 * @return the number of components of this image 2770 */ 2771 public int getSizeC() 2772 { 2773 return getColorModel().getNumComponents(); 2774 } 2775 2776 /** 2777 * @return the width of the image 2778 */ 2779 public int getSizeX() 2780 { 2781 return getWidth(); 2782 } 2783 2784 /** 2785 * @return the height of the image 2786 */ 2787 public int getSizeY() 2788 { 2789 return getHeight(); 2790 } 2791 2792 /** 2793 * Return 2D dimension of image {sizeX, sizeY} 2794 */ 2795 public Dimension getDimension() 2796 { 2797 return new Dimension(getSizeX(), getSizeY()); 2798 } 2799 2800 /** 2801 * Return 2D bounds of image {0, 0, sizeX, sizeY} 2802 */ 2803 public Rectangle getBounds() 2804 { 2805 return new Rectangle(getSizeX(), getSizeY()); 2806 } 2807 2808 /** 2809 * Return the number of sample.<br> 2810 * This is equivalent to<br> 2811 * <code>getSizeX() * getSizeY() * getSizeC()</code> 2812 */ 2813 public int getNumSample() 2814 { 2815 return getSizeX() * getSizeY() * getSizeC(); 2816 } 2817 2818 /** 2819 * Return the offset for specified (x, y) location 2820 */ 2821 public int getOffset(int x, int y) 2822 { 2823 return (y * getWidth()) + x; 2824 } 2825 2826 /** 2827 * create a compatible LUT for this image. 2828 * 2829 * @param createColorModel 2830 * set to <code>true</code> to create a LUT using a new compatible ColorModel else it 2831 * will use the image 2832 * internal ColorModel 2833 */ 2834 public LUT createCompatibleLUT(boolean createColorModel) 2835 { 2836 final IcyColorModel cm; 2837 2838 if (createColorModel) 2839 cm = IcyColorModel.createInstance(getIcyColorModel(), false, false); 2840 else 2841 cm = getIcyColorModel(); 2842 2843 return new LUT(cm); 2844 } 2845 2846 /** 2847 * create a compatible LUT for this image 2848 */ 2849 public LUT createCompatibleLUT() 2850 { 2851 return createCompatibleLUT(true); 2852 } 2853 2854 /** 2855 * @deprecated No attached LUT to an image.<br/> 2856 * Use {@link #createCompatibleLUT(boolean)} instead. 2857 */ 2858 @Deprecated 2859 public LUT getLUT() 2860 { 2861 return createCompatibleLUT(); 2862 } 2863 2864 /** 2865 * Return a direct reference to internal 2D array data [C][XY] 2866 */ 2867 public Object getDataXYC() 2868 { 2869 switch (getDataType_().getJavaType()) 2870 { 2871 case BYTE: 2872 return getDataXYCAsByte(); 2873 case SHORT: 2874 return getDataXYCAsShort(); 2875 case INT: 2876 return getDataXYCAsInt(); 2877 case FLOAT: 2878 return getDataXYCAsFloat(); 2879 case DOUBLE: 2880 return getDataXYCAsDouble(); 2881 default: 2882 return null; 2883 } 2884 } 2885 2886 /** 2887 * Return a direct reference to internal 1D array data [XY] for specified c 2888 */ 2889 public Object getDataXY(int c) 2890 { 2891 switch (getDataType_().getJavaType()) 2892 { 2893 case BYTE: 2894 return getDataXYAsByte(c); 2895 case SHORT: 2896 return getDataXYAsShort(c); 2897 case INT: 2898 return getDataXYAsInt(c); 2899 case FLOAT: 2900 return getDataXYAsFloat(c); 2901 case DOUBLE: 2902 return getDataXYAsDouble(c); 2903 default: 2904 return null; 2905 } 2906 } 2907 2908 /** 2909 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] 2910 */ 2911 public Object getDataCopyXYC() 2912 { 2913 return getDataCopyXYC(null, 0); 2914 } 2915 2916 /** 2917 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY]<br> 2918 * If (out != null) then it's used to store result at the specified offset 2919 */ 2920 public Object getDataCopyXYC(Object out, int offset) 2921 { 2922 switch (getDataType_().getJavaType()) 2923 { 2924 case BYTE: 2925 return getDataCopyXYCAsByte((byte[]) out, offset); 2926 case SHORT: 2927 return getDataCopyXYCAsShort((short[]) out, offset); 2928 case INT: 2929 return getDataCopyXYCAsInt((int[]) out, offset); 2930 case FLOAT: 2931 return getDataCopyXYCAsFloat((float[]) out, offset); 2932 case DOUBLE: 2933 return getDataCopyXYCAsDouble((double[]) out, offset); 2934 default: 2935 return null; 2936 } 2937 } 2938 2939 /** 2940 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c 2941 */ 2942 public Object getDataCopyXY(int c) 2943 { 2944 return getDataCopyXY(c, null, 0); 2945 } 2946 2947 /** 2948 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 2949 * If (out != null) then it's used to store result at the specified offset 2950 */ 2951 public Object getDataCopyXY(int c, Object out, int offset) 2952 { 2953 switch (getDataType_().getJavaType()) 2954 { 2955 case BYTE: 2956 return getDataCopyXYAsByte(c, (byte[]) out, offset); 2957 case SHORT: 2958 return getDataCopyXYAsShort(c, (short[]) out, offset); 2959 case INT: 2960 return getDataCopyXYAsInt(c, (int[]) out, offset); 2961 case FLOAT: 2962 return getDataCopyXYAsFloat(c, (float[]) out, offset); 2963 case DOUBLE: 2964 return getDataCopyXYAsDouble(c, (double[]) out, offset); 2965 default: 2966 return null; 2967 } 2968 } 2969 2970 /** 2971 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY] 2972 */ 2973 public Object getDataCopyCXY() 2974 { 2975 return getDataCopyCXY(null, 0); 2976 } 2977 2978 /** 2979 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 2980 * If (out != null) then it's used to store result at the specified offset 2981 */ 2982 public Object getDataCopyCXY(Object out, int offset) 2983 { 2984 switch (getDataType_().getJavaType()) 2985 { 2986 case BYTE: 2987 return getDataCopyCXYAsByte((byte[]) out, offset); 2988 case SHORT: 2989 return getDataCopyCXYAsShort((short[]) out, offset); 2990 case INT: 2991 return getDataCopyCXYAsInt((int[]) out, offset); 2992 case FLOAT: 2993 return getDataCopyCXYAsFloat((float[]) out, offset); 2994 case DOUBLE: 2995 return getDataCopyCXYAsDouble((double[]) out, offset); 2996 default: 2997 return null; 2998 } 2999 3000 } 3001 3002 /** 3003 * Return a 1D array data copy [C] of specified (x, y) position 3004 */ 3005 public Object getDataCopyC(int x, int y) 3006 { 3007 return getDataCopyC(x, y, null, 0); 3008 } 3009 3010 /** 3011 * Return a 1D array data copy [C] of specified (x, y) position<br> 3012 * If (out != null) then it's used to store result at the specified offset 3013 */ 3014 public Object getDataCopyC(int x, int y, Object out, int offset) 3015 { 3016 switch (getDataType_().getJavaType()) 3017 { 3018 case BYTE: 3019 return getDataCopyCAsByte(x, y, (byte[]) out, offset); 3020 case SHORT: 3021 return getDataCopyCAsShort(x, y, (short[]) out, offset); 3022 case INT: 3023 return getDataCopyCAsInt(x, y, (int[]) out, offset); 3024 case FLOAT: 3025 return getDataCopyCAsFloat(x, y, (float[]) out, offset); 3026 case DOUBLE: 3027 return getDataCopyCAsDouble(x, y, (double[]) out, offset); 3028 default: 3029 return null; 3030 } 3031 } 3032 3033 /** 3034 * Set internal 1D byte array data ([XY]) for specified component 3035 */ 3036 public void setDataXY(int c, Object values) 3037 { 3038 lockRaster(); 3039 try 3040 { 3041 ArrayUtil.arrayToArray(values, getDataXY(c), getDataType_().isSigned()); 3042 } 3043 finally 3044 { 3045 releaseRaster(true); 3046 } 3047 3048 // notify data changed 3049 dataChanged(); 3050 } 3051 3052 /** 3053 * Set 1D array data [C] of specified (x, y) position 3054 */ 3055 public void setDataC(int x, int y, Object values) 3056 { 3057 switch (getDataType_().getJavaType()) 3058 { 3059 case BYTE: 3060 setDataCAsByte(x, y, (byte[]) values); 3061 break; 3062 3063 case SHORT: 3064 setDataCAsShort(x, y, (short[]) values); 3065 break; 3066 3067 case INT: 3068 setDataCAsInt(x, y, (int[]) values); 3069 break; 3070 3071 case FLOAT: 3072 setDataCAsFloat(x, y, (float[]) values); 3073 break; 3074 3075 case DOUBLE: 3076 setDataCAsDouble(x, y, (double[]) values); 3077 break; 3078 3079 default: 3080 // nothing here 3081 break; 3082 } 3083 } 3084 3085 /** 3086 * Return a direct reference to internal 2D array data [C][XY] 3087 */ 3088 public byte[][] getDataXYCAsByte() 3089 { 3090 return ((DataBufferByte) getRaster().getDataBuffer()).getBankData(); 3091 } 3092 3093 /** 3094 * Return a direct reference to internal 2D array data [C][XY] 3095 */ 3096 public short[][] getDataXYCAsShort() 3097 { 3098 final DataBuffer db = getRaster().getDataBuffer(); 3099 if (db instanceof DataBufferUShort) 3100 return ((DataBufferUShort) db).getBankData(); 3101 return ((DataBufferShort) db).getBankData(); 3102 } 3103 3104 /** 3105 * Return a direct reference to internal 2D array data [C][XY] 3106 */ 3107 public int[][] getDataXYCAsInt() 3108 { 3109 return ((DataBufferInt) getRaster().getDataBuffer()).getBankData(); 3110 } 3111 3112 /** 3113 * Return a direct reference to internal 2D array data [C][XY] 3114 */ 3115 public float[][] getDataXYCAsFloat() 3116 { 3117 return ((DataBufferFloat) getRaster().getDataBuffer()).getBankData(); 3118 } 3119 3120 /** 3121 * Return a direct reference to internal 2D array data [C][XY] 3122 */ 3123 public double[][] getDataXYCAsDouble() 3124 { 3125 return ((DataBufferDouble) getRaster().getDataBuffer()).getBankData(); 3126 } 3127 3128 /** 3129 * Return a direct reference to internal 1D array data [XY] for specified c 3130 */ 3131 public byte[] getDataXYAsByte(int c) 3132 { 3133 return ((DataBufferByte) getRaster().getDataBuffer()).getData(c); 3134 } 3135 3136 /** 3137 * Return a direct reference to internal 1D array data [XY] for specified c 3138 */ 3139 public short[] getDataXYAsShort(int c) 3140 { 3141 final DataBuffer db = getRaster().getDataBuffer(); 3142 if (db instanceof DataBufferUShort) 3143 return ((DataBufferUShort) db).getData(c); 3144 return ((DataBufferShort) db).getData(c); 3145 } 3146 3147 /** 3148 * Return a direct reference to internal 1D array data [XY] for specified c 3149 */ 3150 public int[] getDataXYAsInt(int c) 3151 { 3152 return ((DataBufferInt) getRaster().getDataBuffer()).getData(c); 3153 } 3154 3155 /** 3156 * Return a direct reference to internal 1D array data [XY] for specified c 3157 */ 3158 public float[] getDataXYAsFloat(int c) 3159 { 3160 return ((DataBufferFloat) getRaster().getDataBuffer()).getData(c); 3161 } 3162 3163 /** 3164 * Return a direct reference to internal 1D array data [XY] for specified c 3165 */ 3166 public double[] getDataXYAsDouble(int c) 3167 { 3168 return ((DataBufferDouble) getRaster().getDataBuffer()).getData(c); 3169 } 3170 3171 /** 3172 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] 3173 */ 3174 public byte[] getDataCopyXYCAsByte() 3175 { 3176 return getDataCopyXYCAsByte(null, 0); 3177 } 3178 3179 /** 3180 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] If (out != null) then 3181 * it's used to store result at the specified offset 3182 */ 3183 public byte[] getDataCopyXYCAsByte(byte[] out, int off) 3184 { 3185 final long sizeC = getSizeC(); 3186 final long len = (long) getSizeX() * (long) getSizeY(); 3187 if ((len * sizeC) >= Integer.MAX_VALUE) 3188 throw new TooLargeArrayException(); 3189 3190 final byte[][] banks = ((DataBufferByte) getRaster().getDataBuffer()).getBankData(); 3191 final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3192 int offset = off; 3193 3194 for (int c = 0; c < sizeC; c++) 3195 { 3196 final byte[] src = banks[c]; 3197 System.arraycopy(src, 0, result, offset, (int) len); 3198 offset += len; 3199 } 3200 3201 return result; 3202 } 3203 3204 /** 3205 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] 3206 */ 3207 public short[] getDataCopyXYCAsShort() 3208 { 3209 return getDataCopyXYCAsShort(null, 0); 3210 } 3211 3212 /** 3213 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] If (out != null) then 3214 * it's used to store result at the specified offset 3215 */ 3216 public short[] getDataCopyXYCAsShort(short[] out, int off) 3217 { 3218 final long sizeC = getSizeC(); 3219 final long len = (long) getSizeX() * (long) getSizeY(); 3220 if ((len * sizeC) >= Integer.MAX_VALUE) 3221 throw new TooLargeArrayException(); 3222 3223 final DataBuffer db = getRaster().getDataBuffer(); 3224 final short[][] banks; 3225 if (db instanceof DataBufferUShort) 3226 banks = ((DataBufferUShort) db).getBankData(); 3227 else 3228 banks = ((DataBufferShort) db).getBankData(); 3229 final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3230 int offset = off; 3231 3232 for (int c = 0; c < sizeC; c++) 3233 { 3234 final short[] src = banks[c]; 3235 System.arraycopy(src, 0, result, offset, (int) len); 3236 offset += len; 3237 } 3238 3239 return result; 3240 } 3241 3242 /** 3243 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] 3244 */ 3245 public int[] getDataCopyXYCAsInt() 3246 { 3247 return getDataCopyXYCAsInt(null, 0); 3248 } 3249 3250 /** 3251 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] If (out != null) then 3252 * it's used to store result at the specified offset 3253 */ 3254 public int[] getDataCopyXYCAsInt(int[] out, int off) 3255 { 3256 final long sizeC = getSizeC(); 3257 final long len = (long) getSizeX() * (long) getSizeY(); 3258 if ((len * sizeC) >= Integer.MAX_VALUE) 3259 throw new TooLargeArrayException(); 3260 3261 final int[][] banks = ((DataBufferInt) getRaster().getDataBuffer()).getBankData(); 3262 final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3263 int offset = off; 3264 3265 for (int c = 0; c < sizeC; c++) 3266 { 3267 final int[] src = banks[c]; 3268 System.arraycopy(src, 0, result, offset, (int) len); 3269 offset += len; 3270 } 3271 3272 return result; 3273 } 3274 3275 /** 3276 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] 3277 */ 3278 public float[] getDataCopyXYCAsFloat() 3279 { 3280 return getDataCopyXYCAsFloat(null, 0); 3281 } 3282 3283 /** 3284 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] If (out != null) then 3285 * it's used to store result at the specified offset 3286 */ 3287 public float[] getDataCopyXYCAsFloat(float[] out, int off) 3288 { 3289 final long sizeC = getSizeC(); 3290 final long len = (long) getSizeX() * (long) getSizeY(); 3291 if ((len * sizeC) >= Integer.MAX_VALUE) 3292 throw new TooLargeArrayException(); 3293 3294 final float[][] banks = ((DataBufferFloat) getRaster().getDataBuffer()).getBankData(); 3295 final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3296 int offset = off; 3297 3298 for (int c = 0; c < sizeC; c++) 3299 { 3300 final float[] src = banks[c]; 3301 System.arraycopy(src, 0, result, offset, (int) len); 3302 offset += len; 3303 } 3304 3305 return result; 3306 } 3307 3308 /** 3309 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] 3310 */ 3311 public double[] getDataCopyXYCAsDouble() 3312 { 3313 return getDataCopyXYCAsDouble(null, 0); 3314 } 3315 3316 /** 3317 * Return a 1D array data copy [XYC] of internal 2D array data [C][XY] If (out != null) then 3318 * it's used to store result at the specified offset 3319 */ 3320 public double[] getDataCopyXYCAsDouble(double[] out, int off) 3321 { 3322 final long sizeC = getSizeC(); 3323 final long len = (long) getSizeX() * (long) getSizeY(); 3324 if ((len * sizeC) >= Integer.MAX_VALUE) 3325 throw new TooLargeArrayException(); 3326 3327 final double[][] banks = ((DataBufferDouble) getRaster().getDataBuffer()).getBankData(); 3328 final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3329 int offset = off; 3330 3331 for (int c = 0; c < sizeC; c++) 3332 { 3333 final double[] src = banks[c]; 3334 System.arraycopy(src, 0, result, offset, (int) len); 3335 offset += len; 3336 } 3337 3338 return result; 3339 } 3340 3341 /** 3342 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3343 */ 3344 public byte[] getDataCopyXYAsByte(int c) 3345 { 3346 return getDataCopyXYAsByte(c, null, 0); 3347 } 3348 3349 /** 3350 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3351 * If (out != null) then it's used to store result at the specified offset 3352 */ 3353 public byte[] getDataCopyXYAsByte(int c, byte[] out, int off) 3354 { 3355 final int len = getSizeX() * getSizeY(); 3356 final byte[] src = ((DataBufferByte) getRaster().getDataBuffer()).getData(c); 3357 final byte[] result = Array1DUtil.allocIfNull(out, len); 3358 3359 System.arraycopy(src, 0, result, off, len); 3360 3361 return result; 3362 } 3363 3364 /** 3365 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3366 */ 3367 public short[] getDataCopyXYAsShort(int c) 3368 { 3369 return getDataCopyXYAsShort(c, null, 0); 3370 } 3371 3372 /** 3373 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3374 * If (out != null) then it's used to store result at the specified offset 3375 */ 3376 public short[] getDataCopyXYAsShort(int c, short[] out, int off) 3377 { 3378 final int len = getSizeX() * getSizeY(); 3379 final DataBuffer db = getRaster().getDataBuffer(); 3380 final short[] src; 3381 if (db instanceof DataBufferUShort) 3382 src = ((DataBufferUShort) db).getData(c); 3383 else 3384 src = ((DataBufferShort) db).getData(c); 3385 final short[] result = Array1DUtil.allocIfNull(out, len); 3386 3387 System.arraycopy(src, 0, result, off, len); 3388 3389 return result; 3390 } 3391 3392 /** 3393 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3394 */ 3395 public int[] getDataCopyXYAsInt(int c) 3396 { 3397 return getDataCopyXYAsInt(c, null, 0); 3398 } 3399 3400 /** 3401 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3402 * If (out != null) then it's used to store result at the specified offset 3403 */ 3404 public int[] getDataCopyXYAsInt(int c, int[] out, int off) 3405 { 3406 final int len = getSizeX() * getSizeY(); 3407 final int[] src = ((DataBufferInt) getRaster().getDataBuffer()).getData(c); 3408 final int[] result = Array1DUtil.allocIfNull(out, len); 3409 3410 System.arraycopy(src, 0, result, off, len); 3411 3412 return result; 3413 } 3414 3415 /** 3416 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3417 */ 3418 public float[] getDataCopyXYAsFloat(int c) 3419 { 3420 return getDataCopyXYAsFloat(c, null, 0); 3421 } 3422 3423 /** 3424 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3425 * If (out != null) then it's used to store result at the specified offset 3426 */ 3427 public float[] getDataCopyXYAsFloat(int c, float[] out, int off) 3428 { 3429 final int len = getSizeX() * getSizeY(); 3430 final float[] src = ((DataBufferFloat) getRaster().getDataBuffer()).getData(c); 3431 final float[] result = Array1DUtil.allocIfNull(out, len); 3432 3433 System.arraycopy(src, 0, result, off, len); 3434 3435 return result; 3436 } 3437 3438 /** 3439 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3440 */ 3441 public double[] getDataCopyXYAsDouble(int c) 3442 { 3443 return getDataCopyXYAsDouble(c, null, 0); 3444 } 3445 3446 /** 3447 * Return a 1D array data copy [XY] of internal 1D array data [XY] for specified c<br> 3448 * If (out != null) then it's used to store result at the specified offset 3449 */ 3450 public double[] getDataCopyXYAsDouble(int c, double[] out, int off) 3451 { 3452 final int len = getSizeX() * getSizeY(); 3453 final double[] src = ((DataBufferDouble) getRaster().getDataBuffer()).getData(c); 3454 final double[] result = Array1DUtil.allocIfNull(out, len); 3455 3456 System.arraycopy(src, 0, result, off, len); 3457 3458 return result; 3459 } 3460 3461 /** 3462 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3463 */ 3464 public byte[] getDataCopyCXYAsByte() 3465 { 3466 return getDataCopyCXYAsByte(null, 0); 3467 } 3468 3469 /** 3470 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3471 * If (out != null) then it's used to store result at the specified offset 3472 */ 3473 public byte[] getDataCopyCXYAsByte(byte[] out, int off) 3474 { 3475 final long sizeC = getSizeC(); 3476 final long len = (long) getSizeX() * (long) getSizeY(); 3477 if ((len * sizeC) >= Integer.MAX_VALUE) 3478 throw new TooLargeArrayException(); 3479 3480 final byte[][] banks = ((DataBufferByte) getRaster().getDataBuffer()).getBankData(); 3481 final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3482 3483 for (int c = 0; c < sizeC; c++) 3484 { 3485 final byte[] src = banks[c]; 3486 int offset = c + off; 3487 for (int i = 0; i < len; i++, offset += sizeC) 3488 result[offset] = src[i]; 3489 } 3490 3491 return result; 3492 } 3493 3494 /** 3495 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3496 */ 3497 public short[] getDataCopyCXYAsShort() 3498 { 3499 return getDataCopyCXYAsShort(null, 0); 3500 } 3501 3502 /** 3503 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3504 * If (out != null) then it's used to store result at the specified offset 3505 */ 3506 public short[] getDataCopyCXYAsShort(short[] out, int off) 3507 { 3508 final long sizeC = getSizeC(); 3509 final long len = (long) getSizeX() * (long) getSizeY(); 3510 if ((len * sizeC) >= Integer.MAX_VALUE) 3511 throw new TooLargeArrayException(); 3512 3513 final DataBuffer db = getRaster().getDataBuffer(); 3514 final short[][] banks; 3515 if (db instanceof DataBufferUShort) 3516 banks = ((DataBufferUShort) db).getBankData(); 3517 else 3518 banks = ((DataBufferShort) db).getBankData(); 3519 final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3520 3521 for (int c = 0; c < sizeC; c++) 3522 { 3523 final short[] src = banks[c]; 3524 int offset = c + off; 3525 for (int i = 0; i < len; i++, offset += sizeC) 3526 result[offset] = src[i]; 3527 } 3528 3529 return result; 3530 } 3531 3532 /** 3533 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3534 */ 3535 public int[] getDataCopyCXYAsInt() 3536 { 3537 return getDataCopyCXYAsInt(null, 0); 3538 } 3539 3540 /** 3541 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3542 * If (out != null) then it's used to store result at the specified offset 3543 */ 3544 public int[] getDataCopyCXYAsInt(int[] out, int off) 3545 { 3546 final long sizeC = getSizeC(); 3547 final long len = (long) getSizeX() * (long) getSizeY(); 3548 if ((len * sizeC) >= Integer.MAX_VALUE) 3549 throw new TooLargeArrayException(); 3550 3551 final int[][] banks = ((DataBufferInt) getRaster().getDataBuffer()).getBankData(); 3552 final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3553 3554 for (int c = 0; c < sizeC; c++) 3555 { 3556 final int[] src = banks[c]; 3557 int offset = c + off; 3558 for (int i = 0; i < len; i++, offset += sizeC) 3559 result[offset] = src[i]; 3560 } 3561 3562 return result; 3563 } 3564 3565 /** 3566 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3567 */ 3568 public float[] getDataCopyCXYAsFloat() 3569 { 3570 return getDataCopyCXYAsFloat(null, 0); 3571 } 3572 3573 /** 3574 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3575 * If (out != null) then it's used to store result at the specified offset 3576 */ 3577 public float[] getDataCopyCXYAsFloat(float[] out, int off) 3578 { 3579 final long sizeC = getSizeC(); 3580 final long len = (long) getSizeX() * (long) getSizeY(); 3581 if ((len * sizeC) >= Integer.MAX_VALUE) 3582 throw new TooLargeArrayException(); 3583 3584 final float[][] banks = ((DataBufferFloat) getRaster().getDataBuffer()).getBankData(); 3585 final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3586 3587 for (int c = 0; c < sizeC; c++) 3588 { 3589 final float[] src = banks[c]; 3590 int offset = c + off; 3591 for (int i = 0; i < len; i++, offset += sizeC) 3592 result[offset] = src[i]; 3593 } 3594 3595 return result; 3596 } 3597 3598 /** 3599 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3600 */ 3601 public double[] getDataCopyCXYAsDouble() 3602 { 3603 return getDataCopyCXYAsDouble(null, 0); 3604 } 3605 3606 /** 3607 * Return a 1D array data copy [CXY] of internal 2D array data [C][XY]<br> 3608 * If (out != null) then it's used to store result at the specified offset 3609 */ 3610 public double[] getDataCopyCXYAsDouble(double[] out, int off) 3611 { 3612 final long sizeC = getSizeC(); 3613 final long len = (long) getSizeX() * (long) getSizeY(); 3614 if ((len * sizeC) >= Integer.MAX_VALUE) 3615 throw new TooLargeArrayException(); 3616 3617 final double[][] banks = ((DataBufferDouble) getRaster().getDataBuffer()).getBankData(); 3618 final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeC)); 3619 3620 for (int c = 0; c < sizeC; c++) 3621 { 3622 final double[] src = banks[c]; 3623 int offset = c + off; 3624 for (int i = 0; i < len; i++, offset += sizeC) 3625 result[offset] = src[i]; 3626 } 3627 3628 return result; 3629 } 3630 3631 /** 3632 * Return a 1D array data copy [C] of specified (x, y) position 3633 */ 3634 public byte[] getDataCopyCAsByte(int x, int y) 3635 { 3636 return getDataCopyCAsByte(x, y, null, 0); 3637 } 3638 3639 /** 3640 * Return a 1D array data copy [C] of specified (x, y) position<br> 3641 * If (out != null) then it's used to store result at the specified offset 3642 */ 3643 public byte[] getDataCopyCAsByte(int x, int y, byte[] out, int off) 3644 { 3645 final int sizeC = getSizeC(); 3646 final int offset = x + (y * getWidth()); 3647 final byte[][] data = ((DataBufferByte) getRaster().getDataBuffer()).getBankData(); 3648 final byte[] result = Array1DUtil.allocIfNull(out, sizeC); 3649 3650 for (int c = 0; c < sizeC; c++) 3651 // ignore band offset as it's always 0 here 3652 result[c + off] = data[c][offset]; 3653 3654 return result; 3655 } 3656 3657 /** 3658 * Return a 1D array data copy [C] of specified (x, y) position 3659 */ 3660 public short[] getDataCopyCAsShort(int x, int y) 3661 { 3662 return getDataCopyCAsShort(x, y, null, 0); 3663 } 3664 3665 /** 3666 * Return a 1D array data copy [C] of specified (x, y) position<br> 3667 * If (out != null) then it's used to store result at the specified offset 3668 */ 3669 public short[] getDataCopyCAsShort(int x, int y, short[] out, int off) 3670 { 3671 final int sizeC = getSizeC(); 3672 final int offset = x + (y * getWidth()); 3673 final DataBuffer db = getRaster().getDataBuffer(); 3674 final short[][] data; 3675 if (db instanceof DataBufferUShort) 3676 data = ((DataBufferUShort) db).getBankData(); 3677 else 3678 data = ((DataBufferShort) db).getBankData(); 3679 final short[] result = Array1DUtil.allocIfNull(out, sizeC); 3680 3681 for (int c = 0; c < sizeC; c++) 3682 // ignore band offset as it's always 0 here 3683 result[c + off] = data[c][offset]; 3684 3685 return result; 3686 } 3687 3688 /** 3689 * Return a 1D array data copy [C] of specified (x, y) position 3690 */ 3691 public int[] getDataCopyCAsInt(int x, int y) 3692 { 3693 return getDataCopyCAsInt(x, y, null, 0); 3694 } 3695 3696 /** 3697 * Return a 1D array data copy [C] of specified (x, y) position<br> 3698 * If (out != null) then it's used to store result at the specified offset 3699 */ 3700 public int[] getDataCopyCAsInt(int x, int y, int[] out, int off) 3701 { 3702 final int sizeC = getSizeC(); 3703 final int offset = x + (y * getWidth()); 3704 final int[][] data = ((DataBufferInt) getRaster().getDataBuffer()).getBankData(); 3705 final int[] result = Array1DUtil.allocIfNull(out, sizeC); 3706 3707 for (int c = 0; c < sizeC; c++) 3708 // ignore band offset as it's always 0 here 3709 result[c + off] = data[c][offset]; 3710 3711 return result; 3712 } 3713 3714 /** 3715 * Return a 1D array data copy [C] of specified (x, y) position 3716 */ 3717 public float[] getDataCopyCAsFloat(int x, int y) 3718 { 3719 return getDataCopyCAsFloat(x, y, null, 0); 3720 } 3721 3722 /** 3723 * Return a 1D array data copy [C] of specified (x, y) position<br> 3724 * If (out != null) then it's used to store result at the specified offset 3725 */ 3726 public float[] getDataCopyCAsFloat(int x, int y, float[] out, int off) 3727 { 3728 final int sizeC = getSizeC(); 3729 final int offset = x + (y * getWidth()); 3730 final float[][] data = ((DataBufferFloat) getRaster().getDataBuffer()).getBankData(); 3731 final float[] result = Array1DUtil.allocIfNull(out, sizeC); 3732 3733 for (int c = 0; c < sizeC; c++) 3734 // ignore band offset as it's always 0 here 3735 result[c + off] = data[c][offset]; 3736 3737 return result; 3738 } 3739 3740 /** 3741 * Return a 1D array data copy [C] of specified (x, y) position 3742 */ 3743 public double[] getDataCopyCAsDouble(int x, int y) 3744 { 3745 return getDataCopyCAsDouble(x, y, null, 0); 3746 } 3747 3748 /** 3749 * Return a 1D array data copy [C] of specified (x, y) position<br> 3750 * If (out != null) then it's used to store result at the specified offset 3751 */ 3752 public double[] getDataCopyCAsDouble(int x, int y, double[] out, int off) 3753 { 3754 final int sizeC = getSizeC(); 3755 final int offset = x + (y * getWidth()); 3756 final double[][] data = ((DataBufferDouble) getRaster().getDataBuffer()).getBankData(); 3757 final double[] result = Array1DUtil.allocIfNull(out, sizeC); 3758 3759 for (int c = 0; c < sizeC; c++) 3760 // ignore band offset as it's always 0 here 3761 result[c + off] = data[c][offset]; 3762 3763 return result; 3764 } 3765 3766 /** 3767 * Set internal 1D byte array data ([XY]) for specified component 3768 */ 3769 public void setDataXYAsByte(int c, byte[] values) 3770 { 3771 lockRaster(); 3772 try 3773 { 3774 System.arraycopy(values, 0, getDataXYAsByte(c), 0, getSizeX() * getSizeY()); 3775 } 3776 finally 3777 { 3778 releaseRaster(true); 3779 } 3780 3781 // notify data changed 3782 dataChanged(); 3783 } 3784 3785 /** 3786 * Set internal 1D byte array data ([XY]) for specified component 3787 */ 3788 public void setDataXYAsShort(int c, short[] values) 3789 { 3790 lockRaster(); 3791 try 3792 { 3793 System.arraycopy(values, 0, getDataXYAsShort(c), 0, getSizeX() * getSizeY()); 3794 } 3795 finally 3796 { 3797 releaseRaster(true); 3798 } 3799 3800 // notify data changed 3801 dataChanged(); 3802 } 3803 3804 /** 3805 * Set internal 1D byte array data ([XY]) for specified component 3806 */ 3807 public void setDataXYAsInt(int c, int[] values) 3808 { 3809 lockRaster(); 3810 try 3811 { 3812 System.arraycopy(values, 0, getDataXYAsInt(c), 0, getSizeX() * getSizeY()); 3813 } 3814 finally 3815 { 3816 releaseRaster(true); 3817 } 3818 3819 // notify data changed 3820 dataChanged(); 3821 } 3822 3823 /** 3824 * Set internal 1D byte array data ([XY]) for specified component 3825 */ 3826 public void setDataXYAsFloat(int c, float[] values) 3827 { 3828 lockRaster(); 3829 try 3830 { 3831 System.arraycopy(values, 0, getDataXYAsFloat(c), 0, getSizeX() * getSizeY()); 3832 } 3833 finally 3834 { 3835 releaseRaster(true); 3836 } 3837 3838 // notify data changed 3839 dataChanged(); 3840 } 3841 3842 /** 3843 * Set internal 1D byte array data ([XY]) for specified component 3844 */ 3845 public void setDataXYAsDouble(int c, double[] values) 3846 { 3847 lockRaster(); 3848 try 3849 { 3850 System.arraycopy(values, 0, getDataXYAsDouble(c), 0, getSizeX() * getSizeY()); 3851 } 3852 finally 3853 { 3854 releaseRaster(true); 3855 } 3856 3857 // notify data changed 3858 dataChanged(); 3859 } 3860 3861 /** 3862 * Set 1D array data [C] of specified (x, y) position 3863 */ 3864 public void setDataCAsByte(int x, int y, byte[] values) 3865 { 3866 final int offset = x + (y * getWidth()); 3867 final int len = values.length; 3868 final WritableRaster wr = getRaster(); 3869 final byte[][] data = ((DataBufferByte) wr.getDataBuffer()).getBankData(); 3870 3871 for (int comp = 0; comp < len; comp++) 3872 // ignore band offset as it's always 0 here 3873 data[comp][offset] = values[comp]; 3874 3875 // save changed data in cache (need to do cache behind here and still that is terribly slow !!) 3876 saveRasterInCache(wr); 3877 // notify data changed 3878 dataChanged(); 3879 } 3880 3881 /** 3882 * Set 1D array data [C] of specified (x, y) position 3883 */ 3884 public void setDataCAsShort(int x, int y, short[] values) 3885 { 3886 final int offset = x + (y * getWidth()); 3887 final int len = values.length; 3888 final WritableRaster wr = getRaster(); 3889 final DataBuffer db = wr.getDataBuffer(); 3890 final short[][] data; 3891 if (db instanceof DataBufferUShort) 3892 data = ((DataBufferUShort) db).getBankData(); 3893 else 3894 data = ((DataBufferShort) db).getBankData(); 3895 3896 for (int comp = 0; comp < len; comp++) 3897 // ignore band offset as it's always 0 here 3898 data[comp][offset] = values[comp]; 3899 3900 // save changed data in cache (need to do cache behind here and still that is terribly slow !!) 3901 saveRasterInCache(wr); 3902 // notify data changed 3903 dataChanged(); 3904 } 3905 3906 /** 3907 * Set 1D array data [C] of specified (x, y) position 3908 */ 3909 public void setDataCAsInt(int x, int y, int[] values) 3910 { 3911 final int offset = x + (y * getWidth()); 3912 final int len = values.length; 3913 final WritableRaster wr = getRaster(); 3914 final int[][] data = ((DataBufferInt) wr.getDataBuffer()).getBankData(); 3915 3916 for (int comp = 0; comp < len; comp++) 3917 // ignore band offset as it's always 0 here 3918 data[comp][offset] = values[comp]; 3919 3920 // save changed data in cache (need to do cache behind here and still that is terribly slow !!) 3921 saveRasterInCache(wr); 3922 // notify data changed 3923 dataChanged(); 3924 } 3925 3926 /** 3927 * Set 1D array data [C] of specified (x, y) position 3928 */ 3929 public void setDataCAsFloat(int x, int y, float[] values) 3930 { 3931 final int offset = x + (y * getWidth()); 3932 final int len = values.length; 3933 final WritableRaster wr = getRaster(); 3934 final float[][] data = ((DataBufferFloat) wr.getDataBuffer()).getBankData(); 3935 3936 for (int comp = 0; comp < len; comp++) 3937 // ignore band offset as it's always 0 here 3938 data[comp][offset] = values[comp]; 3939 3940 // save changed data in cache (need to do cache behind here and still that is terribly slow !!) 3941 saveRasterInCache(wr); 3942 // notify data changed 3943 dataChanged(); 3944 } 3945 3946 /** 3947 * Set 1D array data [C] of specified (x, y) position 3948 */ 3949 public void setDataCAsDouble(int x, int y, double[] values) 3950 { 3951 final int offset = x + (y * getWidth()); 3952 final int len = values.length; 3953 final WritableRaster wr = getRaster(); 3954 final double[][] data = ((DataBufferDouble) wr.getDataBuffer()).getBankData(); 3955 3956 for (int comp = 0; comp < len; comp++) 3957 // ignore band offset as it's always 0 here 3958 data[comp][offset] = values[comp]; 3959 3960 // save changed data in cache (need to do cache behind here and still that is terribly slow !!) 3961 saveRasterInCache(wr); 3962 // notify data changed 3963 dataChanged(); 3964 } 3965 3966 /** 3967 * Return the value located at (x, y, c) position as a double 3968 * whatever is the internal data type 3969 */ 3970 public double getData(int x, int y, int c) 3971 { 3972 return Array1DUtil.getValue(getDataXY(c), getOffset(x, y), getDataType_()); 3973 } 3974 3975 /** 3976 * Set the value located at (x, y, c) position as a double 3977 * whatever is the internal data type 3978 */ 3979 public void setData(int x, int y, int c, double value) 3980 { 3981 lockRaster(); 3982 try 3983 { 3984 // set value 3985 Array1DUtil.setValue(getDataXY(c), getOffset(x, y), getDataType_(), value); 3986 } 3987 finally 3988 { 3989 // FIXME : save changed data in cache (need to do cache behind here and still that is terribly slow !!) 3990 releaseRaster(true); 3991 } 3992 3993 // notify data changed 3994 dataChanged(); 3995 } 3996 3997 /** 3998 * Returns the data value located at position (x, y, c) as double whatever is the internal data type.<br> 3999 * The value is interpolated depending the current double (x,y) coordinates.<br> 4000 * It returns 0d if value is out of range. 4001 */ 4002 public double getDataInterpolated(double x, double y, int c) 4003 { 4004 final int xi = (int) x; 4005 final int xip = xi + 1; 4006 final int yi = (int) y; 4007 final int yip = yi + 1; 4008 final int sx = getSizeX(); 4009 final int sy = getSizeY(); 4010 4011 double result = 0d; 4012 4013 // at least one pixel inside 4014 if ((xi < sx) && (yi < sy) && (xip >= 0) && (yip >= 0)) 4015 { 4016 final double ratioNextX = x - (double) xi; 4017 final double ratioCurX = 1d - ratioNextX; 4018 final double ratioNextY = y - (double) yi; 4019 final double ratioCurY = 1d - ratioNextY; 4020 4021 if (yi >= 0) 4022 { 4023 if (xi >= 0) 4024 result += getData(xi, yi, c) * (ratioCurX * ratioCurY); 4025 if (xip < sx) 4026 result += getData(xip, yi, c) * (ratioNextX * ratioCurY); 4027 } 4028 if (yip < sy) 4029 { 4030 if (xi >= 0) 4031 result += getData(xi, yip, c) * (ratioCurX * ratioNextY); 4032 if (xip < sx) 4033 result += getData(xip, yip, c) * (ratioNextX * ratioNextY); 4034 } 4035 } 4036 4037 return result; 4038 } 4039 4040 /** 4041 * Return the value located at (x, y, c) position 4042 */ 4043 public byte getDataAsByte(int x, int y, int c) 4044 { 4045 // ignore band offset as it's always 0 here 4046 return (((DataBufferByte) getRaster().getDataBuffer()).getData(c))[x + (y * getWidth())]; 4047 } 4048 4049 /** 4050 * Set the value located at (x, y, c) position 4051 */ 4052 public void setDataAsByte(int x, int y, int c, byte value) 4053 { 4054 final WritableRaster wr = getRaster(); 4055 // ignore band offset as it's always 0 here 4056 (((DataBufferByte) wr.getDataBuffer()).getData(c))[x + (y * getWidth())] = value; 4057 // save changed data in cache 4058 saveRasterInCache(wr); 4059 // notify data changed 4060 dataChanged(); 4061 } 4062 4063 /** 4064 * Return the value located at (x, y, c) position 4065 */ 4066 public short getDataAsShort(int x, int y, int c) 4067 { 4068 // ignore band offset as it's always 0 here 4069 final DataBuffer db = getRaster().getDataBuffer(); 4070 4071 if (db instanceof DataBufferUShort) 4072 return (((DataBufferUShort) db).getData(c))[x + (y * getWidth())]; 4073 4074 return (((DataBufferShort) db).getData(c))[x + (y * getWidth())]; 4075 } 4076 4077 /** 4078 * Set the value located at (x, y, c) position 4079 */ 4080 public void setDataAsShort(int x, int y, int c, short value) 4081 { 4082 final WritableRaster wr = getRaster(); 4083 final DataBuffer db = wr.getDataBuffer(); 4084 if (db instanceof DataBufferUShort) 4085 // ignore band offset as it's always 0 here 4086 (((DataBufferUShort) db).getData(c))[x + (y * getWidth())] = value; 4087 else 4088 (((DataBufferShort) db).getData(c))[x + (y * getWidth())] = value; 4089 // save changed data in cache 4090 saveRasterInCache(wr); 4091 // notify data changed 4092 dataChanged(); 4093 } 4094 4095 /** 4096 * Return the value located at (x, y, c) position 4097 */ 4098 public int getDataAsInt(int x, int y, int c) 4099 { 4100 // ignore band offset as it's always 0 here 4101 return (((DataBufferInt) getRaster().getDataBuffer()).getData(c))[x + (y * getWidth())]; 4102 } 4103 4104 /** 4105 * Set the value located at (x, y, c) position 4106 */ 4107 public void setDataAsInt(int x, int y, int c, int value) 4108 { 4109 final WritableRaster wr = getRaster(); 4110 // ignore band offset as it's always 0 here 4111 (((DataBufferInt) wr.getDataBuffer()).getData(c))[x + (y * getWidth())] = value; 4112 // save changed data in cache 4113 saveRasterInCache(wr); 4114 // notify data changed 4115 dataChanged(); 4116 } 4117 4118 /** 4119 * Return the value located at (x, y, c) position 4120 */ 4121 public float getDataAsFloat(int x, int y, int c) 4122 { 4123 // ignore band offset as it's always 0 here 4124 return (((DataBufferFloat) getRaster().getDataBuffer()).getData(c))[x + (y * getWidth())]; 4125 } 4126 4127 /** 4128 * Set the value located at (x, y, c) position 4129 */ 4130 public void setDataAsFloat(int x, int y, int c, float value) 4131 { 4132 final WritableRaster wr = getRaster(); 4133 // ignore band offset as it's always 0 here 4134 (((DataBufferFloat) wr.getDataBuffer()).getData(c))[x + (y * getWidth())] = value; 4135 // save changed data in cache 4136 saveRasterInCache(wr); 4137 // notify data changed 4138 dataChanged(); 4139 } 4140 4141 /** 4142 * Return the value located at (x, y, c) position 4143 */ 4144 public double getDataAsDouble(int x, int y, int c) 4145 { 4146 // ignore band offset as it's always 0 here 4147 return (((DataBufferDouble) getRaster().getDataBuffer()).getData(c))[x + (y * getWidth())]; 4148 } 4149 4150 /** 4151 * Set the value located at (x, y, c) position 4152 */ 4153 public void setDataAsDouble(int x, int y, int c, double value) 4154 { 4155 final WritableRaster wr = getRaster(); 4156 // ignore band offset as it's always 0 here 4157 (((DataBufferDouble) wr.getDataBuffer()).getData(c))[x + (y * getWidth())] = value; 4158 // save changed data in cache 4159 saveRasterInCache(wr); 4160 // notify data changed 4161 dataChanged(); 4162 } 4163 4164 /** 4165 * Same as getRGB but by using the specified LUT instead of internal one 4166 * 4167 * @see java.awt.image.BufferedImage#getRGB(int, int) 4168 */ 4169 public int getRGB(int x, int y, LUT lut) 4170 { 4171 return getIcyColorModel().getRGB(getRaster().getDataElements(x, y, null), lut); 4172 } 4173 4174 /** 4175 * Internal copy data from an icy image (notify data changed) 4176 * 4177 * @param srcImage 4178 * source icy image 4179 * @param srcRect 4180 * source region 4181 * @param dstPt 4182 * destination X,Y position 4183 * @param srcChannel 4184 * source channel 4185 * @param dstChannel 4186 * destination channel 4187 */ 4188 protected void fastCopyData(IcyBufferedImage srcImage, Rectangle srcRect, Point dstPt, int srcChannel, 4189 int dstChannel) 4190 { 4191 final int srcSizeX = srcImage.getSizeX(); 4192 final int dstSizeX = getSizeX(); 4193 4194 // limit to source image size 4195 Rectangle adjSrcRect = srcRect.intersection(new Rectangle(srcSizeX, srcImage.getSizeY())); 4196 // negative destination x position 4197 if (dstPt.x < 0) 4198 // adjust source rect 4199 adjSrcRect.x += -dstPt.x; 4200 // negative destination y position 4201 if (dstPt.y < 0) 4202 // adjust source rect 4203 adjSrcRect.y += -dstPt.y; 4204 4205 final Rectangle dstRect = new Rectangle(dstPt.x, dstPt.y, adjSrcRect.width, adjSrcRect.height); 4206 // limit to destination image size 4207 final Rectangle adjDstRect = dstRect.intersection(new Rectangle(dstSizeX, getSizeY())); 4208 4209 final int w = Math.min(adjSrcRect.width, adjDstRect.width); 4210 final int h = Math.min(adjSrcRect.height, adjDstRect.height); 4211 4212 // nothing to copy 4213 if ((w == 0) || (h == 0)) 4214 return; 4215 4216 lockRaster(); 4217 try 4218 { 4219 final boolean signed = srcImage.getDataType_().isSigned(); 4220 final Object src = srcImage.getDataXY(srcChannel); 4221 final Object dst = getDataXY(dstChannel); 4222 4223 int srcOffset = adjSrcRect.x + (adjSrcRect.y * srcSizeX); 4224 int dstOffset = adjDstRect.x + (adjDstRect.y * dstSizeX); 4225 4226 for (int y = 0; y < h; y++) 4227 { 4228 ArrayUtil.arrayToArray(src, srcOffset, dst, dstOffset, w, signed); 4229 srcOffset += srcSizeX; 4230 dstOffset += dstSizeX; 4231 } 4232 } 4233 finally 4234 { 4235 releaseRaster(true); 4236 } 4237 4238 // notify data changed 4239 dataChanged(); 4240 } 4241 4242 /** 4243 * Internal copy data from a compatible image (notify data changed) 4244 * 4245 * @param srcImage 4246 * source image 4247 */ 4248 protected void internalCopyData(int srcChannel, int dstChannel, DataBuffer src_db, DataBuffer dst_db, int[] indices, 4249 int[] band_offsets, int[] bank_offsets, int scanlineStride_src, int pixelStride_src, int maxX, int maxY, 4250 int decOffsetSrc) 4251 { 4252 final int scanlineStride_dst = getSizeX(); 4253 4254 final int bank = indices[srcChannel]; 4255 final int offset = band_offsets[srcChannel] + bank_offsets[bank] - decOffsetSrc; 4256 4257 switch (getDataType_().getJavaType()) 4258 { 4259 case BYTE: 4260 { 4261 final byte[] src; 4262 final byte[] dst = ((DataBufferByte) dst_db).getData(dstChannel); 4263 4264 // LOCI use its own buffer classes 4265 if (src_db instanceof SignedByteBuffer) 4266 src = ((SignedByteBuffer) src_db).getData(bank); 4267 else 4268 src = ((DataBufferByte) src_db).getData(bank); 4269 4270 int offset_src = offset; 4271 int offset_dst = 0; 4272 for (int y = 0; y < maxY; y++) 4273 { 4274 int offset_src_pix = offset_src; 4275 int offset_dst_pix = offset_dst; 4276 4277 for (int x = 0; x < maxX; x++) 4278 { 4279 dst[offset_dst_pix] = src[offset_src_pix]; 4280 offset_src_pix += pixelStride_src; 4281 offset_dst_pix++; 4282 } 4283 4284 offset_src += scanlineStride_src; 4285 offset_dst += scanlineStride_dst; 4286 } 4287 break; 4288 } 4289 4290 case SHORT: 4291 { 4292 final short[] src; 4293 final short[] dst; 4294 4295 // LOCI use its own buffer classes 4296 if (src_db instanceof SignedShortBuffer) 4297 src = ((SignedShortBuffer) src_db).getData(bank); 4298 else if (src_db instanceof DataBufferShort) 4299 src = ((DataBufferShort) src_db).getData(bank); 4300 else 4301 src = ((DataBufferUShort) src_db).getData(bank); 4302 4303 if (dst_db instanceof DataBufferShort) 4304 dst = ((DataBufferShort) dst_db).getData(dstChannel); 4305 else 4306 dst = ((DataBufferUShort) dst_db).getData(dstChannel); 4307 4308 int offset_src = offset; 4309 int offset_dst = 0; 4310 for (int y = 0; y < maxY; y++) 4311 { 4312 int offset_src_pix = offset_src; 4313 int offset_dst_pix = offset_dst; 4314 4315 for (int x = 0; x < maxX; x++) 4316 { 4317 dst[offset_dst_pix] = src[offset_src_pix]; 4318 offset_src_pix += pixelStride_src; 4319 offset_dst_pix++; 4320 } 4321 4322 offset_src += scanlineStride_src; 4323 offset_dst += scanlineStride_dst; 4324 } 4325 break; 4326 } 4327 4328 case INT: 4329 { 4330 final int[] src; 4331 final int[] dst = ((DataBufferInt) dst_db).getData(dstChannel); 4332 4333 // LOCI use its own buffer classes 4334 if (src_db instanceof UnsignedIntBuffer) 4335 src = ((UnsignedIntBuffer) src_db).getData(bank); 4336 else 4337 src = ((DataBufferInt) src_db).getData(bank); 4338 4339 int offset_src = offset; 4340 int offset_dst = 0; 4341 for (int y = 0; y < maxY; y++) 4342 { 4343 int offset_src_pix = offset_src; 4344 int offset_dst_pix = offset_dst; 4345 4346 for (int x = 0; x < maxX; x++) 4347 { 4348 dst[offset_dst_pix] = src[offset_src_pix]; 4349 offset_src_pix += pixelStride_src; 4350 offset_dst_pix++; 4351 } 4352 4353 offset_src += scanlineStride_src; 4354 offset_dst += scanlineStride_dst; 4355 } 4356 break; 4357 } 4358 4359 case FLOAT: 4360 { 4361 final float[] src = ((DataBufferFloat) src_db).getData(bank); 4362 final float[] dst = ((DataBufferFloat) dst_db).getData(dstChannel); 4363 4364 int offset_src = offset; 4365 int offset_dst = 0; 4366 for (int y = 0; y < maxY; y++) 4367 { 4368 int offset_src_pix = offset_src; 4369 int offset_dst_pix = offset_dst; 4370 4371 for (int x = 0; x < maxX; x++) 4372 { 4373 dst[offset_dst_pix] = src[offset_src_pix]; 4374 offset_src_pix += pixelStride_src; 4375 offset_dst_pix++; 4376 } 4377 4378 offset_src += scanlineStride_src; 4379 offset_dst += scanlineStride_dst; 4380 } 4381 break; 4382 } 4383 4384 case DOUBLE: 4385 { 4386 final double[] src = ((DataBufferDouble) src_db).getData(bank); 4387 final double[] dst = ((DataBufferDouble) dst_db).getData(dstChannel); 4388 4389 int offset_src = offset; 4390 int offset_dst = 0; 4391 for (int y = 0; y < maxY; y++) 4392 { 4393 int offset_src_pix = offset_src; 4394 int offset_dst_pix = offset_dst; 4395 4396 for (int x = 0; x < maxX; x++) 4397 { 4398 dst[offset_dst_pix] = src[offset_src_pix]; 4399 offset_src_pix += pixelStride_src; 4400 offset_dst_pix++; 4401 } 4402 4403 offset_src += scanlineStride_src; 4404 offset_dst += scanlineStride_dst; 4405 } 4406 break; 4407 } 4408 4409 default: 4410 // do nothing here 4411 break; 4412 } 4413 } 4414 4415 /** 4416 * Copy channel data from a compatible sample model and writable raster (notify data changed). 4417 * 4418 * @param sampleModel 4419 * source sample model 4420 * @param raster 4421 * source writable raster to read data from 4422 * @param srcChannel 4423 * source channel (-1 for all channels) 4424 * @param dstChannel 4425 * destination channel (only significant if source channel != -1) 4426 * @return <code>true</code> if the copy operation succeed, <code>false</code> otherwise 4427 */ 4428 public boolean copyData(ComponentSampleModel sampleModel, WritableRaster sourceRaster, int srcChannel, 4429 int dstChannel) 4430 { 4431 // not compatible sample model 4432 if (DataType.getDataTypeFromDataBufferType(sampleModel.getDataType()) != getDataType_()) 4433 return false; 4434 4435 final DataBuffer src_db = sourceRaster.getDataBuffer(); 4436 final WritableRaster dst_raster = getRaster(); 4437 final DataBuffer dst_db = dst_raster.getDataBuffer(); 4438 final int[] indices = sampleModel.getBankIndices(); 4439 final int[] band_offsets = sampleModel.getBandOffsets(); 4440 final int[] bank_offsets = src_db.getOffsets(); 4441 final int scanlineStride_src = sampleModel.getScanlineStride(); 4442 final int pixelStride_src = sampleModel.getPixelStride(); 4443 final int maxX = Math.min(getSizeX(), sampleModel.getWidth()); 4444 final int maxY = Math.min(getSizeY(), sampleModel.getHeight()); 4445 final int decOffsetSrc = sourceRaster.getSampleModelTranslateX() 4446 + (sourceRaster.getSampleModelTranslateY() * scanlineStride_src); 4447 4448 // all channels 4449 if (srcChannel == -1) 4450 { 4451 final int numBands = sampleModel.getNumBands(); 4452 4453 for (int band = 0; band < numBands; band++) 4454 internalCopyData(band, band, src_db, dst_db, indices, band_offsets, bank_offsets, scanlineStride_src, 4455 pixelStride_src, maxX, maxY, decOffsetSrc); 4456 } 4457 else 4458 { 4459 internalCopyData(srcChannel, dstChannel, src_db, dst_db, indices, band_offsets, bank_offsets, 4460 scanlineStride_src, pixelStride_src, maxX, maxY, decOffsetSrc); 4461 } 4462 4463 // save in cache changed data 4464 saveRasterInCache(dst_raster); 4465 // notify data changed 4466 dataChanged(); 4467 4468 return true; 4469 } 4470 4471 /** 4472 * Copy data to specified location from an data array. 4473 * 4474 * @param data 4475 * source data array (should be same type than image data type) 4476 * @param dataDim 4477 * source data dimension (array length should be >= Dimension.width * Dimension.heigth) 4478 * @param signed 4479 * if the source data array should be considered as signed data (meaningful for integer 4480 * data type only) 4481 * @param dstPt 4482 * destination X,Y position (assume [0,0] if null) 4483 * @param dstChannel 4484 * destination channel 4485 */ 4486 public void copyData(Object data, Dimension dataDim, boolean signed, Point dstPt, int dstChannel) 4487 { 4488 if ((data == null) || (dataDim == null)) 4489 return; 4490 4491 // source image size 4492 final Rectangle adjSrcRect = new Rectangle(dataDim); 4493 // negative destination x position 4494 if (dstPt.x < 0) 4495 // adjust source rect 4496 adjSrcRect.x += -dstPt.x; 4497 // negative destination y position 4498 if (dstPt.y < 0) 4499 // adjust source rect 4500 adjSrcRect.y += -dstPt.y; 4501 4502 final Rectangle dstRect = new Rectangle(dstPt.x, dstPt.y, adjSrcRect.width, adjSrcRect.height); 4503 // limit to destination image size 4504 final Rectangle adjDstRect = dstRect.intersection(new Rectangle(getSizeX(), getSizeY())); 4505 4506 final int w = Math.min(adjSrcRect.width, adjDstRect.width); 4507 final int h = Math.min(adjSrcRect.height, adjDstRect.height); 4508 4509 // nothing to copy 4510 if ((w == 0) || (h == 0)) 4511 return; 4512 4513 lockRaster(); 4514 try 4515 { 4516 final Object dst = getDataXY(dstChannel); 4517 final int srcSizeX = dataDim.width; 4518 final int dstSizeX = getSizeX(); 4519 4520 int srcOffset = adjSrcRect.x + (adjSrcRect.y * srcSizeX); 4521 int dstOffset = adjDstRect.x + (adjDstRect.y * dstSizeX); 4522 4523 for (int y = 0; y < h; y++) 4524 { 4525 // do data copy (and conversion if needed) 4526 ArrayUtil.arrayToArray(data, srcOffset, dst, dstOffset, w, signed); 4527 srcOffset += srcSizeX; 4528 dstOffset += dstSizeX; 4529 } 4530 } 4531 finally 4532 { 4533 releaseRaster(true); 4534 } 4535 4536 // notify data changed 4537 dataChanged(); 4538 } 4539 4540 /** 4541 * Copy data from an image (notify data changed) 4542 * 4543 * @param srcImage 4544 * source image 4545 * @param srcRect 4546 * source region to copy (assume whole image if null) 4547 * @param dstPt 4548 * destination X,Y position (assume [0,0] if null) 4549 * @param srcChannel 4550 * source channel (-1 for all channels) 4551 * @param dstChannel 4552 * destination channel (only significant if source channel != -1) 4553 */ 4554 public void copyData(IcyBufferedImage srcImage, Rectangle srcRect, Point dstPt, int srcChannel, int dstChannel) 4555 { 4556 if (srcImage == null) 4557 return; 4558 4559 final Rectangle adjSrcRect; 4560 final Point adjDstPt; 4561 4562 if (srcRect == null) 4563 adjSrcRect = new Rectangle(srcImage.getSizeX(), srcImage.getSizeY()); 4564 else 4565 adjSrcRect = srcRect; 4566 if (dstPt == null) 4567 adjDstPt = new Point(0, 0); 4568 else 4569 adjDstPt = dstPt; 4570 4571 // copy all possible components 4572 if (srcChannel == -1) 4573 { 4574 final int sizeC = Math.min(srcImage.getSizeC(), getSizeC()); 4575 4576 beginUpdate(); 4577 try 4578 { 4579 for (int c = 0; c < sizeC; c++) 4580 fastCopyData(srcImage, adjSrcRect, adjDstPt, c, c); 4581 } 4582 finally 4583 { 4584 endUpdate(); 4585 } 4586 } 4587 else 4588 fastCopyData(srcImage, adjSrcRect, adjDstPt, srcChannel, dstChannel); 4589 } 4590 4591 /** 4592 * Copy data from an image (notify data changed) 4593 * 4594 * @param srcImage 4595 * source image 4596 * @param srcRect 4597 * source region to copy (assume whole image if null) 4598 * @param dstPt 4599 * destination (assume [0,0] if null) 4600 */ 4601 public void copyData(IcyBufferedImage srcImage, Rectangle srcRect, Point dstPt) 4602 { 4603 if (srcImage == null) 4604 return; 4605 4606 copyData(srcImage, srcRect, dstPt, -1, 0); 4607 } 4608 4609 /** 4610 * Copy data from an image (notify data changed) 4611 * 4612 * @param srcImage 4613 * source image 4614 * @param srcChannel 4615 * source channel to copy (-1 for all channels) 4616 * @param dstChannel 4617 * destination channel to receive data (only significant if source channel != -1) 4618 */ 4619 public void copyData(BufferedImage srcImage, int srcChannel, int dstChannel) 4620 { 4621 if (srcImage == null) 4622 return; 4623 4624 if (srcImage instanceof IcyBufferedImage) 4625 copyData(((IcyBufferedImage) srcImage), null, null, srcChannel, dstChannel); 4626 else 4627 { 4628 final boolean done; 4629 4630 // try to use faster copy for compatible image 4631 if (srcImage.getSampleModel() instanceof ComponentSampleModel) 4632 done = copyData((ComponentSampleModel) srcImage.getSampleModel(), srcImage.getRaster(), srcChannel, 4633 dstChannel); 4634 else 4635 done = false; 4636 4637 if (!done) 4638 { 4639 final WritableRaster wr = getRaster(); 4640 // image not compatible, use generic (and slow) data copy 4641 srcImage.copyData(wr); 4642 // save changed data in cache 4643 saveRasterInCache(wr); 4644 // notify data changed 4645 dataChanged(); 4646 } 4647 } 4648 } 4649 4650 /** 4651 * Copy data from an image (notify data changed) 4652 * 4653 * @param srcImage 4654 * source image 4655 */ 4656 public void copyData(BufferedImage srcImage) 4657 { 4658 copyData(srcImage, -1, -1); 4659 } 4660 4661 /** 4662 * Return raw data component as an array of byte 4663 * 4664 * @param c 4665 * component index 4666 * @param out 4667 * output array (can be null) 4668 * @param offset 4669 * output offset 4670 * @param little 4671 * little endian order 4672 */ 4673 public byte[] getRawData(int c, byte[] out, int offset, boolean little) 4674 { 4675 // alloc output array if needed 4676 final byte[] result = Array1DUtil.allocIfNull(out, 4677 offset + (getSizeX() * getSizeY() * getDataType_().getSize())); 4678 4679 return ByteArrayConvert.toByteArray(getDataXY(c), 0, result, offset, little); 4680 } 4681 4682 /** 4683 * Return raw data component as an array of byte 4684 * 4685 * @param c 4686 * component index 4687 * @param little 4688 * little endian order 4689 */ 4690 public byte[] getRawData(int c, boolean little) 4691 { 4692 return getRawData(c, null, 0, little); 4693 } 4694 4695 /** 4696 * Return raw data for all components as an array of byte 4697 * 4698 * @param out 4699 * output array (can be null) 4700 * @param offset 4701 * output offset 4702 * @param little 4703 * little endian order 4704 */ 4705 public byte[] getRawData(byte[] out, int offset, boolean little) 4706 { 4707 final int sizeXY = getSizeX() * getSizeY(); 4708 final int sizeC = getSizeC(); 4709 final int sizeType = getDataType_().getSize(); 4710 4711 // alloc output array if needed 4712 final byte[] result = Array1DUtil.allocIfNull(out, offset + (sizeC * sizeXY * sizeType)); 4713 4714 int outOff = offset; 4715 for (int c = 0; c < sizeC; c++) 4716 { 4717 getRawData(c, result, outOff, little); 4718 outOff += sizeXY * sizeType; 4719 } 4720 4721 return result; 4722 } 4723 4724 /** 4725 * Return raw data for all components as an array of byte 4726 * 4727 * @param little 4728 * little endian order 4729 */ 4730 public byte[] getRawData(boolean little) 4731 { 4732 return getRawData(null, 0, little); 4733 } 4734 4735 /** 4736 * Set raw data component from an array of byte (notify data changed) 4737 * 4738 * @param c 4739 * component index 4740 * @param data 4741 * data as byte array 4742 * @param offset 4743 * input offset 4744 * @param little 4745 * little endian order 4746 */ 4747 public void setRawData(int c, byte[] data, int offset, boolean little) 4748 { 4749 if (data == null) 4750 return; 4751 4752 lockRaster(); 4753 try 4754 { 4755 ByteArrayConvert.byteArrayTo(data, offset, getDataXY(c), 0, -1, little); 4756 } 4757 finally 4758 { 4759 releaseRaster(true); 4760 } 4761 4762 // notify data changed 4763 dataChanged(); 4764 } 4765 4766 /** 4767 * Set raw data component from an array of byte (notify data changed) 4768 * 4769 * @param c 4770 * component index 4771 * @param data 4772 * data as byte array 4773 * @param little 4774 * little endian order 4775 */ 4776 public void setRawData(int c, byte[] data, boolean little) 4777 { 4778 setRawData(c, data, 0, little); 4779 } 4780 4781 /** 4782 * Set raw data for all components from an array of byte (notify data changed).<br/> 4783 * Data are arranged in the following dimension order: XYC 4784 * 4785 * @param data 4786 * data as byte array 4787 * @param offset 4788 * input offset 4789 * @param little 4790 * little endian order 4791 */ 4792 public void setRawData(byte[] data, int offset, boolean little) 4793 { 4794 if (data == null) 4795 return; 4796 4797 final int sizeXY = getSizeX() * getSizeY(); 4798 final int sizeC = getSizeC(); 4799 final int sizeType = getDataType_().getSize(); 4800 4801 beginUpdate(); 4802 try 4803 { 4804 int inOff = offset; 4805 for (int c = 0; c < sizeC; c++) 4806 { 4807 setRawData(c, data, inOff, little); 4808 inOff += sizeXY * sizeType; 4809 } 4810 } 4811 finally 4812 { 4813 endUpdate(); 4814 } 4815 } 4816 4817 /** 4818 * Set raw data for all components from an array of byte (notify data changed) 4819 * 4820 * @param data 4821 * data as byte array 4822 * @param little 4823 * little endian order 4824 */ 4825 public void setRawData(byte[] data, boolean little) 4826 { 4827 setRawData(data, 0, little); 4828 } 4829 4830 /** 4831 * Return the colormap of the specified channel. 4832 */ 4833 public IcyColorMap getColorMap(int channel) 4834 { 4835 return getIcyColorModel().getColorMap(channel); 4836 } 4837 4838 /** 4839 * @deprecated Use {@link #getColorMap(int)} instead (different case). 4840 */ 4841 @Deprecated 4842 public IcyColorMap getColormap(int channel) 4843 { 4844 return getColorMap(channel); 4845 } 4846 4847 /** 4848 * @deprecated Use {@link #setColorMaps(BufferedImage)} instead. 4849 */ 4850 @Deprecated 4851 public void copyColormap(BufferedImage srcImage) 4852 { 4853 setColorMaps(srcImage); 4854 } 4855 4856 /** 4857 * Set colormaps from specified image. 4858 */ 4859 public void setColorMaps(BufferedImage srcImage) 4860 { 4861 getIcyColorModel().setColorMaps(srcImage.getColorModel()); 4862 } 4863 4864 /** 4865 * @deprecated Use {@link #setColorMaps(BufferedImage)} instead (different case). 4866 */ 4867 @Deprecated 4868 public void setColormaps(BufferedImage srcImage) 4869 { 4870 setColorMaps(srcImage); 4871 } 4872 4873 /** 4874 * Set the colormap for the specified channel. 4875 * 4876 * @param channel 4877 * channel we want to set the colormap 4878 * @param map 4879 * source colorspace to copy 4880 * @param setAlpha 4881 * also set the alpha information 4882 */ 4883 public void setColorMap(int channel, IcyColorMap map, boolean setAlpha) 4884 { 4885 getIcyColorModel().setColorMap(channel, map, setAlpha); 4886 } 4887 4888 /** 4889 * Set the colormap for the specified channel. 4890 * 4891 * @param channel 4892 * channel we want to set the colormap 4893 * @param map 4894 * source colorspace to copy 4895 */ 4896 public void setColorMap(int channel, IcyColorMap map) 4897 { 4898 getIcyColorModel().setColorMap(channel, map, map.isAlpha()); 4899 } 4900 4901 /** 4902 * @deprecated Use {@link #setColorMap(int, IcyColorMap, boolean)} instead. 4903 */ 4904 @Deprecated 4905 public void setColormap(int channel, IcyColorMap map) 4906 { 4907 setColorMap(channel, map, true); 4908 } 4909 4910 /** 4911 * notify image data has changed 4912 */ 4913 public void dataChanged() 4914 { 4915 updater.changed(new IcyBufferedImageEvent(this, IcyBufferedImageEventType.DATA_CHANGED)); 4916 } 4917 4918 /** 4919 * notify image colorMap has changed 4920 */ 4921 protected void colormapChanged(int component) 4922 { 4923 updater.changed(new IcyBufferedImageEvent(this, IcyBufferedImageEventType.COLORMAP_CHANGED, component)); 4924 } 4925 4926 /** 4927 * notify image channels bounds has changed 4928 */ 4929 public void channelBoundsChanged(int channel) 4930 { 4931 updater.changed(new IcyBufferedImageEvent(this, IcyBufferedImageEventType.BOUNDS_CHANGED, channel)); 4932 } 4933 4934 /** 4935 * @deprecated Use {@link #channelBoundsChanged(int)} instead. 4936 */ 4937 @Deprecated 4938 public void componentBoundsChanged(int component) 4939 { 4940 channelBoundsChanged(component); 4941 } 4942 4943 /** 4944 * fire change event 4945 */ 4946 protected void fireChangeEvent(IcyBufferedImageEvent e) 4947 { 4948 for (IcyBufferedImageListener listener : new ArrayList<IcyBufferedImageListener>(listeners)) 4949 listener.imageChanged(e); 4950 } 4951 4952 public void addListener(IcyBufferedImageListener listener) 4953 { 4954 listeners.add(listener); 4955 } 4956 4957 public void removeListener(IcyBufferedImageListener listener) 4958 { 4959 listeners.remove(listener); 4960 } 4961 4962 public void beginUpdate() 4963 { 4964 updater.beginUpdate(); 4965 } 4966 4967 public void endUpdate() 4968 { 4969 updater.endUpdate(); 4970 } 4971 4972 public boolean isUpdating() 4973 { 4974 return updater.isUpdating(); 4975 } 4976 4977 @Override 4978 public void onChanged(CollapsibleEvent object) 4979 { 4980 IcyBufferedImageEvent event = (IcyBufferedImageEvent) object; 4981 4982 switch (event.getType()) 4983 { 4984 // do here global process on image data change 4985 case DATA_CHANGED: 4986 // update image components bounds 4987 if (autoUpdateChannelBounds) 4988 updateChannelsBounds(); 4989 break; 4990 4991 // do here global process on image bounds change 4992 case BOUNDS_CHANGED: 4993 break; 4994 4995 // do here global process on image colormap change 4996 case COLORMAP_CHANGED: 4997 break; 4998 } 4999 5000 // notify listener we have changed 5001 fireChangeEvent(event); 5002 } 5003 5004 @Override 5005 public void colorModelChanged(IcyColorModelEvent e) 5006 { 5007 switch (e.getType()) 5008 { 5009 case COLORMAP_CHANGED: 5010 colormapChanged(e.getComponent()); 5011 break; 5012 5013 case SCALER_CHANGED: 5014 channelBoundsChanged(e.getComponent()); 5015 break; 5016 } 5017 } 5018 5019 @Override 5020 public String toString() 5021 { 5022 return "IcyBufferedImage: " + getSizeX() + " x " + getSizeY() + " - " + getSizeC() + " ch (" + getDataType_() 5023 + ")"; 5024 } 5025}