package fab.image.viewer.editors; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URI; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuCreator; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IURIEditorInput; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.EditorPart; public class ImageEditor extends EditorPart implements PaintListener { private static final int[][] WELL_KNOWN_SIZES = { { 854, 480 }, { 864, 480 }, { 320, 240 }, { 640, 480 }, { 720, 38 }, { 720, 1280 }, { 1280, 720 }, { 720, 1280-38 }, { 1920, 1080 }, { 1920, 1088 }, }; private Canvas canvas; private MappedByteBuffer buffer; private ByteOrder endianess = ByteOrder.LITTLE_ENDIAN; private int alphaShift = 24; private int redShift = 16; private int greenShift = 8; private int blueShift = 0; private Image image; private Label description; private int width = 320; private int bytePerPixel = 4; private boolean swap16 = false; private Text hexDump; public ImageEditor() { // TODO Auto-generated constructor stub } @Override public void doSave(IProgressMonitor monitor) { // TODO Auto-generated method stub } @Override public void doSaveAs() { // TODO Auto-generated method stub } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { setSite(site); setInput(input); setPartName(input.getName()); if (input instanceof IURIEditorInput) { IURIEditorInput uriInput = (IURIEditorInput) input; URI uri = uriInput.getURI(); File file = new File(uri); try { RandomAccessFile f = new RandomAccessFile(file, "r"); FileChannel channel = f.getChannel(); buffer = channel.map(MapMode.READ_ONLY, 0, channel.size()); f.close(); } catch (FileNotFoundException e) { throw new PartInitException("File not found", e); } catch (IOException e) { throw new PartInitException("Error reading file", e); } } } private synchronized void createImage() { Display device = getEditorSite().getWorkbenchWindow().getShell().getDisplay(); // int[] knownSize = isWellKnownSize(size, bytePerPixel); // if (knownSize != null) { // width = knownSize[0]; // height = knownSize[1]; // } if (width == 0) width = 720; int size = buffer.limit(); int height = size / bytePerPixel / width; // clamp size to used image data byte[] data; data = new byte[buffer.limit()]; buffer.position(0); // buffer.get(data); Image newImage; if (bytePerPixel == 4) newImage = create32bitImage(device, size, height, data); else newImage = create16bitImage(device, size, height, data); if (image != null) image.dispose(); image = newImage; if (canvas != null && !canvas.isDisposed()) canvas.redraw(); updateDescription(); } private Image create32bitImage(Display device, int size, int height, byte[] data) { buffer.order(endianess); ByteBuffer temp = ByteBuffer.wrap(data); // correct for endianess if necessary // if (!endianess.equals(ByteOrder.nativeOrder())) { // temp.order(ByteOrder.nativeOrder()); // } for (int i = 0; i < size; i+=4) { int v = buffer.getInt(); if (swap16) v = (v >> 16) & (0x0000FFFF) | (v & (0x0000FFFF)) << 16 ; temp.putInt(v); } int redMask = 0x00FF << (adjustForAlpha(redShift, alphaShift)); int greenMask = 0x00FF << (adjustForAlpha(greenShift, alphaShift)); int blueMask = 0x00FF << (adjustForAlpha(blueShift, alphaShift)); PaletteData pd = new PaletteData(redMask, greenMask, blueMask); ImageData id = new ImageData(width, height, 32, pd, width, data); Image newImage = new Image(device, id); return newImage; } private Image create16bitImage(Display device, int size, int height, byte[] data) { // correct for endianess if necessary // if (!endianess.equals(ByteOrder.nativeOrder())) { buffer.order(endianess); ByteBuffer temp = ByteBuffer.wrap(data); temp.order(ByteOrder.nativeOrder()); for (int i = 0; i < size; i+=4) { temp.putShort(buffer.getShort()); } // } int redMask = maskFor16bit(redShift); int greenMask = maskFor16bit(greenShift); int blueMask = maskFor16bit(blueShift); PaletteData pd = new PaletteData(redMask, greenMask, blueMask); ImageData id = new ImageData(width, height, 16, pd, width, data); Image newImage = new Image(device, id); return newImage; } @Override public boolean isDirty() { // TODO Auto-generated method stub return false; } @Override public boolean isSaveAsAllowed() { return false; } private int adjustForAlpha(int cshift, int alpha) { if (cshift < alpha) return cshift; else return cshift + 8; // add alpha channel size } private int maskFor16bit(int cshift) { switch (cshift) { case 0: return 0x1F << 0; case 8: return 0x3F << 5; case 16: return 0x1F << 11; } return 0; } private class RGBAction extends Action { private int rshift; private int gshift; private int bshift; public RGBAction(String name) { super(name); rshift = (2 - name.indexOf('R')) * 8; gshift = (2 - name.indexOf('G')) * 8; bshift = (2 - name.indexOf('B')) * 8; } @Override public void run() { redShift = rshift; greenShift = gshift; blueShift = bshift; createImage(); } } private class AlphaAction extends Action { private int shift; public AlphaAction(String name) { super(name); shift = (3 - name.indexOf("A")) * 8; } @Override public void run() { alphaShift = shift; createImage(); } } private class EndianAction extends Action { ByteOrder byteOrder; public EndianAction(ByteOrder bo) { super(bo.toString()); byteOrder = bo; } @Override public void run() { endianess = byteOrder; createImage(); } } private class SizeAction extends Action { private int w; public SizeAction(int width, int height) { super(Integer.toString(width)); w = width; } @Override public void run() { width = w; createImage(); } } @Override public void createPartControl(Composite parent) { GridLayoutFactory.fillDefaults().numColumns(2).applyTo(parent); description = new Label(parent, SWT.SINGLE); GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(description); ToolBarManager toolBarManager = new ToolBarManager(); Action tAction = new Action("Size", IAction.AS_DROP_DOWN_MENU) { }; tAction.setMenuCreator(new IMenuCreator() { private Menu menu; private MenuManager mm; @Override public Menu getMenu(Menu parent) { return null; } @Override public Menu getMenu(Control parent) { if (mm == null) { mm = new MenuManager(); for (int[] s : WELL_KNOWN_SIZES) mm.add(new SizeAction(s[0], s[1])); menu = mm.createContextMenu(parent); } return menu; } @Override public void dispose() { mm.dispose(); mm = null; menu = null; } }); toolBarManager.add(tAction); toolBarManager.createControl(parent); GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(toolBarManager.getControl()); canvas = new Canvas(parent, SWT.NO_REDRAW_RESIZE | SWT.V_SCROLL); canvas.addPaintListener(this); GridDataFactory.fillDefaults().grab(true, true).applyTo(canvas); canvas.addMouseListener(new MouseListener() { @Override public void mouseUp(MouseEvent e) { } @Override public void mouseDown(MouseEvent e) { if (e.button == 1) { String hd = String.format("X:%d Y:%d\n", e.x, e.y); int index = (e.x + e.y * width) * bytePerPixel; for (int i = 0 ; i < 16 ; i++) { byte v = buffer.get(index + i); hd += String.format("%02x ", v); } hd += "\n"; for (int i = 0 ; i < 4 ; i++) { int v = buffer.getInt(index + i * 4); hd += String.format(" %08x ", v); } hexDump.setText(hd); } } @Override public void mouseDoubleClick(MouseEvent e) { } }); hexDump = new Text(parent, SWT.MULTI); GridDataFactory.fillDefaults().grab(true, true).applyTo(hexDump); MenuManager mm = new MenuManager(); mm.add(new RGBAction("RGB")); mm.add(new RGBAction("RBG")); mm.add(new RGBAction("GBR")); mm.add(new RGBAction("GRB")); mm.add(new RGBAction("BGR")); mm.add(new RGBAction("BRG")); mm.add(new Separator()); mm.add(new AlphaAction("Axxx")); mm.add(new AlphaAction("xxxA")); mm.add(new Separator()); mm.add(new EndianAction(ByteOrder.LITTLE_ENDIAN)); mm.add(new EndianAction(ByteOrder.BIG_ENDIAN)); mm.add(new Action("RGB32") { @Override public void run() { bytePerPixel = 4; createImage(); } }); mm.add(new Action("RGB16") { @Override public void run() { bytePerPixel = 2; createImage(); } }); mm.add(new Action("SWAP16") { @Override public void run() { swap16 = !swap16; createImage(); } }); Menu menu = mm.createContextMenu(canvas); canvas.setMenu(menu); createImage(); } @Override public void setFocus() { canvas.setFocus(); } @Override public void dispose() { if (image != null) image.dispose(); super.dispose(); } @Override public void paintControl(PaintEvent e) { if (image != null && !image.isDisposed()) e.gc.drawImage(image, 0, 0); } private void updateDescription() { char[] d = new char[4]; d[3 - adjustForAlpha(redShift, alphaShift)/8] = 'R'; d[3 - adjustForAlpha(greenShift, alphaShift)/8] = 'G'; d[3 - adjustForAlpha(blueShift, alphaShift)/8] = 'B'; d[3 - alphaShift/8] = 'A'; int redMask = 0x00FF << (adjustForAlpha(redShift, alphaShift)); int greenMask = 0x00FF << (adjustForAlpha(greenShift, alphaShift)); int blueMask = 0x00FF << (adjustForAlpha(blueShift, alphaShift)); int alphaMask = 0x00FF << alphaShift; if (description != null && !description.isDisposed()) { String string = "Format: " + new String(d); string += " w:" + width; string += " e:" + endianess; string += " bpp:" + bytePerPixel; string += String.format("redMask:%08x ", redMask); string += String.format("greenMask:%08x ", greenMask); string += String.format("blueMask:%08x ", blueMask); string += String.format("alphaMask:%08x ", alphaMask); string += String.format(" swap16:%b", swap16); description.setText(string); } } private int[] isWellKnownSize(int size, int bytePerPixel) { for (int i = 0; i < WELL_KNOWN_SIZES.length ; i++) { int[] dims = WELL_KNOWN_SIZES[i]; if (dims[0] * dims[1] * bytePerPixel == size) return dims; } return null; } }