/* * @(#)/XSliceVolumeFilter.java 1.4 96/03/31 by Andrew Barclay abb@nuccard.eushc.org * * Copyright (c) 1995 Andrew B. Barclay All Rights Reserved. */ import java.awt.image.ColorModel ; import java.awt.image.ImageConsumer ; import java.awt.image.ImageProducer ; import java.awt.Point ; import java.awt.Rectangle ; import SliceVolumeFilter ; /** * An ImageFilter class for sliceing volume images. * This class extends the basic ImageFilter Class to extract a given * slice of an existing Volume and provide a source for a * new image containing just the extracted slice. It is meant to * be used in conjunction with a FilteredImageSource object to produce * sliced versions of existing volumes. * * This one's a bear, because the image granularity is 1 pixel. After * endless trials (really) I've resorted to just waiting for complete * scanlines before updating. Random pixel delivery to the dithering * engine produces unpredictable results. * * @see CropImageFilter * @see FilteredImageSource * @see ImageFilter * @see SliceVolumeFilter * * @version 1.4 96/03/31 * @author Andrew Barclay */ public class XSliceVolumeFilter extends SliceVolumeFilter { public byte dpixbyte[] ; // byte extracted pixel buffer public int dpixint[] ; // int extracted pixel buffer double szincr ; // source z-increment for each dest. z slice int nslices ; int hints ; int completecols[] ; int completerows ; ColorModel colormodel ; // last colormodel (for resends) synchronized void dbg( String s ) { if( false ) { System.out.println( s ) ; } } /** * Construct a SliceVolumeFilter that extracts a slice of the source * Volume specified by the ul, ll and ur parameters. * @param srcSlices[] the array of x,y positions of the source image slices * @param volWidth the width of the source volume * @param volHeight the height of the source volume * @param slice the slice number to be extracted * @param width the width of the slice to be extracted * @param height the height of the slice to be extracted */ public XSliceVolumeFilter( Point srcSlices[], int volWidth, int volHeight, double slice, int width, int height ) { super( srcSlices, volWidth, volHeight, slice, width, height ) ; type = 'X' ; szincr = (double)voldims[2] / (double)width ; // usually 1.0 nslices = voldims[0] ; hints = 0 ; completecols = new int[height] ; for( int i = 0 ; i < height ; i++ ) { completecols[i] = 0 ; } completerows = 0 ; } public void resendTopDownLeftRight( ImageProducer ip ) { dbg( "resendTopDownLeftRight called." ) ; if( dpixbyte != null ) { setConsumerPixels( 0, 0, width, height, this.colormodel, dpixbyte, 0, width ) ; } else if( dpixint != null ) { setConsumerPixels( 0, 0, width, height, this.colormodel, dpixint, 0, width ) ; } } public void setHints( int hints ) { this.hints = hints ; dbg( "setHints called, COMPLETESCANLINES="+((hints&ImageConsumer.COMPLETESCANLINES)!=0)+", RANDOMPIXELORDER="+((hints&ImageConsumer.RANDOMPIXELORDER)!=0) ) ; dbg( "SINGLEPASS="+((hints&ImageConsumer.SINGLEPASS)!=0)+", SINGLEFRAME="+((hints&ImageConsumer.SINGLEFRAME)!=0) ) ; } /** * Decide if we can deliver complete scanlines before sending * pixels to consumer. */ public void setConsumerPixels( int sx, int sy, int sw, int sh, ColorModel colormodel, byte spix[], int soff, int sscansize ) { if( sw != width ) { /* * The code below doesn't work and is very cpu intensive. * I suspect that the API cannot handle RANDOMPIXELORDER. * * Random delivery code: * In this case, clear all other hints so the consumer * will request a resend. The next time through we * should have complete scanlines, etc. * hints = ImageConsumer.RANDOMPIXELORDER ; consumer.setHints( hints ) ; consumer.setPixels( sx, sy, sw, sh, colormodel, spix, soff, sscansize ) ; */ dbg( "g.setConsumerPixels.byte: sx="+sx+" sy="+sy+" sw="+sw+" sh="+sh+"." ) ; for( int y = sy ; y < sy+sh ; y++ ) { completecols[y] += sw ; if( completecols[y] == width ) { completerows++ ; /* * This doesn't work either -- just get a blank image, * though I'm delivering real data to the consumer??? * // spit complete line out. int p = y*width ; byte max = spix[p++] ; for( int i = 1 ; i < width ; i++ ) { if( spix[p] > max ) max = spix[p] ; p++ ; } System.out.println( "completed line "+y+" max="+max+"." ) ; //consumer.setHints( COMPLETESCANLINES|SINGLEPASS|SINGLEFRAME ) ; consumer.setHints( hints ) ; consumer.setPixels( 0, y, width, 1, colormodel, spix, soff, sscansize ) ; */ } } /* * Full buffering is the only way it works reliably. */ if( completerows == height ) { consumer.setHints( hints ) ; consumer.setPixels( 0, 0, width, height, colormodel, dpixbyte, 0, width ) ; } } else { dbg( "setConsumerPixels.byte: sw==width="+sw+".") ; consumer.setHints( hints ) ; consumer.setPixels( sx, sy, sw, sh, colormodel, spix, soff, sscansize ) ; } } public void setConsumerPixels( int sx, int sy, int sw, int sh, ColorModel colormodel, int spix[], int soff, int sscansize ) { if( sw != width ) { dbg( "g.setConsumerPixels.int: sx="+sx+" sy="+sy+" sw="+sw+" sh="+sh+"." ) ; for( int y = sy ; y < sy+sh ; y++ ) { completecols[y] += sw ; if( completecols[y] == width ) { completerows++ ; } } /* * Full buffering is the only way it works reliably. */ if( completerows == height ) { consumer.setHints( hints ) ; consumer.setPixels( 0, 0, width, height, colormodel, dpixint, 0, width ) ; } } else { dbg( "setConsumerPixels.int: sw==width="+sw+".") ; consumer.setHints( hints ) ; consumer.setPixels( sx, sy, sw, sh, colormodel, spix, soff, sscansize ) ; } } /** * Determine if the delivered pixels intersect the slice to * be extracted and pass through only that subset of pixels that * appear in the output slice. */ public void setPixels( int sx, int sy, int sw, int sh, ColorModel colormodel, byte spix[], int soff, int sscansize ) { long ct = System.currentTimeMillis() ; Rectangle sr = new Rectangle( sx, sy, sw, sh ) ; int sz1 = firstZSlice( sr ) ; // first z-slice intersecting region int sz2 = lastZSlice( sr ) ; // last z-slice intersecting region this.colormodel = colormodel ; /* dbg( "sr="+sr+" sz1="+sz1+" sz2="+sz2 ) ; dbg( "off="+soff+" scansize="+sscansize+" type="+type+" slice="+slice ) ; */ // implement this generally later -- for now, just take orthogonal // slices. int islice = (int)( 0.5 + slice ) ; if( islice >= 0 && islice < nslices ) { // fix later: the allocation should depend on szincr too... if( dpixbyte == null ) { dpixbyte = new byte[width*height] ; } double sz ; int ncols = 0 ; int lastxstart = 0, lastystart = 0, lastyend = 0 ; for( sz = -0.5*szincr + sz1 ; sz < (-0.5 + sz2) ; sz += szincr ) { // z-coords in source correspond to x-coords in dest. int xstart = (int)( 0.5 + sz/szincr ) ; int isz = (int)( 0.5 + sz ) ; int x1 = srcSlices[isz].x + islice ; int y1 = srcSlices[isz].y ; if( sy < (y1+height) && (sy+sh) > y1 && sx <= x1 && (sx+sw) > x1 ) { int ystart = (y1>=sy) ? 0 : sy-y1 ; int yend = ((y1+height)>(sy+sh)) ? (sy+sh-y1) : height ; int is = soff + (x1-sx) + (y1+ystart-sy)*sscansize ; int id = xstart + ystart*width ; // Copy portion of column from the source to dest. for( int dy = ystart ; dy < yend ; dy++ ) { dpixbyte[id] = spix[is] ; is += sscansize ; id += width ; } if( ncols == 0 ) { // initialize these. lastxstart = xstart ; lastystart = ystart ; lastyend = yend ; ncols = 1 ; } else if( lastystart != ystart || lastyend != yend ) { // spit out columns up to this column. setConsumerPixels( lastxstart, lastystart, ncols, lastyend-lastystart, colormodel, dpixbyte, 0, width ) ; lastxstart = xstart ; lastystart = ystart ; lastyend = yend ; ncols = 1 ; } else { ncols++ ; } } else if( ncols > 0 ) { // probably skipping a z-slice, spit out columns up // to this column. setConsumerPixels( lastxstart, lastystart, ncols, lastyend-lastystart, colormodel, dpixbyte, 0, width ) ; ncols = 0 ; } } // spit out remaining columns. if( ncols > 0 ) { setConsumerPixels( lastxstart, lastystart, ncols, lastyend-lastystart, colormodel, dpixbyte, 0, width ) ; dbg( "byte time= "+(System.currentTimeMillis()-ct)+" ms.\n" ) ; dbg( "xslice="+islice+" sr="+sr+" sz1="+sz1+" sz2="+sz2 ) ; dbg( "lastxstart="+lastxstart+" ncols="+ncols ) ; dbg( "lastystart="+lastystart+" lastyend="+lastyend ) ; } } } public void setPixels( int sx, int sy, int sw, int sh, ColorModel colormodel, int spix[], int soff, int sscansize ) { long ct = System.currentTimeMillis() ; Rectangle sr = new Rectangle( sx, sy, sw, sh ) ; int sz1 = firstZSlice( sr ) ; // first z-slice intersecting region int sz2 = lastZSlice( sr ) ; // last z-slice intersecting region this.colormodel = colormodel ; /* dbg( "sr="+sr+" sz1="+sz1+" sz2="+sz2 ) ; dbg( "off="+soff+" scansize="+sscansize+" type="+type+" slice="+slice ) ; */ // implement this generally later -- for now, just take orthogonal // slices. int islice = (int)( 0.5 + slice ) ; if( islice >= 0 && islice < nslices ) { // fix later: the allocation should depend on szincr too... if( dpixint == null ) { dpixint = new int[width*height] ; } double sz ; int ncols = 0 ; int lastxstart = 0, lastystart = 0, lastyend = 0 ; for( sz = -0.5*szincr + sz1 ; sz < (-0.5 + sz2) ; sz += szincr ) { // z-coords in source correspond to x-coords in dest. int xstart = (int)( 0.5 + sz/szincr ) ; int isz = (int)( 0.5 + sz ) ; int x1 = srcSlices[isz].x + islice ; int y1 = srcSlices[isz].y ; if( sy < (y1+height) && (sy+sh) > y1 && sx <= x1 && (sx+sw) > x1 ) { int ystart = (y1>=sy) ? 0 : sy-y1 ; int yend = ((y1+height)>(sy+sh)) ? (sy+sh-y1) : height ; int is = soff + (x1-sx) + (y1+ystart-sy)*sscansize ; int id = xstart + ystart*width ; // Copy portion of column from the source to dest. for( int dy = ystart ; dy < yend ; dy++ ) { dpixint[id] = spix[is] ; is += sscansize ; id += width ; } if( ncols == 0 ) { // initialize these. lastxstart = xstart ; lastystart = ystart ; lastyend = yend ; ncols = 1 ; } else if( lastystart != ystart || lastyend != yend ) { // spit out columns up to this column. setConsumerPixels( lastxstart, lastystart, ncols, lastyend-lastystart, colormodel, dpixint, 0, width ) ; lastxstart = xstart ; lastystart = ystart ; lastyend = yend ; ncols = 1 ; } else { ncols++ ; } } else if( ncols > 0 ) { // probably skipping a z-slice, spit out columns up // to this column. setConsumerPixels( lastxstart, lastystart, ncols, lastyend-lastystart, colormodel, dpixint, 0, width ) ; ncols = 0 ; } } // spit out remaining columns. if( ncols > 0 ) { setConsumerPixels( lastxstart, lastystart, ncols, lastyend-lastystart, colormodel, dpixint, 0, width ) ; dbg( "int time= "+(System.currentTimeMillis()-ct)+" ms.\n" ) ; dbg( "xslice="+islice+" sr="+sr+" sz1="+sz1+" sz2="+sz2 ) ; dbg( "lastxstart="+lastxstart+" ncols="+ncols ) ; dbg( "lastystart="+lastystart+" lastyend="+lastyend ) ; } } } }