свинг нимб ShadowEffect - PullRequest
       27

свинг нимб ShadowEffect

2 голосов
/ 31 августа 2011

Я использую Nimbus в своих приложениях для свинга, и это довольно круто.Я заметил, что это «чистый» внешний вид: он «обшивает» компоненты, но не добавляет новых графических элементов.Интересно, можно ли использовать какую-то часть технологии рендеринга, например, класс:

javax.swing.plaf.nimbus.ShadowEffect

доступно для добавления теней к другим элементам, таким как JLabel, у которых по умолчанию нет тени?

1 Ответ

2 голосов
/ 07 сентября 2011

Я получил IllegalAccess, поэтому я скопировал класс, если мне это нужно, в небольшом приложении.
Я знаю, что это нехорошо, но ShadowEffect нельзя использовать повторно для любого другого класса.: (

Пример:

public class Example extends JFrame {
    private JSlider slider = new JSlider(JSlider.VERTICAL,0,20,2);
    private JTextField txt = new JTextField();
    private BufferedImage raw = null;
    private Image src = null;
    private Rectangle rect = new Rectangle(400,200);

    public Example() {
        slider.addChangeListener( new ChangeListener() {
            @Override
            public void stateChanged( ChangeEvent e ) {
                repaint();
            }
        });

        JButton btn = new JButton(new AbstractAction("load") {
            @Override
            public void actionPerformed( ActionEvent ev ) {
                try {
                    src = new ImageIcon( new URL( txt.getText() ) ).getImage();
                    rect = new Rectangle( src.getWidth( null ), src.getHeight( null ) );
                    raw = new BufferedImage( rect.width+20, rect.height+20, BufferedImage.TYPE_INT_ARGB );
                    raw.getGraphics().drawImage( src, 0, 0, null );
                    pack();
                } catch( Exception e ) {
                    e.printStackTrace();
                }
            }
        });
        JPanel ori = new JPanel(new BorderLayout());
        ori.add( txt, BorderLayout.CENTER );
        txt.setText( "http://upload.wikimedia.org/wikipedia/en/d/d5/Transparent_google_logo.png" );
        ori.add( btn, BorderLayout.EAST);
        JPanel pnl = new JPanel() {
            @Override
            protected void paintComponent( Graphics g ) {
                if(raw != null && src != null){
                    BufferedImage dst = new BufferedImage( rect.width+20, rect.height+20, BufferedImage.TYPE_INT_ARGB );
                    new MyEffect().applyEffect( raw, dst, rect.width+20, rect.height+20 );
                    g.drawImage( dst, slider.getValue(), slider.getValue(), null );
                    g.drawImage( src, 0, 0, null );
                }
            }
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(rect.width+5,rect.height+5);
            }
        };

        setDefaultCloseOperation( EXIT_ON_CLOSE );
        getContentPane().setLayout( new BorderLayout() );
        getContentPane().setBackground( Color.WHITE );
        getContentPane().add( ori, BorderLayout.NORTH );
        getContentPane().add( pnl, BorderLayout.CENTER );
        getContentPane().add( slider, BorderLayout.EAST );
        pack();
    }

    public static void main( String[] args ) {
        try {
            for( UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels() ) {
                if( "Nimbus".equals( info.getName() ) ) {
                    UIManager.setLookAndFeel( info.getClassName() );
                    break;
                }
            }
        } catch( Exception system ) {
            system.printStackTrace();
        }
        EventQueue.invokeLater( new Runnable() {
            @Override
            public void run() {
                new Example().setVisible( true );
            }
        } );
    }

    enum EffectType {
        UNDER, BLENDED, OVER
    }

    public class MyEffect {

        protected Color color    = Color.BLACK;
        /** Opacity a float 0-1 for percentage */
        protected float opacity  = 0.75f;
        /** Angle in degrees between 0-360 */
        protected int   angle    = 135;
        /** Distance in pixels */
        protected int   distance = 5;
        /** The shadow spread between 0-100 % */
        protected int   spread   = 0;
        /** Size in pixels */
        protected int   size     = 5;

        protected ArrayCache getArrayCache() {
            ArrayCache cache = (ArrayCache)AppContext.getAppContext().get( ArrayCache.class );
            if( cache == null ) {
                cache = new ArrayCache();
                AppContext.getAppContext().put( ArrayCache.class, cache );
            }
            return cache;
        }

        protected class ArrayCache {
            private SoftReference<int[]>  tmpIntArray   = null;
            private SoftReference<byte[]> tmpByteArray1 = null;
            private SoftReference<byte[]> tmpByteArray2 = null;
            private SoftReference<byte[]> tmpByteArray3 = null;

            protected int[] getTmpIntArray( int size ) {
                int[] tmp;
                if( tmpIntArray == null || (tmp = tmpIntArray.get()) == null || tmp.length < size ) {
                    // create new array
                    tmp = new int[size];
                    tmpIntArray = new SoftReference<int[]>( tmp );
                }
                return tmp;
            }

            protected byte[] getTmpByteArray1( int size ) {
                byte[] tmp;
                if( tmpByteArray1 == null || (tmp = tmpByteArray1.get()) == null || tmp.length < size ) {
                    // create new array
                    tmp = new byte[size];
                    tmpByteArray1 = new SoftReference<byte[]>( tmp );
                }
                return tmp;
            }

            protected byte[] getTmpByteArray2( int size ) {
                byte[] tmp;
                if( tmpByteArray2 == null || (tmp = tmpByteArray2.get()) == null || tmp.length < size ) {
                    // create new array
                    tmp = new byte[size];
                    tmpByteArray2 = new SoftReference<byte[]>( tmp );
                }
                return tmp;
            }

            protected byte[] getTmpByteArray3( int size ) {
                byte[] tmp;
                if( tmpByteArray3 == null || (tmp = tmpByteArray3.get()) == null || tmp.length < size ) {
                    // create new array
                    tmp = new byte[size];
                    tmpByteArray3 = new SoftReference<byte[]>( tmp );
                }
                return tmp;
            }
        }

        Color getColor() {
            return color;
        }

        void setColor( Color color ) {
            Color old = getColor();
            this.color = color;
        }

        float getOpacity() {
            return opacity;
        }

        void setOpacity( float opacity ) {
            float old = getOpacity();
            this.opacity = opacity;
        }

        int getAngle() {
            return angle;
        }

        void setAngle( int angle ) {
            int old = getAngle();
            this.angle = angle;
        }

        int getDistance() {
            return distance;
        }

        void setDistance( int distance ) {
            int old = getDistance();
            this.distance = distance;
        }

        int getSpread() {
            return spread;
        }

        void setSpread( int spread ) {
            int old = getSpread();
            this.spread = spread;
        }

        int getSize() {
            return size;
        }

        void setSize( int size ) {
            int old = getSize();
            this.size = size;
        }

        EffectType getEffectType() {
            return EffectType.UNDER;
        }

        BufferedImage applyEffect( BufferedImage src, BufferedImage dst, int w, int h ) {
            if( src == null || src.getType() != BufferedImage.TYPE_INT_ARGB ) {
                throw new IllegalArgumentException( "Effect only works with "
                    + "source images of type BufferedImage.TYPE_INT_ARGB." );
            }
            if( dst != null && dst.getType() != BufferedImage.TYPE_INT_ARGB ) {
                throw new IllegalArgumentException( "Effect only works with "
                    + "destination images of type BufferedImage.TYPE_INT_ARGB." );
            }
            // calculate offset
            double trangleAngle = Math.toRadians( angle - 90 );
            int offsetX = (int)(Math.sin( trangleAngle ) * distance);
            int offsetY = (int)(Math.cos( trangleAngle ) * distance);
            // clac expanded size
            int tmpOffX = offsetX + size;
            int tmpOffY = offsetX + size;
            int tmpW = w + offsetX + size + size;
            int tmpH = h + offsetX + size;
            // create tmp buffers
            int[] lineBuf = getArrayCache().getTmpIntArray( w );
            byte[] tmpBuf1 = getArrayCache().getTmpByteArray1( tmpW * tmpH );
            Arrays.fill( tmpBuf1, (byte)0x00 );
            byte[] tmpBuf2 = getArrayCache().getTmpByteArray2( tmpW * tmpH );
            // extract src image alpha channel and inverse and offset
            Raster srcRaster = src.getRaster();
            for( int y = 0; y < h; y++ ) {
                int dy = (y + tmpOffY);
                int offset = dy * tmpW;
                srcRaster.getDataElements( 0, y, w, 1, lineBuf );
                for( int x = 0; x < w; x++ ) {
                    int dx = x + tmpOffX;
                    tmpBuf1[offset + dx] = (byte)((lineBuf[x] & 0xFF000000) >>> 24);
                }
            }
            // blur
            float[] kernel = createGaussianKernel( size );
            blur( tmpBuf1, tmpBuf2, tmpW, tmpH, kernel, size ); // horizontal pass
            blur( tmpBuf2, tmpBuf1, tmpH, tmpW, kernel, size );// vertical pass
            //rescale
            float spread = Math.min( 1 / (1 - (0.01f * this.spread)), 255 );
            for( int i = 0; i < tmpBuf1.length; i++ ) {
                int val = (int)((tmpBuf1[i] & 0xFF) * spread);
                tmpBuf1[i] = (val > 255) ? (byte)0xFF : (byte)val;
            }
            // create color image with shadow color and greyscale image as alpha
            if( dst == null ) {
                dst = new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB );
            }
            WritableRaster shadowRaster = dst.getRaster();
            int red = color.getRed(), green = color.getGreen(), blue = color.getBlue();
            for( int y = 0; y < h; y++ ) {
                int srcY = y + tmpOffY;
                int shadowOffset = (srcY - offsetY) * tmpW;
                for( int x = 0; x < w; x++ ) {
                    int srcX = x + tmpOffX;
                    lineBuf[x] = tmpBuf1[shadowOffset + (srcX - offsetX)] << 24 | red << 16 | green << 8 | blue;
                }
                shadowRaster.setDataElements( 0, y, w, 1, lineBuf );
            }
            return dst;
        }
    }

    static float[] createGaussianKernel( int radius ) {
        if( radius < 1 ) {
            throw new IllegalArgumentException( "Radius must be >= 1" );
        }

        float[] data = new float[radius * 2 + 1];

        float sigma = radius / 3.0f;
        float twoSigmaSquare = 2.0f * sigma * sigma;
        float sigmaRoot = (float)Math.sqrt( twoSigmaSquare * Math.PI );
        float total = 0.0f;

        for( int i = -radius; i <= radius; i++ ) {
            float distance = i * i;
            int index = i + radius;
            data[index] = (float)Math.exp( -distance / twoSigmaSquare ) / sigmaRoot;
            total += data[index];
        }

        for( int i = 0; i < data.length; i++ ) {
            data[i] /= total;
        }

        return data;
    }

    static void blur( byte[] srcPixels, byte[] dstPixels, int width, int height, float[] kernel, int radius ) {
        float p;
        int cp;
        for( int y = 0; y < height; y++ ) {
            int index = y;
            int offset = y * width;
            for( int x = 0; x < width; x++ ) {
                p = 0.0f;
                for( int i = -radius; i <= radius; i++ ) {
                    int subOffset = x + i;
                    //                 if (subOffset < 0) subOffset = 0;
                    //                 if (subOffset >= width) subOffset = width-1;
                    if( subOffset < 0 || subOffset >= width ) {
                        subOffset = (x + width) % width;
                    }
                    int pixel = srcPixels[offset + subOffset] & 0xFF;
                    float blurFactor = kernel[radius + i];
                    p += blurFactor * pixel;
                }
                cp = (int)(p + 0.5f);
                dstPixels[index] = (byte)(cp > 255 ? 255 : cp);
                index += height;
            }
        }
    }
}
...