JDK-6443357 : corrupted toplevel icon on scaled image
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: generic
  • Submitted: 2006-06-26
  • Updated: 2011-01-19
  • Resolved: 2006-08-04
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b95Fixed
Related Reports
Relates :  
Relates :  
Description
Setting a scaled image as an icon for frame leads to wrong painting of icon that appears on ALT+TAB and of icon in the task bar. First one contains two vertical lines, second one vertical line in the center.
See you should use image with size different from 32x32 pixels.
Run test with image say 30x30 and press ALT+TAB. Also note vert. line in the icon from the task bar.
This seen only on Windows platform.
Pretty well noticeable on Windows2000. Less visible on WindowsXP.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.Iterator;

public class TestScale extends Frame{
//    public static Frame frame;
    private Button buttonSimple;
    public static Image image; // = frame.getIconImage();
    public static java.util.List<Image> images;
    static String fileBase;
    static String fileName;

    public static void main (String s[]){
        if (System.getProperty("test.src") == null){
            fileBase = "";
        } else {
            fileBase = System.getProperty("test.src") + System.getProperty("file.separator");
        }

        fileName = "images"+System.getProperty("file.separator") + "cross.jpg";

        Frame frame = new TestScale();

        frame.setSize(500, 500);
        frame.setVisible(true);
    }

    public void paint(Graphics g){
       g.setColor(Color.black);
       g.fillRect(0, 0, this.getWidth(), this.getHeight());
       Image image;
       Image scaledImage;
       try {
           image = Toolkit.getDefaultToolkit().getImage(fileName);
//           if ( != null){
               System.out.println("setting to frame");
               this.setIconImage(image);
//           }
           g.drawImage(image, 100, 100, null);
           System.out.println("Loaded image.");
           java.util.List imageList = new java.util.ArrayList();
           imageList.add(image);
           scaledImage = getScaledIconImage(imageList, 100, 100);
           g.drawImage(scaledImage, 300, 300, null);


           scaledImage = getScaledIconImage(imageList, 16, 16);
           g.drawImage(scaledImage, 400, 400, null);

           scaledImage = getScaledIconImage(imageList, 32, 32);
           g.drawImage(scaledImage, 450, 400, null);

           scaledImage = getScaledIconImage(imageList, 48, 48);
           g.drawImage(scaledImage, 450, 450, null);

       } catch (Exception e) {
           e.printStackTrace();
       }
    }

    public static BufferedImage getScaledIconImage(java.util.List<Image> imageList, int width, int height) {
        if (width == 0 || height == 0) {
            return null;
        }
        Image bestImage = null;
        int bestWidth = 0;
        int bestHeight = 0;
        double bestSimilarity = 3; //Impossibly high value
        double bestScaleFactor = 0;
        for (Iterator<Image> i = imageList.iterator();i.hasNext();) {
            Image im = i.next();
            if (im == null) {
                continue;
            }
            int iw;
            int ih;

            try {
                iw = im.getWidth(null);
                ih = im.getHeight(null);
            } catch (Exception e){
                continue;
            }
            
            if (iw > 0 && ih > 0) {
                //Calc scale factor
                double scaleFactor = Math.min((double)width / (double)iw, 
                                              (double)height / (double)ih);
                //Calculate scaled image dimensions 
                //adjusting scale factor to nearest "good" value
                int adjw = 0;
                int adjh = 0;
                double scaleMeasure = 1; //0 - best (no) scale, 1 - impossibly bad
                if (scaleFactor >= 2) {
                    //Need to enlarge image more than twice
                    //Round down scale factor to multiply by integer value
                    scaleFactor = Math.floor(scaleFactor);
                    adjw = iw * (int)scaleFactor;
                    adjh = ih * (int)scaleFactor;
                    scaleMeasure = 1.0 - 0.5 / scaleFactor;
                } else if (scaleFactor >= 1) {
                    //Don't scale
                    scaleFactor = 1.0;
                    adjw = iw;
                    adjh = ih;
                    scaleMeasure = 0;
                } else if (scaleFactor >= 0.75) {
                    //Multiply by 3/4
                    scaleFactor = 0.75;
                    adjw = iw * 3 / 4;
                    adjh = ih * 3 / 4;
                    scaleMeasure = 0.3;
                } else if (scaleFactor >= 0.6666) {
                    //Multiply by 2/3
                    scaleFactor = 0.6666;
                    adjw = iw * 2 / 3;
                    adjh = ih * 2 / 3;
                    scaleMeasure = 0.33;
                } else {
                    //Multiply size by 1/scaleDivider
                    //where scaleDivider is minimum possible integer
                    //larger than 1/scaleFactor
                    double scaleDivider = Math.ceil(1.0 / scaleFactor);
                    scaleFactor = 1.0 / scaleDivider;
                    adjw = (int)Math.round((double)iw / scaleDivider);
                    adjh = (int)Math.round((double)ih / scaleDivider);
                    scaleMeasure = 1.0 - 1.0 / scaleDivider;
                }
                double similarity = ((double)width - (double)adjw) / (double)width + 
                    ((double)height - (double)adjh) / (double)height + //Large padding is bad
                    scaleMeasure; //Large rescale is bad
                if (similarity < bestSimilarity) {
                    bestSimilarity = similarity;
                    bestScaleFactor = scaleFactor;
                    bestImage = im;
                    bestWidth = adjw;
                    bestHeight = adjh;
                }
                if (similarity == 0) break;
            }
        }
        if (bestImage == null) {
            //No images were found, possibly all are broken
            return null;
        }
        BufferedImage bimage =
            new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = bimage.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        try {
            int x = (width - bestWidth) / 2; 
            int y = (height - bestHeight) / 2;
            g.drawImage(bestImage, x, y, bestWidth, bestHeight, null);
        } finally {
            g.dispose();
        }
        return bimage;
    }

}

