1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.awt;
  27 
  28 import sun.awt.CustomCursor;
  29 import java.awt.*;
  30 import java.awt.image.*;
  31 import sun.awt.image.ImageRepresentation;
  32 
  33 /**
  34  * A class to encapsulate a custom image-based cursor.
  35  *
  36  * @see Component#setCursor
  37  * @author      Thomas Ball
  38  */
  39 @SuppressWarnings("serial") // JDK-implementation class
  40 public abstract class X11CustomCursor extends CustomCursor {
  41 
  42     public X11CustomCursor(Image cursor, Point hotSpot, String name)
  43             throws IndexOutOfBoundsException {
  44         super(cursor, hotSpot, name);
  45     }
  46 
  47     protected void createNativeCursor(Image im, int[] pixels, int width, int height,
  48                                       int xHotSpot, int yHotSpot) {
  49 
  50         class CCount implements Comparable<CCount> {
  51             int color;
  52             int count;
  53 
  54             public CCount(int cl, int ct) {
  55                 color = cl;
  56                 count = ct;
  57             }
  58 
  59             public int compareTo(CCount cc) {
  60                 return cc.count - count;
  61             }
  62         }
  63 
  64         int tmp[] = new int[pixels.length];
  65         for (int i=0; i<pixels.length; i++) {
  66             if ((pixels[i] & 0xff000000) == 0) {
  67                 tmp[i] = -1;
  68             } else {
  69                 tmp[i] = pixels[i] & 0x00ffffff;
  70             }
  71         }
  72         java.util.Arrays.sort(tmp);
  73 
  74         int fc = 0x000000;
  75         int bc = 0xffffff;
  76         CCount cols[] = new CCount[pixels.length];
  77 
  78         int is = 0;
  79         int numColors = 0;
  80         while ( is < pixels.length ) {
  81             if (tmp[is] != -1) {
  82                 cols[numColors++] = new CCount(tmp[is], 1);
  83                 break;
  84             }
  85             is ++;
  86         }
  87 
  88         for (int i = is+1; i < pixels.length; i++) {
  89             if (tmp[i] != cols[numColors-1].color) {
  90                 cols[numColors++] = new CCount(tmp[i], 1);
  91             } else {
  92                 cols[numColors-1].count ++;
  93             }
  94         }
  95         java.util.Arrays.sort(cols, 0, numColors);
  96 
  97         if (numColors > 0) fc = cols[0].color;
  98         int fcr = (fc >> 16) & 0x000000ff;
  99         int fcg = (fc >>  8) & 0x000000ff;
 100         int fcb = (fc >>  0) & 0x000000ff;
 101 
 102         int rdis = 0;
 103         int gdis = 0;
 104         int bdis = 0;
 105         for (int j = 1; j < numColors; j++) {
 106             int rr = (cols[j].color >> 16) & 0x000000ff;
 107             int gg = (cols[j].color >>  8) & 0x000000ff;
 108             int bb = (cols[j].color >>  0) & 0x000000ff;
 109             rdis = rdis + cols[j].count * rr;
 110             gdis = gdis + cols[j].count * gg;
 111             bdis = bdis + cols[j].count * bb;
 112         }
 113         int rest = pixels.length - ((numColors > 0) ? cols[0].count : 0);
 114     // 4653170 Avoid divide / zero exception
 115     if (rest > 0) {
 116         rdis = rdis / rest - fcr;
 117         gdis = gdis / rest - fcg;
 118         bdis = bdis / rest - fcb;
 119     }
 120         rdis = (rdis*rdis + gdis*gdis + bdis*bdis) / 2;
 121         // System.out.println(" rdis is "+ rdis);
 122 
 123         for (int j = 1; j < numColors; j++) {
 124             int rr = (cols[j].color >> 16) & 0x000000ff;
 125             int gg = (cols[j].color >>  8) & 0x000000ff;
 126             int bb = (cols[j].color >>  0) & 0x000000ff;
 127 
 128             if ( (rr-fcr)*(rr-fcr) + (gg-fcg)*(gg-fcg) + (bb-fcb)*(bb-fcb)
 129                  >= rdis )  {
 130                 bc = cols[j].color;
 131                 break;
 132             }
 133         }
 134         int bcr = (bc >> 16) & 0x000000ff;
 135         int bcg = (bc >>  8) & 0x000000ff;
 136         int bcb = (bc >>  0) & 0x000000ff;
 137 
 138 
 139         // On Solaris 2.5.x, the above code for cursor of any size runs fine
 140         // but on Solaris 2.6, the width of a cursor has to be 8 divisible,
 141         //   otherwise, the cursor could be displayed as garbaged.
 142         // To work around the 2.6 problem, the following code pads any cursor
 143         //   with a transparent area to make a new cursor of width 8 multiples.
 144         // --- Bug 4148455
 145         int wNByte = (width + 7)/8;
 146         int tNByte = wNByte * height;
 147         byte[] xorMask = new byte[tNByte];
 148         byte[] andMask = new byte[tNByte];
 149 
 150         for (int i = 0; i < width; i++) {
 151             int omask = 1 << (i % 8);
 152             for (int j = 0; j < height; j++) {
 153                 int ip = j*width + i;
 154                 int ibyte = j*wNByte + i/8;
 155 
 156                 if ((pixels[ip] & 0xff000000) != 0) {
 157                     andMask[ibyte] |= omask;
 158                 }
 159 
 160                 int pr = (pixels[ip] >> 16) & 0x000000ff;
 161                 int pg = (pixels[ip] >>  8) & 0x000000ff;
 162                 int pb = (pixels[ip] >>  0) & 0x000000ff;
 163                 if ( (pr-fcr)*(pr-fcr) + (pg-fcg)*(pg-fcg) + (pb-fcb)*(pb-fcb)
 164                   <= (pr-bcr)*(pr-bcr) + (pg-bcg)*(pg-bcg) + (pb-bcb)*(pb-bcb) ) {
 165                     // show foreground color
 166                     xorMask[ibyte] |= omask;
 167                 }
 168             }
 169         }
 170 
 171         createCursor(xorMask, andMask, 8*wNByte, height, fc, bc, xHotSpot, yHotSpot);
 172     }
 173 
 174     protected abstract void createCursor(byte[] xorMask, byte[] andMask,
 175                                      int width, int height,
 176                                      int fcolor, int bcolor,
 177                                      int xHotSpot, int yHotSpot);
 178 
 179 }