001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Dmitry A. Durnev
019: * @version $Revision$
020: */package org.apache.harmony.awt.wtk.linux;
021:
022: import java.awt.Color;
023: import java.awt.Dimension;
024: import java.awt.Image;
025: import java.awt.color.ColorSpace;
026: import java.awt.image.BufferedImage;
027: import java.awt.image.DataBufferByte;
028: import java.awt.image.MultiPixelPackedSampleModel;
029: import java.awt.image.WritableRaster;
030:
031: import org.apache.harmony.awt.gl.Utils;
032: import org.apache.harmony.awt.internal.nls.Messages;
033: import org.apache.harmony.awt.nativebridge.linux.X11;
034: import org.apache.harmony.awt.nativebridge.linux.X11Defs;
035: import org.apache.harmony.awt.wtk.CursorFactory;
036: import org.apache.harmony.awt.wtk.NativeCursor;
037: import org.apache.harmony.misc.accessors.AccessorFactory;
038: import org.apache.harmony.misc.accessors.ArrayAccessor;
039: import org.apache.harmony.misc.accessors.LockedArray;
040: import org.apache.harmony.misc.accessors.MemoryAccessor;
041:
042: /**
043: * Implementation of CursorFactory for Linux(X11) platform.
044: */
045: public class LinuxCursorFactory extends CursorFactory implements
046: X11Defs {
047: private static final X11 x11 = X11.getInstance();
048: private static final MemoryAccessor memAccess = AccessorFactory
049: .getMemoryAccessor();
050:
051: private final long display;
052: private final LinuxWindowFactory factory;
053:
054: LinuxCursorFactory(LinuxWindowFactory factory) {
055: this .factory = factory;
056: display = factory.getDisplay();
057: }
058:
059: /**
060: * Java to native type translation table:
061: * native id of symbol in cursorfont(from cursorfont.h),
062: * commented Java cursor type
063: */
064: static final int[] predefined = { XC_top_left_arrow, /*DEFAULT_CURSOR*/
065: XC_cross, /*CROSSHAIR_CURSOR*/
066: XC_xterm, /*TEXT_CURSOR*/
067: XC_watch, /*WAIT_CURSOR*/
068: XC_bottom_left_corner, /*SW_RESIZE_CURSOR*/
069: XC_bottom_right_corner, /*SE_RESIZE_CURSOR*/
070: XC_top_left_corner, /*NW_RESIZE_CURSOR*/
071: XC_top_right_corner, /*NE_RESIZE_CURSOR*/
072: XC_top_side, /*N_RESIZE_CURSOR*/
073: XC_bottom_side, /*S_RESIZE_CURSOR*/
074: XC_left_side, /*W_RESIZE_CURSOR*/
075: XC_right_side, /*E_RESIZE_CURSOR*/
076: XC_hand2, /*HAND_CURSOR*/
077: XC_fleur, /*MOVE_CURSOR*/
078:
079: };
080:
081: /**
082: * @see org.apache.harmony.awt.wtk.CursorFactory#createCursor(int)
083: */
084: public NativeCursor createCursor(int type) {
085: if (type >= 0 && type < predefined.length) {
086: long cursor = x11.XCreateFontCursor(display,
087: predefined[type]);
088: return new LinuxCursor(cursor, display);
089: }
090: return null;
091: }
092:
093: /**
094: * @see org.apache.harmony.awt.wtk.CursorFactory#createCustomCursor(java.awt.Image, int, int)
095: */
096: public NativeCursor createCustomCursor(Image img, int xHotSpot,
097: int yHotSpot) {
098:
099: int width = img.getWidth(null);
100: int height = img.getHeight(null);
101: BufferedImage bufImg = Utils.getBufferedImage(img);
102: if (bufImg == null)
103: throw new NullPointerException("Cursor Image is null");
104:
105: //must convert image into TYPE_BYTE_BINARY format of depth 1
106: BufferedImage bmpSrc = convertTo1Bit(bufImg);
107: BufferedImage bmpMask = getMask(bufImg);
108: //get pixel data from bufImg & create X11 pixmap
109: byte[] bmpSrcData = ((DataBufferByte) bmpSrc.getData()
110: .getDataBuffer()).getData();
111: byte[] rSrcData = convertToLSBFirst(bmpSrcData);
112: byte[] bmpMaskData = ((DataBufferByte) bmpMask.getData()
113: .getDataBuffer()).getData();
114: byte[] rMaskData = convertToLSBFirst(bmpMaskData);
115:
116: ArrayAccessor arrayAccess = AccessorFactory.getArrayAccessor();
117: long wnd = factory.getRootWindow();
118: LockedArray larr = arrayAccess.lockArrayShort(rSrcData);
119: long dataPtr = larr.getAddress();
120: long pixmap = x11.XCreateBitmapFromData(display, wnd, dataPtr,
121: width, height);
122: //System.out.println("source pixmap=" + pixmap);
123: larr.release();
124:
125: larr = arrayAccess.lockArrayShort(rMaskData);
126: dataPtr = larr.getAddress();
127: long pixmapMask = x11.XCreateBitmapFromData(display, wnd,
128: dataPtr, width, height);
129: //System.out.println("mask pixmap=" + pixmap);
130: larr.release();
131: int fgRGB = bufImg.getRGB(0, 0);
132: Color fgColor = new Color(fgRGB);
133: Color bkColor = getBkColor(bufImg, fgColor);
134: X11.XColor bkColorPtr = getXColor(bkColor);
135: X11.XColor fgColorPtr = getXColor(fgColor);
136: //then pass this pixmap to x11.XCreatePixmapCursor()
137: long cursor = x11.XCreatePixmapCursor(display, pixmap,
138: pixmapMask, fgColorPtr, bkColorPtr, xHotSpot, yHotSpot);
139:
140: x11.XFreePixmap(display, pixmap);
141: x11.XFreePixmap(display, pixmapMask);
142:
143: return new LinuxCursor(cursor, display);
144: }
145:
146: // Convert Bitmap bits to LSBFirst
147: private byte[] convertToLSBFirst(byte[] src) {
148: int len = src.length;
149: byte[] dst = new byte[len];
150:
151: for (int i = 0; i < len; i++) {
152: int pix = src[i] & 0xff;
153: int rpix = pix & 0x1;
154: for (int j = 1; j < 8; j++) {
155: pix >>= 1;
156: rpix <<= 1;
157: rpix |= (pix & 0x1);
158: }
159: dst[i] = (byte) rpix;
160: }
161: return dst;
162: }
163:
164: /**
165: * Select one of bufImg pixel colors as background color
166: * when foreground color is fgColor.
167: * @param bufImg
168: * @param fgColor
169: * @return background color
170: */
171: private Color getBkColor(BufferedImage bufImg, Color fgColor) {
172: int w = bufImg.getWidth(), h = bufImg.getHeight();
173: float maxError = 0, error = 0;
174: Color color, bkColor = fgColor;
175: ColorSpace diffSpace = ColorSpace
176: .getInstance(ColorSpace.CS_sRGB);
177: for (int x = 0; x < w; x++) {
178: for (int y = 0; y < h; y++) {
179: int rgb = bufImg.getRGB(x, y);
180: color = new Color(rgb);
181: error = getColorDistance(color, fgColor, diffSpace);
182: if (error > maxError) {
183: maxError = error;
184: bkColor = color;
185: }
186: }
187: }
188: return bkColor;
189: }
190:
191: /**
192: * Returns square of "distance" between 2 colors in
193: * color space cspace
194: * @param color1
195: * @param color2
196: * @param cspace color space where to compare 2 colors
197: * @return
198: */
199: private float getColorDistance(Color color1, Color color2,
200: ColorSpace cspace) {
201: float[] c1 = color1.getRGBColorComponents(/*cspace, */null);
202: float[] c2 = color2.getRGBColorComponents(/*cspace, */null);
203:
204: float sum = 0;
205: for (int i = 0; i < Math.min(c1.length, c2.length); i++) {
206: float diff = c1[i] - c2[i];
207: sum += diff * diff;
208: }
209: return sum;
210: }
211:
212: /**
213: * @param fgRGB
214: * @return
215: */
216: static X11.XColor getXColor(Color color) {
217: X11.XColor xcolor = x11.createXColor(false);
218: //shift to set MSB for 8-bit R,G,B values
219: xcolor.set_green((short) (color.getGreen() << 8));
220: xcolor.set_blue((short) (color.getBlue() << 8));
221: xcolor.set_red((short) (color.getRed() << 8));
222:
223: return xcolor;
224: }
225:
226: /**
227: * Create a mask as 1-bit bitmap from bufImg with alpha.
228: * Mask has the same size as bufImg. Mask pixel is 0 for
229: * completely transparent pixel in bufImg (alpha=0) and 1 otherwise.
230: * @param bufImg
231: * @return new BufferedImage containing mask
232: */
233: static BufferedImage getMask(BufferedImage bufImg) {
234: int w = bufImg.getWidth(), h = bufImg.getHeight();
235: //WritableRaster alphaRaster = bufImg.getAlphaRaster();
236: BufferedImage dstImg = new BufferedImage(w, h,
237: BufferedImage.TYPE_BYTE_BINARY);
238: WritableRaster raster = dstImg.getRaster();
239: //dstImg.setData(alphaRaster);
240: for (int x = 0; x < w; x++) {
241: for (int y = 0; y < h; y++) {
242: int rgb = bufImg.getRGB(x, y);
243: //dstImg.setRGB(x, y, (rgb & 0xFF000000) != 0 ? 0xFFFFFF : 0x0);
244: raster.setPixel(x, y,
245: (rgb & 0xFF000000) != 0 ? new int[] { 0x1 }
246: : new int[] { 0x0 });
247: }
248: }
249: return dstImg;
250: }
251:
252: /**
253: * we have to explicitly free system("predefined") cursors on X11
254: * because actually they're not "system", but just normal cursors
255: */
256: protected void finalize() {
257: //System.out.println("finalizing system cursors on X11!");
258: for (int i = 0; i < systemCursors.length; i++) {
259: LinuxCursor cursor = (LinuxCursor) systemCursors[i];
260: if (cursor != null) {
261: cursor.destroy();
262: }
263: }
264: }
265:
266: /**
267: * @see org.apache.harmony.awt.wtk.CursorFactory#getBestCursorSize(int, int)
268: */
269: public Dimension getBestCursorSize(int prefWidth, int prefHeight) {
270: long rwidthPtr = memAccess.malloc(4);
271: long rheightPtr = memAccess.malloc(4);
272: int status = x11.XQueryBestCursor(display, factory
273: .getRootWindow(), prefWidth, prefHeight, rwidthPtr,
274: rheightPtr);
275: int rwidth = 0, rheight = 0;
276: if (status == 1) {
277: rwidth = memAccess.getInt(rwidthPtr);
278: rheight = memAccess.getInt(rheightPtr);
279: }
280: memAccess.free(rwidthPtr);
281: memAccess.free(rheightPtr);
282: return new Dimension(rwidth, rheight);
283: }
284:
285: /**
286: * @see org.apache.harmony.awt.wtk.CursorFactory#getMaximumCursorColors()
287: */
288: public int getMaximumCursorColors() {
289: // TODO query XServer if RENDER extension is supported
290: // and return (& use for custom cursors) more than 2 colors in this case
291: return 2;
292: }
293:
294: X11.XColor getXColorByName(String strColor) {
295: X11.XColor color = x11.createXColor(false);
296: int status = x11.XParseColor(display, x11.XDefaultColormap(
297: display, factory.getScreen()), strColor, color);
298: if (status == 0) {
299: // awt.13=Cannot allocate color named '{0}'
300: throw new RuntimeException(Messages.getString(
301: "awt.13", strColor)); //$NON-NLS-1$
302: }
303: return color;
304: }
305:
306: /**
307: * Convert buffered image src to 1-bit BYTE_BINARY buffered image.
308: * @param src Image to convert
309: * @return new Buffered image containing 1-bit bitmap
310: */
311: static BufferedImage convertTo1Bit(BufferedImage src) {
312: if (src.getType() == BufferedImage.TYPE_BYTE_BINARY) {
313: MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel) src
314: .getRaster().getSampleModel();
315: if (mppsm.getPixelBitStride() == 1)
316: return src;
317: }
318: int w = src.getWidth(null);
319: int h = src.getHeight(null);
320:
321: BufferedImage dstImg = new BufferedImage(w, h,
322: BufferedImage.TYPE_BYTE_BINARY);
323: dstImg.getGraphics().drawImage(src, 0, 0, null);
324: return dstImg;
325:
326: }
327: }
|