4 package ini
.trakem2
.display
;
8 import ij
.process
.ImageProcessor
;
9 import ini
.trakem2
.Project
;
10 import ini
.trakem2
.utils
.Utils
;
12 import java
.awt
.AlphaComposite
;
13 import java
.awt
.Composite
;
14 import java
.awt
.Graphics2D
;
15 import java
.awt
.Image
;
16 import java
.awt
.Rectangle
;
17 import java
.awt
.geom
.AffineTransform
;
18 import java
.awt
.geom
.Area
;
19 import java
.util
.ArrayList
;
20 import java
.util
.HashMap
;
21 import java
.util
.HashSet
;
22 import java
.util
.Iterator
;
24 import java
.util
.TreeMap
;
25 import java
.util
.concurrent
.Callable
;
26 import java
.util
.concurrent
.Future
;
28 import mpicbg
.ij
.stack
.InverseTransformMapping
;
29 import mpicbg
.models
.AffineModel2D
;
30 import mpicbg
.models
.Boundable
;
31 import mpicbg
.models
.InvertibleCoordinateTransformList
;
32 import mpicbg
.models
.TranslationModel3D
;
33 import mpicbg
.trakem2
.transform
.InvertibleCoordinateTransform
;
34 import mpicbg
.util
.Util
;
40 public class Stack
extends ZDisplayable
42 private String file_path
;
43 private double depth
= -1;
46 private InvertibleCoordinateTransform ict
;
47 final private float[] boundsMin
= new float[]{ 0, 0, 0 };
48 final private float[] boundsMax
= new float[]{ 0, 0, 0 };
51 * References cached images in Loader whose unique identifier from the
52 * prespective of the stack are double:magnification and double:z.
54 final private HashMap
< SliceViewKey
, Long
> cachedImages
= new HashMap
< SliceViewKey
, Long
>();
56 final private HashMap
< Long
, Future
< Image
> > futureImages
= new HashMap
< Long
, Future
<Image
> >();
58 private static class SliceViewKey
60 final double magnification
;
63 SliceViewKey( final double magnification
, final double z
)
65 this.magnification
= magnification
;
70 final public boolean equals( Object o
)
72 final SliceViewKey k
= ( SliceViewKey
)o
;
73 return k
.magnification
== magnification
&& k
.z
== z
;
77 final public int hashCode()
83 public Stack( final Project project
, final String title
, double x
, double y
, Layer initial_layer
, final String file_path
)
85 super( project
, title
, x
, y
);
86 this.file_path
= file_path
;
87 // ct ==> initial_layer;
88 final ImagePlus imp
= project
.getLoader().fetchImagePlus( this );
90 /* TODO scale regarding the Calibration and shift regarding x, y and the initial_layer */
91 depth
= imp
.getNSlices();
92 width
= imp
.getWidth();
93 height
= imp
.getHeight();
94 min
= imp
.getDisplayRangeMin();
95 max
= imp
.getDisplayRangeMax();
98 boundsMin
[ 0 ] = ( float )x
;
99 boundsMin
[ 1 ] = ( float )y
;
100 boundsMin
[ 2 ] = ( float )initial_layer
.getZ();
102 boundsMax
[ 0 ] = ( float )( x
+ width
);
103 boundsMax
[ 1 ] = ( float )( y
+ height
);
104 boundsMax
[ 2 ] = ( float )( boundsMin
[ 2 ] + depth
);
109 /** Construct a Stack from an XML entry. */
110 public Stack(Project project
, long id
, HashMap ht
, HashMap ht_links
) {
111 super(project
, id
, ht
, ht_links
);
112 // parse specific fields
114 final Iterator it
= ht
.entrySet().iterator();
115 while (it
.hasNext()) {
116 final Map
.Entry entry
= (Map
.Entry
)it
.next();
117 final String key
= (String
)entry
.getKey();
118 final String data
= (String
)entry
.getValue();
119 if (key
.equals("min")) {
120 this.min
= Double
.parseDouble(data
);
121 } else if (key
.equals("max")) {
122 this.max
= Double
.parseDouble(data
);
123 } else if (key
.equals("file_path")) {
124 this.file_path
= data
;
125 } else if (key
.equals("depth")) {
126 this.depth
= Double
.parseDouble(data
);
133 boundsMax
[ 0 ] = ( float )width
;
134 boundsMax
[ 1 ] = ( float )height
;
135 boundsMax
[ 2 ] = ( float )depth
;
139 * @see ini.trakem2.display.ZDisplayable#getFirstLayer()
142 public Layer
getFirstLayer()
144 // TODO Auto-generated method stub
149 * @see ini.trakem2.display.ZDisplayable#intersects(java.awt.geom.Area, double, double)
152 public boolean intersects( Area area
, double z_first
, double z_last
)
154 // TODO Auto-generated method stub
159 * @see ini.trakem2.display.ZDisplayable#linkPatches()
162 public void linkPatches()
164 // TODO Auto-generated method stub
169 * @see ini.trakem2.display.Displayable#clone(ini.trakem2.Project, boolean)
172 public Displayable
clone( Project pr
, boolean copy_id
)
174 // TODO Auto-generated method stub
179 * @see ini.trakem2.display.Displayable#isDeletable()
182 public boolean isDeletable()
184 // TODO Auto-generated method stub
188 public String
getFilePath(){
189 return this.file_path
;
192 public long estimateImageFileSize()
195 return IJ
.maxMemory() / 2;
196 return (long) (width
* height
* depth
* 4);
202 final double magnification
,
203 final boolean active
,
205 final Layer active_layer
)
208 final AffineTransform atp
= this.at
;
210 //final Image image = project.getLoader().fetchImage(this,0);
211 //Utils.log2("Patch " + id + " painted image " + image);
213 final double currentZ
= active_layer
.getZ();
215 synchronized ( cachedImages
)
217 final SliceViewKey sliceViewKey
= new SliceViewKey( magnification
, currentZ
);
219 Long imageIdL
= cachedImages
.get( sliceViewKey
);
220 if ( imageIdL
== null )
222 imageId
= project
.getLoader().getNextTempId();
223 cachedImages
.put( sliceViewKey
, imageId
);
227 /* fetch the image from cache---still, it may be that it is not there... */
229 image
= project
.getLoader().getCached( cachedImages
.get( sliceViewKey
), 0 );
234 /* image has to be generated */
236 synchronized ( futureImages
)
238 Future
< Image
> fu
= futureImages
.get( imageId
);
240 futureImages
.put( imageId
, project
.getLoader().doLater( new Callable
< Image
>() {
241 public Image
call() {
242 final ImagePlus imp
= project
.getLoader().fetchImagePlus( Stack
.this );
243 final ImageProcessor ip
= imp
.getStack().getProcessor( 1 ).createProcessor( ( int )Math
.ceil( boundsMax
[ 0 ] - boundsMin
[ 0 ] ), ( int )Math
.ceil( boundsMax
[ 1 ] - boundsMin
[ 1 ] ) );
245 final InvertibleCoordinateTransformList
< mpicbg
.models
.InvertibleCoordinateTransform
> ictl
= new InvertibleCoordinateTransformList
< mpicbg
.models
.InvertibleCoordinateTransform
>();
246 if ( ict
!= null ) ictl
.add( ict
);
247 final TranslationModel3D sliceShift
= new TranslationModel3D();
248 sliceShift
.set( 0, 0, ( float )-currentZ
);
249 ictl
.add( sliceShift
);
251 final InverseTransformMapping
< InvertibleCoordinateTransformList
< mpicbg
.models
.InvertibleCoordinateTransform
> > mapping
= new InverseTransformMapping
< InvertibleCoordinateTransformList
< mpicbg
.models
.InvertibleCoordinateTransform
> >( ictl
);
252 mapping
.mapInterpolated( imp
.getStack(), ip
);
253 final Image image
= ip
.createImage();
256 Utils
.log2("Stack.paint: null image, returning");
257 return null; // TEMPORARY from lazy repaints after closing a Project
260 project
.getLoader().cacheAWT( imageId
, image
);
262 synchronized ( futureImages
) {
263 futureImages
.remove( imageId
);
266 //Display.repaint( active_layer, Stack.this );
267 Display
.repaint( active_layer
);
273 // Utils.log2( "fu is not null" );
274 // // We don't do anything: we wait for itself to launch a repaint event
280 if ( image
!= null) {
282 //arrange transparency
283 Composite original_composite
= null;
285 original_composite
= g
.getComposite();
286 g
.setComposite(AlphaComposite
.getInstance(AlphaComposite
.SRC_OVER
, alpha
));
289 final AffineTransform backup
= g
.getTransform();
290 final AffineTransform screenScale
= new AffineTransform( backup
);
291 screenScale
.scale( 1.0 / magnification
, 1.0 / magnification
);
292 g
.setTransform( screenScale
);
294 g
.drawImage(image
, atp
, null);
296 g
.setTransform( backup
);
298 //Transparency: fix composite back to original.
300 g
.setComposite(original_composite
);
306 static public final void exportDTD( final StringBuffer sb_header
, final HashSet hs
, final String indent
) {
307 String type
= "t2_stack";
308 if (hs
.contains(type
)) return;
310 sb_header
.append(indent
).append("<!ELEMENT t2_stack (").append(Displayable
.commonDTDChildren()).append(",(iict_transform|iict_transform_list)?)>\n");
311 Displayable
.exportDTD( type
, sb_header
, hs
, indent
);
312 sb_header
.append(indent
).append(TAG_ATTR1
).append(type
).append(" file_path CDATA #REQUIRED>\n")
313 .append(indent
).append(TAG_ATTR1
).append(type
).append(" depth CDATA #REQUIRED>\n");
316 /** Opens and closes the tag and exports data. The image is saved in the directory provided in @param any as a String. */
317 public void exportXML(StringBuffer sb_body
, String indent
, Object any
) { // TODO the Loader should handle the saving of images, not this class.
318 String in
= indent
+ "\t";
319 sb_body
.append(indent
).append("<t2_stack\n");
320 String rel_path
= null;
322 super.exportXML(sb_body
, in
, any
);
323 String
[] RGB
= Utils
.getHexRGBColor(color
);
325 sb_body
.append(in
).append("file_path=\"").append(file_path
).append("\"\n")
326 .append(in
).append("style=\"fill-opacity:").append(alpha
).append(";stroke:#").append(RGB
[0]).append(RGB
[1]).append(RGB
[2]).append(";\"\n")
327 .append(in
).append("depth=\"").append(depth
).append("\"\n")
328 .append(in
).append("min=\"").append(min
).append("\"\n")
329 .append(in
).append("max=\"").append(max
).append("\"\n")
332 sb_body
.append(indent
).append(">\n");
335 sb_body
.append(ict
.toXML(in
)).append('\n');
338 super.restXML(sb_body
, in
, any
);
340 sb_body
.append(indent
).append("</t2_stack>\n");
344 protected Rectangle
getBounds( final Rectangle rect
)
346 final AffineModel2D a
= new AffineModel2D();
349 final float[] rMin
= new float[]{ Float
.MAX_VALUE
, Float
.MAX_VALUE
};
350 final float[] rMax
= new float[]{ -Float
.MAX_VALUE
, -Float
.MAX_VALUE
};
352 final float[] l
= new float[]{ boundsMin
[ 0 ], boundsMin
[ 1 ] };
358 l
[ 0 ] = boundsMin
[ 0 ];
359 l
[ 1 ] = boundsMax
[ 1 ];
364 l
[ 0 ] = boundsMax
[ 0 ];
365 l
[ 1 ] = boundsMin
[ 1 ];
370 l
[ 0 ] = boundsMax
[ 0 ];
371 l
[ 1 ] = boundsMax
[ 1 ];
376 rect
.x
= ( int )rMin
[ 0 ];
377 rect
.y
= ( int )rMin
[ 1 ];
378 rect
.width
= ( int )Math
.ceil( rMax
[ 0 ] - rect
.x
);
379 rect
.height
= ( int )Math
.ceil( rMax
[ 1 ] - rect
.y
);
384 private void updateBounds()
390 boundsMax
[ 0 ] = ( float )width
;
391 boundsMax
[ 1 ] = ( float )height
;
392 boundsMax
[ 2 ] = ( float )depth
;
394 if ( ict
== null ) return;
395 else if ( Boundable
.class.isInstance( ict
) )
396 ( ( Boundable
)ict
).estimateBounds( boundsMin
, boundsMax
);
399 final ArrayList
< Layer
> layers
= layer_set
.getLayers();
400 boundsMax
[ 0 ] = ( float )layer_set
.width
;
401 boundsMax
[ 1 ] = ( float )layer_set
.height
;
402 boundsMax
[ 2 ] = ( float )( layers
.get( layers
.size() - 1 ).getZ() - layers
.get( 0 ).getZ() );
406 /** For reconstruction purposes, overwrites the present InvertibleCoordinateTransform, if any, with the given one. */
407 public void setInvertibleCoordinateTransformSilently( final InvertibleCoordinateTransform ict
)
409 cachedImages
.clear();