How to improve the performance of g.drawImage() method for resizing images

后端 未结 12 1468
面向向阳花
面向向阳花 2020-11-27 11:16

I have an application where users are able to upload pictures in albums but naturally the uploaded images need to be resized so there are also thumbs available and the shown

相关标签:
12条回答
  • 2020-11-27 11:38

    The main point of the question was about the performance of scaling images in Java. The other answers, showed different approaches, without evaluating them further. I was curious about this as well, so I tried to write a small performance test. However, testing the image scaling performance reliably, sensibly and objectively is difficult. There are far too many infuencing factors to be taken into account:

    • The size of the input image
    • The size of the output image
    • The interpolation (i.e. "quality": Nearest neighbor, bilinear, bicubic)
    • The BufferedImage.TYPE_* of the input image
    • The BufferedImage.TYPE_* of the output image
    • The JVM version and operating system
    • Finally: The method that is actually used for performing the operation.

    I tried to cover those that I considered the most important ones. The setup was:

    • The input is a simple, "average" photo (particularly, this "Image Of The Day" from Wikipedia, with a size of 2560x1706 pixels)

    • The main interpolation types are tested - namely, by using RenderingHints where the INTERPOLATION key was set to the values NEAREST_NEIGHBOR, BILINEAR and BICUBIC

    • The input image was converted to different types:

      • BufferedImage.TYPE_INT_RGB: A type that is commonly used, as it "usually" shows the best performance characteristics

      • BufferedImage.TYPE_3BTE_BGR: This is the type that it is read with by default, when just reading it with ImageIO

    • The target image size was varied between a width of 10000 (thus, scaling the image up), and 100 (thus, scaling the image down to thumbnail size)

    The tests have been run on a Win64/AMD K10 with 3.7 GHz and JDK 1.8u31, with -Xmx4000m -server.

    The tested methods are:

    • Using an AffineTransformOp, as in the answer by Jörn Horstmann
    • Using a Graphics, as in the answer by johnstosh
    • Using Image#getScaledInstance

    The code of the tests is shown here:

    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.MediaTracker;
    import java.awt.RenderingHints;
    import java.awt.geom.AffineTransform;
    import java.awt.image.AffineTransformOp;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Locale;
    import java.util.function.Supplier;
    
    import javax.imageio.ImageIO;
    import javax.swing.JLabel;
    
    public class ImageScalingPerformance
    {
        private static int blackHole = 0;
    
        public static void main(String[] args) throws IOException
        {
            // Image with size 2560 x 1706, from https://upload.wikimedia.org/
            //   wikipedia/commons/4/41/Pitta_moluccensis_-_Kaeng_Krachan.jpg
            BufferedImage image = ImageIO.read(
                new File("Pitta_moluccensis_-_Kaeng_Krachan.jpg"));
    
            int types[] =
            {
                BufferedImage.TYPE_3BYTE_BGR,
                BufferedImage.TYPE_INT_RGB,
            };
            Object interpolationValues[] =
            {
                RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC,
            };
            int widths[] =
            {
                10000, 5000, 2500, 1000, 500, 100
            };
    
    
            System.out.printf("%10s%22s%6s%18s%10s\n",
                "Image type", "Interpolation", "Size", "Method", "Duration (ms)");
    
            for (int type : types)
            {
                BufferedImage currentImage = convert(image, type);
                for (Object interpolationValue : interpolationValues)
                {
                    for (int width : widths)
                    {
                        List<Supplier<Image>> tests = 
                            createTests(currentImage, interpolationValue, width);
    
                        for (Supplier<Image> test : tests)
                        {
                            double durationMs = computeMs(test);
    
                            System.out.printf("%10s%22s%6s%18s%10s\n",
                                stringForBufferedImageType(type),
                                stringForInterpolationValue(interpolationValue),
                                String.valueOf(width), 
                                String.valueOf(test),
                                String.format(Locale.ENGLISH, "%6.3f", durationMs));
                        }
                    }
                }
            }
            System.out.println(blackHole);
        }
    
        private static List<Supplier<Image>> createTests(
            BufferedImage image, Object interpolationValue, int width)
        {
            RenderingHints renderingHints = new RenderingHints(null);
            renderingHints.put(
                RenderingHints.KEY_INTERPOLATION, 
                interpolationValue);
            double scale = (double) width / image.getWidth();
            int height = (int)(scale * image.getHeight());
    
            Supplier<Image> s0 = new Supplier<Image>()
            {
                @Override
                public BufferedImage get()
                {
                    return scaleWithAffineTransformOp(
                        image, width, height, renderingHints);
                }
    
                @Override
                public String toString()
                {
                    return "AffineTransformOp";
                }
            };
    
            Supplier<Image> s1 = new Supplier<Image>()
            {
                @Override
                public Image get()
                {
                    return scaleWithGraphics(
                        image, width, height, renderingHints);
                }
    
                @Override
                public String toString()
                {
                    return "Graphics";
                }
            };
    
            Supplier<Image> s2 = new Supplier<Image>()
            {
                @Override
                public Image get()
                {
                    return scaleWithGetScaledInstance(
                        image, width, height, renderingHints);
                }
    
                @Override
                public String toString()
                {
                    return "GetScaledInstance";
                }
            };
    
            List<Supplier<Image>> tests = new ArrayList<Supplier<Image>>();
            tests.add(s0);
            tests.add(s1);
            tests.add(s2);
            return tests;
        }
    
        private static double computeMs(Supplier<Image> supplier)
        {
            int runs = 5;
            long before = System.nanoTime();
            for (int i=0; i<runs; i++)
            {
                Image image0 = supplier.get();
                blackHole += image0.hashCode();
            }
            long after = System.nanoTime();
            double durationMs = (after-before) / 1e6 / runs;
            return durationMs;
        }
    
        private static BufferedImage convert(BufferedImage image, int type)
        {
            BufferedImage newImage = new BufferedImage(
                image.getWidth(), image.getHeight(), type);
            Graphics2D g = newImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();
            return newImage;
        }        
    
        private static BufferedImage scaleWithAffineTransformOp(
            BufferedImage image, int w, int h,
            RenderingHints renderingHints)
        {
            BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
            double scaleX = (double) w / image.getWidth();
            double scaleY = (double) h / image.getHeight();
            AffineTransform affineTransform = 
                AffineTransform.getScaleInstance(scaleX, scaleY);
            AffineTransformOp affineTransformOp = new AffineTransformOp(
                affineTransform, renderingHints);
            return affineTransformOp.filter(
                image, scaledImage);
        }
    
        private static BufferedImage scaleWithGraphics(
            BufferedImage image, int w, int h,
            RenderingHints renderingHints) 
        {
            BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
            Graphics2D g = scaledImage.createGraphics();
            g.setRenderingHints(renderingHints);
            g.drawImage(image, 0, 0, w, h, null);
            g.dispose();
            return scaledImage;
        }
    
        private static Image scaleWithGetScaledInstance(
            BufferedImage image, int w, int h,
            RenderingHints renderingHints)
        {
            int hint = Image.SCALE_REPLICATE;
            if (renderingHints.get(RenderingHints.KEY_ALPHA_INTERPOLATION) != 
                RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
            {
                hint = Image.SCALE_AREA_AVERAGING;
            }
            Image scaledImage = image.getScaledInstance(w, h, hint);
            MediaTracker mediaTracker = new MediaTracker(new JLabel());
            mediaTracker.addImage(scaledImage, 0);
            try
            {
                mediaTracker.waitForAll();
            }
            catch (InterruptedException e)
            {
                Thread.currentThread().interrupt();
            }
            return scaledImage;
        }
    
        private static String stringForBufferedImageType(int type)
        {
            switch (type)
            {
                case BufferedImage.TYPE_INT_RGB : return "INT_RGB";
                case BufferedImage.TYPE_INT_ARGB : return "INT_ARGB";
                case BufferedImage.TYPE_INT_ARGB_PRE : return "INT_ARGB_PRE";
                case BufferedImage.TYPE_INT_BGR : return "INT_BGR";
                case BufferedImage.TYPE_3BYTE_BGR : return "3BYTE_BGR";
                case BufferedImage.TYPE_4BYTE_ABGR : return "4BYTE_ABGR";
                case BufferedImage.TYPE_4BYTE_ABGR_PRE : return "4BYTE_ABGR_PRE";
                case BufferedImage.TYPE_USHORT_565_RGB : return "USHORT_565_RGB";
                case BufferedImage.TYPE_USHORT_555_RGB : return "USHORT_555_RGB";
                case BufferedImage.TYPE_BYTE_GRAY : return "BYTE_GRAY";
                case BufferedImage.TYPE_USHORT_GRAY : return "USHORT_GRAY";
                case BufferedImage.TYPE_BYTE_BINARY : return "BYTE_BINARY";
                case BufferedImage.TYPE_BYTE_INDEXED : return "BYTE_INDEXED";
            }
            return "CUSTOM";
        }
    
        private static String stringForInterpolationValue(Object value)
        {
            if (value == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
            {
                return "NEAREST/REPLICATE";
            }
            if (value == RenderingHints.VALUE_INTERPOLATION_BILINEAR)
            {
                return "BILINEAR/AREA_AVG";
            }
            if (value == RenderingHints.VALUE_INTERPOLATION_BICUBIC)
            {
                return "BICUBIC/AREA_AVG";
            }
            return "(unknown)";
        }
    
    
    }
    

    First, regarding getScaledInstance: As Chris Campbell has pointed out in his (famous) article about The Perils of Image.getScaledInstance() (which was already linked to in other answers), the Image#getScaledInstance method is somewhat broken, and has a distressingly bad performance for most configurations. Additionally, it has the disadvantage of not having such a fine-grained control about the interpolation type. This should be taken into account in the following performance comparison: The quality of the resulting images may differ, which is not considered here. For example, the "area averaging" method of getScaledInstance does not yield a good image quality when the image size is increased.

    (The most severe drawback of Image#getScaledInstance is IMHO that it only delivers an Image, and not a BufferedImage, but if the image is only supposed to be painted into a Graphics, this may not be important)

    I'll just dump the output of the program here for reference, some details will follow below:

    Image type         Interpolation  Size            MethodDuration (ms)
     3BYTE_BGR     NEAREST/REPLICATE 10000 AffineTransformOp   197.287
     3BYTE_BGR     NEAREST/REPLICATE 10000          Graphics   184.427
     3BYTE_BGR     NEAREST/REPLICATE 10000 GetScaledInstance  1869.759
     3BYTE_BGR     NEAREST/REPLICATE  5000 AffineTransformOp    38.354
     3BYTE_BGR     NEAREST/REPLICATE  5000          Graphics    40.220
     3BYTE_BGR     NEAREST/REPLICATE  5000 GetScaledInstance  1088.448
     3BYTE_BGR     NEAREST/REPLICATE  2500 AffineTransformOp    10.153
     3BYTE_BGR     NEAREST/REPLICATE  2500          Graphics     9.461
     3BYTE_BGR     NEAREST/REPLICATE  2500 GetScaledInstance   613.030
     3BYTE_BGR     NEAREST/REPLICATE  1000 AffineTransformOp     2.137
     3BYTE_BGR     NEAREST/REPLICATE  1000          Graphics     1.956
     3BYTE_BGR     NEAREST/REPLICATE  1000 GetScaledInstance   464.989
     3BYTE_BGR     NEAREST/REPLICATE   500 AffineTransformOp     0.861
     3BYTE_BGR     NEAREST/REPLICATE   500          Graphics     0.750
     3BYTE_BGR     NEAREST/REPLICATE   500 GetScaledInstance   407.751
     3BYTE_BGR     NEAREST/REPLICATE   100 AffineTransformOp     0.206
     3BYTE_BGR     NEAREST/REPLICATE   100          Graphics     0.153
     3BYTE_BGR     NEAREST/REPLICATE   100 GetScaledInstance   385.863
     3BYTE_BGR     BILINEAR/AREA_AVG 10000 AffineTransformOp   830.097
     3BYTE_BGR     BILINEAR/AREA_AVG 10000          Graphics  1501.290
     3BYTE_BGR     BILINEAR/AREA_AVG 10000 GetScaledInstance  1627.934
     3BYTE_BGR     BILINEAR/AREA_AVG  5000 AffineTransformOp   207.816
     3BYTE_BGR     BILINEAR/AREA_AVG  5000          Graphics   376.789
     3BYTE_BGR     BILINEAR/AREA_AVG  5000 GetScaledInstance  1063.942
     3BYTE_BGR     BILINEAR/AREA_AVG  2500 AffineTransformOp    52.362
     3BYTE_BGR     BILINEAR/AREA_AVG  2500          Graphics    95.041
     3BYTE_BGR     BILINEAR/AREA_AVG  2500 GetScaledInstance   612.660
     3BYTE_BGR     BILINEAR/AREA_AVG  1000 AffineTransformOp     9.121
     3BYTE_BGR     BILINEAR/AREA_AVG  1000          Graphics    15.749
     3BYTE_BGR     BILINEAR/AREA_AVG  1000 GetScaledInstance   452.578
     3BYTE_BGR     BILINEAR/AREA_AVG   500 AffineTransformOp     2.593
     3BYTE_BGR     BILINEAR/AREA_AVG   500          Graphics     4.237
     3BYTE_BGR     BILINEAR/AREA_AVG   500 GetScaledInstance   407.661
     3BYTE_BGR     BILINEAR/AREA_AVG   100 AffineTransformOp     0.275
     3BYTE_BGR     BILINEAR/AREA_AVG   100          Graphics     0.297
     3BYTE_BGR     BILINEAR/AREA_AVG   100 GetScaledInstance   381.835
     3BYTE_BGR      BICUBIC/AREA_AVG 10000 AffineTransformOp  3015.943
     3BYTE_BGR      BICUBIC/AREA_AVG 10000          Graphics  5431.703
     3BYTE_BGR      BICUBIC/AREA_AVG 10000 GetScaledInstance  1654.424
     3BYTE_BGR      BICUBIC/AREA_AVG  5000 AffineTransformOp   756.136
     3BYTE_BGR      BICUBIC/AREA_AVG  5000          Graphics  1359.288
     3BYTE_BGR      BICUBIC/AREA_AVG  5000 GetScaledInstance  1063.467
     3BYTE_BGR      BICUBIC/AREA_AVG  2500 AffineTransformOp   189.953
     3BYTE_BGR      BICUBIC/AREA_AVG  2500          Graphics   341.039
     3BYTE_BGR      BICUBIC/AREA_AVG  2500 GetScaledInstance   615.807
     3BYTE_BGR      BICUBIC/AREA_AVG  1000 AffineTransformOp    31.351
     3BYTE_BGR      BICUBIC/AREA_AVG  1000          Graphics    55.914
     3BYTE_BGR      BICUBIC/AREA_AVG  1000 GetScaledInstance   451.808
     3BYTE_BGR      BICUBIC/AREA_AVG   500 AffineTransformOp     8.422
     3BYTE_BGR      BICUBIC/AREA_AVG   500          Graphics    15.028
     3BYTE_BGR      BICUBIC/AREA_AVG   500 GetScaledInstance   408.626
     3BYTE_BGR      BICUBIC/AREA_AVG   100 AffineTransformOp     0.703
     3BYTE_BGR      BICUBIC/AREA_AVG   100          Graphics     0.825
     3BYTE_BGR      BICUBIC/AREA_AVG   100 GetScaledInstance   382.610
       INT_RGB     NEAREST/REPLICATE 10000 AffineTransformOp   330.445
       INT_RGB     NEAREST/REPLICATE 10000          Graphics   114.656
       INT_RGB     NEAREST/REPLICATE 10000 GetScaledInstance  2784.542
       INT_RGB     NEAREST/REPLICATE  5000 AffineTransformOp    83.081
       INT_RGB     NEAREST/REPLICATE  5000          Graphics    29.148
       INT_RGB     NEAREST/REPLICATE  5000 GetScaledInstance  1117.136
       INT_RGB     NEAREST/REPLICATE  2500 AffineTransformOp    22.296
       INT_RGB     NEAREST/REPLICATE  2500          Graphics     7.735
       INT_RGB     NEAREST/REPLICATE  2500 GetScaledInstance   436.779
       INT_RGB     NEAREST/REPLICATE  1000 AffineTransformOp     3.859
       INT_RGB     NEAREST/REPLICATE  1000          Graphics     2.542
       INT_RGB     NEAREST/REPLICATE  1000 GetScaledInstance   205.863
       INT_RGB     NEAREST/REPLICATE   500 AffineTransformOp     1.413
       INT_RGB     NEAREST/REPLICATE   500          Graphics     0.963
       INT_RGB     NEAREST/REPLICATE   500 GetScaledInstance   156.537
       INT_RGB     NEAREST/REPLICATE   100 AffineTransformOp     0.160
       INT_RGB     NEAREST/REPLICATE   100          Graphics     0.074
       INT_RGB     NEAREST/REPLICATE   100 GetScaledInstance   126.159
       INT_RGB     BILINEAR/AREA_AVG 10000 AffineTransformOp  1019.438
       INT_RGB     BILINEAR/AREA_AVG 10000          Graphics  1230.621
       INT_RGB     BILINEAR/AREA_AVG 10000 GetScaledInstance  2721.918
       INT_RGB     BILINEAR/AREA_AVG  5000 AffineTransformOp   254.616
       INT_RGB     BILINEAR/AREA_AVG  5000          Graphics   308.374
       INT_RGB     BILINEAR/AREA_AVG  5000 GetScaledInstance  1269.898
       INT_RGB     BILINEAR/AREA_AVG  2500 AffineTransformOp    68.137
       INT_RGB     BILINEAR/AREA_AVG  2500          Graphics    80.163
       INT_RGB     BILINEAR/AREA_AVG  2500 GetScaledInstance   444.968
       INT_RGB     BILINEAR/AREA_AVG  1000 AffineTransformOp    13.093
       INT_RGB     BILINEAR/AREA_AVG  1000          Graphics    15.396
       INT_RGB     BILINEAR/AREA_AVG  1000 GetScaledInstance   211.929
       INT_RGB     BILINEAR/AREA_AVG   500 AffineTransformOp     3.238
       INT_RGB     BILINEAR/AREA_AVG   500          Graphics     3.689
       INT_RGB     BILINEAR/AREA_AVG   500 GetScaledInstance   159.688
       INT_RGB     BILINEAR/AREA_AVG   100 AffineTransformOp     0.329
       INT_RGB     BILINEAR/AREA_AVG   100          Graphics     0.277
       INT_RGB     BILINEAR/AREA_AVG   100 GetScaledInstance   127.905
       INT_RGB      BICUBIC/AREA_AVG 10000 AffineTransformOp  4211.287
       INT_RGB      BICUBIC/AREA_AVG 10000          Graphics  4712.587
       INT_RGB      BICUBIC/AREA_AVG 10000 GetScaledInstance  2830.749
       INT_RGB      BICUBIC/AREA_AVG  5000 AffineTransformOp  1069.088
       INT_RGB      BICUBIC/AREA_AVG  5000          Graphics  1182.285
       INT_RGB      BICUBIC/AREA_AVG  5000 GetScaledInstance  1155.663
       INT_RGB      BICUBIC/AREA_AVG  2500 AffineTransformOp   263.003
       INT_RGB      BICUBIC/AREA_AVG  2500          Graphics   297.663
       INT_RGB      BICUBIC/AREA_AVG  2500 GetScaledInstance   444.497
       INT_RGB      BICUBIC/AREA_AVG  1000 AffineTransformOp    42.841
       INT_RGB      BICUBIC/AREA_AVG  1000          Graphics    48.605
       INT_RGB      BICUBIC/AREA_AVG  1000 GetScaledInstance   209.261
       INT_RGB      BICUBIC/AREA_AVG   500 AffineTransformOp    11.004
       INT_RGB      BICUBIC/AREA_AVG   500          Graphics    12.407
       INT_RGB      BICUBIC/AREA_AVG   500 GetScaledInstance   156.794
       INT_RGB      BICUBIC/AREA_AVG   100 AffineTransformOp     0.817
       INT_RGB      BICUBIC/AREA_AVG   100          Graphics     0.790
       INT_RGB      BICUBIC/AREA_AVG   100 GetScaledInstance   128.700
    

    It can be seen that for almost all cases, getScaledInstance performs poorly compared to the other approaches (and the few cases where it seems to perform better can be explained by the lower quality when scaling up).

    The AffineTransformOp based approach seems to perform best on average, with the only notable exception being a NEAREST_NEIGHBOR scaling of TYPE_INT_RGB images, where the Graphics-based approach seems to be consistently faster.

    The bottom line is: The method using AffineTransformOp, as in the answer by Jörn Horstmann, seems to be the one that offers the best performance for most application cases.

    0 讨论(0)
  • 2020-11-27 11:38

    Old question but in case someone else hits this issue: I profiled your code and your biggest bottleneck is the call to:

    Image.getScaledInstance()
    

    That call is well known to be horribly slow. Please be convinced by reading this document:

    The Perils of Image.getScaledInstance()

    The simplest/best solution for a dramatic performance improvement would be to replace that call. You could use the method from dpineda's answer (see his answer/code above):

    private BufferedImage getScaledImage(BufferedImage src, int w, int h){
    

    I tested his method and it works really well. In my testing, his implementation (which avoids the slow Image.getScaledInstance()) shaved 80% of the processing time!

    0 讨论(0)
  • 2020-11-27 11:46

    If you want something fast, you're probably better with some native code, if you can give up on portability.

    But if you want a pure Java solution, you can try some other solutions as well, like Graphics2D.scale and Image.getScaledInstance. I've used them in the past, but can't remember which had better performance or better looking results, sorry.

    Try them out, and see which one best fits your needs.

    0 讨论(0)
  • 2020-11-27 11:48

    Some improvement in performance (perhaps small, perhaps negligible, perhaps at the expense of quality) can be attained by tweaking the rendering hints. E.g.

        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
                   RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    
    0 讨论(0)
  • 2020-11-27 11:49

    Well, Jacob and I wanted to resize an Image, not a BufferedImage. So we ended up with this code:

    /**
     * we want the x and o to be resized when the JFrame is resized
     *
     * @param originalImage an x or an o. Use cross or oh fields.
     *
     * @param biggerWidth
     * @param biggerHeight
     */
    private Image resizeToBig(Image originalImage, int biggerWidth, int biggerHeight) {
        int type = BufferedImage.TYPE_INT_ARGB;
    
    
        BufferedImage resizedImage = new BufferedImage(biggerWidth, biggerHeight, type);
        Graphics2D g = resizedImage.createGraphics();
    
        g.setComposite(AlphaComposite.Src);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
        g.drawImage(originalImage, 0, 0, biggerWidth, biggerHeight, this);
        g.dispose();
    
    
        return resizedImage;
    }
    
    0 讨论(0)
  • 2020-11-27 11:49

    this works for me:

    private BufferedImage getScaledImage(BufferedImage src, int w, int h){
        int original_width = src.getWidth();
        int original_height = src.getHeight();
        int bound_width = w;
        int bound_height = h;
        int new_width = original_width;
        int new_height = original_height;
    
        // first check if we need to scale width
        if (original_width > bound_width) {
            //scale width to fit
            new_width = bound_width;
            //scale height to maintain aspect ratio
            new_height = (new_width * original_height) / original_width;
        }
    
        // then check if we need to scale even with the new height
        if (new_height > bound_height) {
            //scale height to fit instead
            new_height = bound_height;
            //scale width to maintain aspect ratio
            new_width = (new_height * original_width) / original_height;
        }
    
        BufferedImage resizedImg = new BufferedImage(new_width, new_height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = resizedImg.createGraphics();
        g2.setBackground(Color.WHITE);
        g2.clearRect(0,0,new_width, new_height);
        g2.drawImage(src, 0, 0, new_width, new_height, null);
        g2.dispose();
        return resizedImg;
    }
    

    also i added white background for png

    0 讨论(0)
提交回复
热议问题