package com.informagen;
import java.awt.Image;
import java.awt.image.ImageProducer;
import java.awt.image.MemoryImageSource;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.Toolkit;
import com.apple.mrj.MRJOSType;
import com.apple.mrj.jdirect.GenericHandle;
import com.apple.mrj.jdirect.HandleStruct;
import com.apple.mrj.jdirect.ByteArrayStruct;
import com.apple.mrj.jdirect.PointerStruct;
import com.apple.mrj.jdirect.Struct;
// Package containing the LOCK object
import com.apple.mrj.macos.toolbox.Toolbox;
import com.apple.mrj.macos.libraries.InterfaceLib;
public class MacClipboard {
public static boolean isMacintosh() {
return System.getProperty("os.name").equalsIgnoreCase("Mac OS");
}
public static boolean isTEXT() {
return isType("TEXT");
}
public static boolean isPICT() {
return isType("PICT");
}
public static boolean isType(String inType){
MRJOSType resType = new MRJOSType(inType);
return isType(resType);
}
public static boolean isType(MRJOSType resType){
int jDirectHdl = MemoryFunctions.NewHandle(0);
int[] dontCare = new int[1];
int scrapReturn = ScrapFunctions.GetScrap(jDirectHdl, resType.toInt(), dontCare);
MemoryFunctions.DisposeHandle(jDirectHdl);
return scrapReturn != ErrorConstants.noTypeErr;
}
// The current MRJ virtual machine supports this type of scrap, ie
//
// clipboard = getToolkit().getSystemClipboard();
// Transferable contents = clipboard.getContents(this);
// String string = (String) contents.getTransferData(DataFlavor.stringFlavor);
//
// However, this will work as well and is included for completeness
public static String getText() {
// Must be scrap type of 'TEXT'
MRJOSType TEXTtype = new MRJOSType("TEXT");
short osErr;
int jDirectHdl = MemoryFunctions.NewHandle(0);
int[] dontCare = new int[1];
int scrapReturn = ScrapFunctions.GetScrap(jDirectHdl, TEXTtype.toInt(), dontCare);
String returnString = null;
if ( scrapReturn != ErrorConstants.noTypeErr ) {
int size = MemoryFunctions.GetHandleSize(jDirectHdl);
GenericHandle scrapHdl = new GenericHandle(jDirectHdl);
byte[] bytes = scrapHdl.getBytesAt(0, size);
returnString = new String(bytes);
}
MemoryFunctions.DisposeHandle(jDirectHdl);
return returnString;
}
public static Image getImage() {
// These variables set within the synchronized block but are
// referenced outside of it
int pixelSize = 0;
int pixelType = 0;
int cmpSize = 0;
int colorTable = 0;
short rowBytes = 0;
byte[] pixels = null;
short osErr;
int jDirectHdl = MemoryFunctions.NewHandle(0);
int[] dontCare = new int[1];
// Test for PICT scrap type
int scrapReturn = ScrapFunctions.GetScrap(jDirectHdl, new MRJOSType("PICT").toInt(), dontCare);
if ( scrapReturn == ErrorConstants.noTypeErr ) {
MemoryFunctions.DisposeHandle(jDirectHdl);
return null;
}
// OK, we have a 'PICT' from clipboard, turn it into a "java.awt.Image" //////////////////////
// Get and lock the scrap handle in order to obtain the PICT bounding rectangle
GenericHandle hDest = new GenericHandle(jDirectHdl);
MemoryFunctions.HLock(jDirectHdl);
short height = (short)(hDest.getShortAt(6) - hDest.getShortAt(2));
short width = (short)(hDest.getShortAt(8) - hDest.getShortAt(4));
MemoryFunctions.HUnlock(jDirectHdl);
// Don't let JVM AWT peers at the Mac Toolbox while we are working.
synchronized( Toolbox.LOCK ) {
// Save the current GWorld, which is the Monitor's Desktop
// JDirect uses single element arrays used to retrieve Toolbox values
int [] refPort = new int[1];
int [] refGDhdl = new int[1];
QDOffscreenFunctions.GetGWorld(refPort, refGDhdl);
// Dereference the first value of each array
int currPort = refPort[0];
GDeviceStruct gds = new GDeviceStruct(refGDhdl[0]);
// Create an offscreen GWorld in which to render this PICT scrap
// Use a single element array to get the reference to the offscreenGWorld
int[] refInt = new int[1];
short PixelDepth = 0;
RectStruct boundsRect = new RectStruct();
boundsRect.setTop((short)0);
boundsRect.setLeft((short)0);
boundsRect.setBottom(height);
boundsRect.setRight(width);
osErr = QDOffscreenFunctions.NewGWorld(refInt, PixelDepth, boundsRect, null, null, 0);
int offscreenGWorld = refInt[0];
// Make the offscreen GWorld the one in which to do the drawing
// Create a PictureStruct from the 'PICT' scrap handle, pass it
// to DrawPicture to be rendered.
QDOffscreenFunctions.SetGWorld(offscreenGWorld, gds);
PictureStruct aPict = new PictureStruct(jDirectHdl);
QuickdrawFunctions.DrawPicture(aPict, boundsRect);
// Now that we have the 'PICT' drawn for us, we will convert it's pixels into
// an array of Java ints in order to make an Image.
PixMapStruct pm = new PixMapStruct(QDOffscreenFunctions.GetGWorldPixMap(offscreenGWorld));
int packType = pm.getPackType();
rowBytes = (short)(pm.getRowBytes() & 0x3fff);
pixelType = pm.getPixelType();
pixelSize = pm.getPixelSize();
colorTable = pm.getPmTable();
cmpSize = pm.getCmpSize();
int baseAddr = QDOffscreenFunctions.GetPixBaseAddr(pm);
QDOffscreenFunctions.LockPixels(pm);
ImageStruct pix = new ImageStruct(baseAddr, rowBytes, height);
pixels = pix.getBytes();
QDOffscreenFunctions.UnlockPixels(pm);
// Restore the GWorld to the main screen.
QDOffscreenFunctions.SetGWorld(currPort, gds);
// Dispose of structures and handles
//QuickdrawFunctions.DisposePixMap(pm);
QDOffscreenFunctions.DisposeGWorld(offscreenGWorld);
MemoryFunctions.DisposeHandle(jDirectHdl);
}
// Using the raw pixel data obtained from the Toolbox create an ImageProducer
// and then an image.
ImageProducer pixelSource = null;
if ( pixelType == QuickdrawConstants.RGBDirect )
pixelSource = fromRGBDirect(pixelSize, cmpSize, height, width, rowBytes, pixels);
else if ( pixelType == QuickdrawConstants.clutType )
pixelSource = fromCLUT(colorTable, cmpSize, height, width, rowBytes, pixels);
// Create and return the java.awt.Image
if (pixelSource != null)
return Toolkit.getDefaultToolkit().createImage(pixelSource);
else
return null;
}
private static ImageProducer fromRGBDirect(int pixelSize, int cmpSize, short height, short width, int rowBytes, byte[] pixels) {
int i = 0;
int ii = 0;
int row = 0;
int bytesPerPixel = pixelSize/8;
int[] imagePixels = new int[(rowBytes/bytesPerPixel) * height];
while ( row++ < height ) {
for (int j=0; j<rowBytes; j=j+bytesPerPixel) {
// Build the Quickdraw pixel from raw bytes
int pixel = 0;
for (int jj=0; jj<bytesPerPixel; jj++) {
pixel <<= 8;
pixel += ((int)pixels[i++]) & 0xFF;
}
// Extract out the r,g,b values
int mask = 1;
mask <<= cmpSize;
mask--;
int b = pixel & mask;
pixel >>= cmpSize;
int g = pixel & mask;
pixel >>= cmpSize;
int r = pixel & mask;
r <<= 8 - cmpSize;
g <<= 8 - cmpSize;
b <<= 8 - cmpSize;
// Repack the r,g,b values into an java.awt.Image int array
imagePixels[ii++] = 0xFF <<24 | r << 16 | g << 8 | b;
}
}
return new MemoryImageSource(width, height, imagePixels, 0, rowBytes/bytesPerPixel);
}
//
// As of MRJ 2.1.2 "DirectColorModel" did not work properly for all color depths
//
private static ImageProducer fromRGBDirect2(int pixelSize, int cmpSize, short height, short width, int rowBytes, byte[] pixels) {
int bytesPerPixel = pixelSize/8;
// Create a mask cmpSize bits wide
int mask = 1;
mask <<= cmpSize;
mask--;
int redMask = mask << (cmpSize * 2);
int greenMask = mask << cmpSize;
int blueMask = mask;
ColorModel cm = new DirectColorModel(pixelSize, redMask, greenMask, blueMask);
int i = 0;
int ii = 0;
int row = 0;
int[] imagePixels = new int[(rowBytes/bytesPerPixel) * height];
while ( row++ < height ) {
for (int j=0; j<rowBytes; j=j+bytesPerPixel) {
// Build the Quickdraw pixel from raw bytes
int pixel = 0;
for (int jj=0; jj<bytesPerPixel; jj++) {
pixel <<= 8;
pixel |= ((int)pixels[i++]) & 0xFF;
}
// Copy this value into an java.awt.Image int array
imagePixels[ii++] = pixel;
}
}
return new MemoryImageSource(width, height, cm, imagePixels, 0, rowBytes/bytesPerPixel);
}
private static ImageProducer fromCLUT(int colorTable, int cmpSize, short height, short width, int rowBytes, byte[] pixels) {
ColorTableStruct cTab = new ColorTableStruct(colorTable);
int ctSize = cTab.getCtSize();
RGBColorStruct rgbColor = new RGBColorStruct();
// Build an indexed color model
byte[] reds = new byte[ctSize];
byte[] greens = new byte[ctSize];
byte[] blues = new byte[ctSize];
for (int index=0; index<ctSize; index++) {
QuickdrawFunctions.Index2Color(index, rgbColor);
reds[index] = (byte)(((int)rgbColor.getRed()) & 0xFF);
greens[index] = (byte)(((int)rgbColor.getGreen()) & 0xFF);
blues[index] = (byte)(((int)rgbColor.getBlue()) & 0xFF);
}
ColorModel cm = new IndexColorModel(cmpSize, ctSize, reds, greens, blues);
// Move the PICT index values (bytes) into an int array
int i = 0;
int ii = 0;
int row = 0;
int[] imagePixels = new int[rowBytes * height];
while ( row++ < height )
for (int j=0; j<rowBytes; j++)
imagePixels[ii++] = ((int)pixels[i++]) & 0xFF;
return new MemoryImageSource(width, height, cm, imagePixels, 0, rowBytes);
}
}
// Could not find a similar structure wrapper in the JDirect sample code.
class ImageStruct extends PointerStruct {
short rowBytes;
short numRows;
public ImageStruct(int inPtr, short inRowBytes, short inNumRows) {
super(inPtr);
rowBytes = inRowBytes;
numRows = inNumRows;
}
public int getSize() { return rowBytes * numRows; }
}
// Macintosh Toolbox JDirect ///////////////////////////////////////////////////
//
// Copy from MRJ JDirect Sample Code files only what is needed.
//
// NB: During Development include everything by using the files, Quickdraw.java,
// QDOffscreen.java, MacTypes.java, and PictUtils.java. Although these files
// could be left in the final build they add over 80K to the application. By
// extracting out only those structures and methods needed to support this class
// this space overhead can be minimized. By using package names this technique
// can be used in other places without name conflicts.
//
class ScrapFunctions implements InterfaceLib {
private ScrapFunctions() {}
public native static int GetScrap(int hDest, int theType, int [] offset);
}
interface ErrorConstants {
public final short noErr = (short)0; // No error
public final short noTypeErr = (short)-102; // No object of that type in scrap
}
class MemoryFunctions implements InterfaceLib {
private MemoryFunctions() {};
public native static int GetHandleSize(int h);
public native static int NewHandle(int h);
public native static void HLock(int h);
public native static void HUnlock(int h);
public native static void DisposeHandle(int h);
}
class QDOffscreenFunctions implements InterfaceLib {
private QDOffscreenFunctions() {};
public static short NewGWorld(int [] offscreenGWorld, short PixelDepth, RectStruct boundsRect, ColorTableStruct cTable, GDeviceStruct aGDevice, int flags) {
return NewGWorld(offscreenGWorld, PixelDepth, boundsRect.getByteArray(), (cTable != null) ? cTable.getHandle() : 0, (aGDevice != null) ? aGDevice.getHandle() : 0, flags);
}
public native static short NewGWorld(int [] offscreenGWorld, short PixelDepth, byte[] boundsRect, int cTable, int aGDevice, int flags);
public static boolean LockPixels(PixMapStruct pm) { return LockPixels(pm.getHandle()); }
public native static boolean LockPixels(int pm);
public static void UnlockPixels(PixMapStruct pm) { UnlockPixels(pm.getHandle()); }
public native static void UnlockPixels(int pm);
public native static void GetGWorld(int [] port, int [] gdh);
public static void SetGWorld(int port, GDeviceStruct gdh) { SetGWorld(port, gdh.getHandle()); }
public native static void SetGWorld(int port, int gdh);
public native static int GetGWorldPixMap(int offscreenGWorld);
public static int GetPixBaseAddr(PixMapStruct pm) { return GetPixBaseAddr(pm.getHandle()); }
public native static int GetPixBaseAddr(int pm);
public native static void DisposeGWorld(int offscreenGWorld);
}
public interface QuickdrawConstants {
public final int RGBDirect = 16;
public final int clutType = 0;
}
class QuickdrawFunctions implements InterfaceLib {
private QuickdrawFunctions() {};
public static void DrawPicture(PictureStruct myPicture, RectStruct dstRect) {
DrawPicture(myPicture.getHandle(), dstRect.getByteArray());
}
public native static void DrawPicture(int myPicture, byte[] dstRect);
public static void Index2Color(int index, RGBColorStruct aColor) {
Index2Color(index, aColor.getByteArray());
}
public native static void Index2Color(int index, byte[] aColor);
}
// From MacType.java ////////////////////////////////////////////////////////////////////////
class RectStruct extends ByteArrayStruct {
public final static int sizeOfRect = 8;
public RectStruct() { super(sizeOfRect); }
public final void setTop(short top) { setShortAt(0, top); }
public final void setLeft(short left) { setShortAt(2, left); }
public final void setBottom(short bottom) { setShortAt(4, bottom); }
public final void setRight(short right) { setShortAt(6, right); }
}
// From QuickDraw.java ////////////////////////////////////////////////////////////////////////
class ColorTableStruct extends HandleStruct {
public final static int sizeOfColorTable = 16;
public ColorTableStruct(int handle) { super(handle); }
public final short getCtSize() { return getShortAt(6); }
public int getSize() { return sizeOfColorTable; }
}
class PixMapStruct extends HandleStruct {
public final static int sizeOfPixMap = 50;
public PixMapStruct(int handle) { super(handle); }
public final short getRowBytes() { return getShortAt(4); }
public final short getPixelType() { return getShortAt(30); }
public final int getPmTable() { return getIntAt(42);}
public final short getPackType() { return getShortAt(16); }
public final short getCmpCount() { return getShortAt(34); }
public final short getCmpSize() { return getShortAt(36); }
public final short getPixelSize() { return getShortAt(32); }
public int getSize() { return sizeOfPixMap; }
}
class GDeviceStruct extends HandleStruct {
public GDeviceStruct(int handle) { super(handle); }
public final static int sizeOfGDevice = 62;
public int getSize() { return sizeOfGDevice; }
}
class PictureStruct extends HandleStruct {
public final static int sizeOfPicture = 10;
public PictureStruct(int handle) {super(handle); }
public int getSize() { return sizeOfPicture; }
}
public class RGBColorStruct extends ByteArrayStruct {
public RGBColorStruct() {
super(sizeOfRGBColor);
}
public final short getRed() { return getShortAt(0); }
public final short getGreen() { return getShortAt(2); }
public final short getBlue() { return getShortAt(4); }
public final static int sizeOfRGBColor = 6;
}