Comments
SUGGESTED FIX ------- awt_BitmapUtil.h ------- 2c2 < * @(#)awt_Component.h 1.197 05/11/30 --- > * %W% %E% 14c14,19 < * Creates B&W Bitmap from specified ARGB input data --- > * Creates B&W Bitmap with transparency mask from specified ARGB input data > * 0 for opaque pixels, 1 for transparent. > * MSDN article for ICONINFO says that 'for color icons, this mask only > * defines the AND bitmask of the icon'. That's wrong! If mask bit for > * specific pixel is 0, the pixel is drawn opaque, otherwise it's XORed > * with background. 16c21 < static HBITMAP CreateMaskFromARGB(int width, int height, int* imageData); --- > static HBITMAP CreateTransparencyMaskFromARGB(int width, int height, int* imageData); 19c24,26 < * Creates V4 Bitmap (Win95-compatible) from specified ARGB input data --- > * Creates 32-bit ARGB V4 Bitmap (Win95-compatible) from specified ARGB input data > * The color for transparent pixels (those with 0 alpha) is reset to 0 (BLACK) > * to prevent errors on systems prior to XP. ------- awt_BitmapUtil.cpp ------- 2c2 < * @(#)awt_Component.cpp 1.387 05/11/30 --- > * %W% %E% 15c15 < HBITMAP BitmapUtil::CreateMaskFromARGB(int width, int height, int* imageData) --- > HBITMAP BitmapUtil::CreateTransparencyMaskFromARGB(int width, int height, int* imageData) 18c18 < int bufLength = ((width + 31) / 32) * height;//buf length (words) --- > int bufLength = ((width + 15) / 16 * 2) * height;//buf length (bytes) 20,21c20,21 < int* buf = new int[bufLength]; < int* bufPos = buf; --- > char* buf = new char[bufLength]; > char* bufPos = buf; 23c23 < int cbit = 1; --- > int cbit = 0x80; 27,29c27,29 < //cbit is shifted left for every pixel < //& is required to treat possible 64-bit issues < if ((cbit & 0xFFFFFFFF) == 0) { --- > //cbit is shifted right for every pixel > //next byte is stored when cbit is zero > if ((cbit & 0xFF) == 0x00) { 33c33 < cbit = 1; --- > cbit = 0x80; 35c35,36 < if (!(*srcPos & 0xFF000000)) { --- > unsigned char alpha = (*srcPos >> 0x18) & 0xFF; > if (alpha == 0x00) { 38c39 < cbit <<= 1; --- > cbit >>= 1; 45c46,51 < cbit = 1; --- > cbit = 0x80; > //add word-padding byte if necessary > if (((bufPos - buf) & 0x01) == 0x01) { > *bufPos = 0; > bufPos++; > } 47a54 > delete[] buf; 99,103c106,114 < dest[3] = (*src >> 0x18) & 0xFF; < dest[2] = (*src >> 0x10) & 0xFF; < dest[1] = (*src >> 0x08) & 0xFF; < dest[0] = *src & 0xFF; < --- > unsigned char alpha = (*src >> 0x18) & 0xFF; > if (alpha == 0) { > dest[3] = dest[2] = dest[1] = dest[0] = 0; > } else { > dest[3] = alpha; > dest[2] = (*src >> 0x10) & 0xFF; > dest[1] = (*src >> 0x08) & 0xFF; > dest[0] = *src & 0xFF; > } ------- awt_Window.cpp ------- 1778c1778 < mask = BitmapUtil::CreateMaskFromARGB(w, h, iconRasterBuffer); --- > mask = BitmapUtil::CreateTransparencyMaskFromARGB(w, h, iconRasterBuffer);
19-07-2006

EVALUATION The regression is caused by fix for 6339074. The function that generates transparency mask for icon works incorrectly sometimes. The bug is not reproducible on WinXP because it uses alpha-channel instead of transparency mask.
29-06-2006