PixelPerfectLoupe.java revision f526dc3bb658b07c5a96d0372b17ffa2fbbe6079
1bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev/* 2bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * Copyright (C) 2010 The Android Open Source Project 3bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * 4bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * Licensed under the Apache License, Version 2.0 (the "License"); 5bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * you may not use this file except in compliance with the License. 6bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * You may obtain a copy of the License at 7bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * 8bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * http://www.apache.org/licenses/LICENSE-2.0 9bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * 10bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * Unless required by applicable law or agreed to in writing, software 11bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * distributed under the License is distributed on an "AS IS" BASIS, 12bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * See the License for the specific language governing permissions and 14bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * limitations under the License. 15bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev */ 16bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 1770a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrevpackage com.android.hierarchyviewerlib.ui; 18bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 19bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport com.android.hierarchyviewerlib.models.PixelPerfectModel; 20901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrevimport com.android.hierarchyviewerlib.models.PixelPerfectModel.IImageChangeListener; 21bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 22bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.SWT; 23e361a961cbe1ead74659b4a761453b312d026e55Konstantin Lopyrevimport org.eclipse.swt.events.DisposeEvent; 24e361a961cbe1ead74659b4a761453b312d026e55Konstantin Lopyrevimport org.eclipse.swt.events.DisposeListener; 2597b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrevimport org.eclipse.swt.events.KeyEvent; 2697b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrevimport org.eclipse.swt.events.KeyListener; 27bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.events.MouseEvent; 28bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.events.MouseListener; 29c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrevimport org.eclipse.swt.events.MouseWheelListener; 30bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.events.PaintEvent; 31bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.events.PaintListener; 32bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.Color; 33bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.GC; 34bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.Image; 35bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.ImageData; 36bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.PaletteData; 3770a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrevimport org.eclipse.swt.graphics.Point; 38bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.RGB; 39bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.Rectangle; 40bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.Transform; 41bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.widgets.Canvas; 42bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.widgets.Composite; 43bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.widgets.Display; 44bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 45901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrevpublic class PixelPerfectLoupe extends Canvas implements IImageChangeListener { 46901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private PixelPerfectModel mModel; 47bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 48901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private Image mImage; 49bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 50901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private Image mGrid; 51bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 52901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private Color mCrosshairColor; 53bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 54901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private int mWidth; 55bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 56901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private int mHeight; 57bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 58901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private Point mCrosshairLocation; 59bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 60901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private int mZoom; 61bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 62901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private Transform mTransform; 63bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 64901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private int mCanvasWidth; 65bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 66901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private int mCanvasHeight; 67bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 68901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private Image mOverlayImage; 6970a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev 70901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private double mOverlayTransparency; 7170a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev 72901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private boolean mShowOverlay = false; 7370a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev 74bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public PixelPerfectLoupe(Composite parent) { 75c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev super(parent, SWT.NONE); 76901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mModel = PixelPerfectModel.getModel(); 77901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mModel.addImageChangeListener(this); 78bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 79901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev addPaintListener(mPaintListener); 80901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev addMouseListener(mMouseListener); 81901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev addMouseWheelListener(mMouseWheelListener); 82901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev addDisposeListener(mDisposeListener); 83901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev addKeyListener(mKeyListener); 84bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 85901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairColor = new Color(Display.getDefault(), new RGB(255, 94, 254)); 86bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 87901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mTransform = new Transform(Display.getDefault()); 8899bd6912e7b5b97fc6d4bb787e76b2d9dfffd7aeKonstantin Lopyrev 8999bd6912e7b5b97fc6d4bb787e76b2d9dfffd7aeKonstantin Lopyrev imageLoaded(); 90bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 91bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 9270a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev public void setShowOverlay(boolean value) { 9370a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (this) { 94901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mShowOverlay = value; 9570a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 9697b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev doRedraw(); 9770a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 9870a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev 99901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private DisposeListener mDisposeListener = new DisposeListener() { 100f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 101e361a961cbe1ead74659b4a761453b312d026e55Konstantin Lopyrev public void widgetDisposed(DisposeEvent e) { 102901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mModel.removeImageChangeListener(PixelPerfectLoupe.this); 103901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairColor.dispose(); 104901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mTransform.dispose(); 105901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mGrid != null) { 106901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mGrid.dispose(); 107e361a961cbe1ead74659b4a761453b312d026e55Konstantin Lopyrev } 108bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 109e361a961cbe1ead74659b4a761453b312d026e55Konstantin Lopyrev }; 110bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 111901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private MouseListener mMouseListener = new MouseListener() { 112bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 113f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 114bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void mouseDoubleClick(MouseEvent e) { 115bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev // pass 116bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 117bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 118f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 119bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void mouseDown(MouseEvent e) { 120c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev handleMouseEvent(e); 121bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 122bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 123f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 124bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void mouseUp(MouseEvent e) { 125c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev // 126bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 127bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 128bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev }; 129bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 130901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private MouseWheelListener mMouseWheelListener = new MouseWheelListener() { 131f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 132c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev public void mouseScrolled(MouseEvent e) { 133c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev int newZoom = -1; 13470a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (PixelPerfectLoupe.this) { 135901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mImage != null && mCrosshairLocation != null) { 136c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev if (e.count > 0) { 137901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev newZoom = mZoom + 1; 138c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } else { 139901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev newZoom = mZoom - 1; 140c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 141c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 142c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 143c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev if (newZoom != -1) { 144901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mModel.setZoom(newZoom); 145c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 146c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 147c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev }; 148c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev 149bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev private void handleMouseEvent(MouseEvent e) { 150bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev int newX = -1; 151bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev int newY = -1; 15270a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (PixelPerfectLoupe.this) { 153901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mImage == null) { 154bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev return; 155bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 156901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev int zoomedX = -mCrosshairLocation.x * mZoom - mZoom / 2 + getBounds().width / 2; 157901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev int zoomedY = -mCrosshairLocation.y * mZoom - mZoom / 2 + getBounds().height / 2; 158901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev int x = (e.x - zoomedX) / mZoom; 159901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev int y = (e.y - zoomedY) / mZoom; 160901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (x >= 0 && x < mWidth && y >= 0 && y < mHeight) { 161bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev newX = x; 162bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev newY = y; 163bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 164bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 165bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev if (newX != -1) { 166901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mModel.setCrosshairLocation(newX, newY); 167bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 168bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 169bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 170901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private KeyListener mKeyListener = new KeyListener() { 17197b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev 172f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 17397b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev public void keyPressed(KeyEvent e) { 17497b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev boolean crosshairMoved = false; 17597b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev synchronized (PixelPerfectLoupe.this) { 176901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mImage != null) { 17797b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev switch (e.keyCode) { 17897b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev case SWT.ARROW_UP: 179901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mCrosshairLocation.y != 0) { 180901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairLocation.y--; 18197b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev crosshairMoved = true; 18297b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 18397b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev break; 18497b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev case SWT.ARROW_DOWN: 185901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mCrosshairLocation.y != mHeight - 1) { 186901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairLocation.y++; 18797b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev crosshairMoved = true; 18897b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 18997b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev break; 19097b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev case SWT.ARROW_LEFT: 191901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mCrosshairLocation.x != 0) { 192901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairLocation.x--; 19397b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev crosshairMoved = true; 19497b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 19597b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev break; 19697b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev case SWT.ARROW_RIGHT: 197901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mCrosshairLocation.x != mWidth - 1) { 198901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairLocation.x++; 19997b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev crosshairMoved = true; 20097b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 20197b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev break; 20297b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 20397b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 20497b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 20597b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev if (crosshairMoved) { 206901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mModel.setCrosshairLocation(mCrosshairLocation.x, mCrosshairLocation.y); 20797b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 20897b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 20997b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev 210f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 21197b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev public void keyReleased(KeyEvent e) { 21297b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev // pass 21397b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev } 21497b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev 21597b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev }; 21697b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev 217901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev private PaintListener mPaintListener = new PaintListener() { 218f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 219bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void paintControl(PaintEvent e) { 22070a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (PixelPerfectLoupe.this) { 221bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); 222c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev e.gc.fillRectangle(0, 0, getSize().x, getSize().y); 223901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mImage != null && mCrosshairLocation != null) { 224901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev int zoomedX = -mCrosshairLocation.x * mZoom - mZoom / 2 + getBounds().width / 2; 225901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev int zoomedY = -mCrosshairLocation.y * mZoom - mZoom / 2 + getBounds().height / 2; 226901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mTransform.translate(zoomedX, zoomedY); 227901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mTransform.scale(mZoom, mZoom); 228bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev e.gc.setInterpolation(SWT.NONE); 229901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.setTransform(mTransform); 230901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.drawImage(mImage, 0, 0); 231901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mShowOverlay && mOverlayImage != null) { 232901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.setAlpha((int) (mOverlayTransparency * 255)); 233901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.drawImage(mOverlayImage, 0, mHeight - mOverlayImage.getBounds().height); 23470a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev e.gc.setAlpha(255); 23570a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 23670a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev 237901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mTransform.identity(); 238901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.setTransform(mTransform); 239bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 240bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev // If the size of the canvas has changed, we need to make 241bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev // another grid. 242901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mGrid != null 243901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev && (mCanvasWidth != getBounds().width || mCanvasHeight != getBounds().height)) { 244901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mGrid.dispose(); 245901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mGrid = null; 246bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 247901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCanvasWidth = getBounds().width; 248901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCanvasHeight = getBounds().height; 249901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mGrid == null) { 250bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev // Make a transparent image; 251bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev ImageData imageData = 252901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev new ImageData(mCanvasWidth + mZoom + 1, mCanvasHeight + mZoom + 1, 1, 253bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev new PaletteData(new RGB[] { 254bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev new RGB(0, 0, 0) 255bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev })); 256bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev imageData.transparentPixel = 0; 257bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 258bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev // Draw the grid. 259901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mGrid = new Image(Display.getDefault(), imageData); 260901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev GC gc = new GC(mGrid); 261bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE)); 262901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev for (int x = 0; x <= mCanvasWidth + mZoom; x += mZoom) { 263901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev gc.drawLine(x, 0, x, mCanvasHeight + mZoom); 264bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 265901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev for (int y = 0; y <= mCanvasHeight + mZoom; y += mZoom) { 266901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev gc.drawLine(0, y, mCanvasWidth + mZoom, y); 267bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 268bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev gc.dispose(); 269bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 270bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 271901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.setClipping(new Rectangle(zoomedX, zoomedY, mWidth * mZoom + 1, mHeight 272901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev * mZoom + 1)); 273bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev e.gc.setAlpha(76); 274901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.drawImage(mGrid, (mCanvasWidth / 2 - mZoom / 2) % mZoom - mZoom, 275901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev (mCanvasHeight / 2 - mZoom / 2) % mZoom - mZoom); 276bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev e.gc.setAlpha(255); 277bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 278901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.setForeground(mCrosshairColor); 279901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.drawLine(0, mCanvasHeight / 2, mCanvasWidth - 1, mCanvasHeight / 2); 280901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev e.gc.drawLine(mCanvasWidth / 2, 0, mCanvasWidth / 2, mCanvasHeight - 1); 281bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 282bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 283bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 284bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev }; 285bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 286c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev private void doRedraw() { 28799bd6912e7b5b97fc6d4bb787e76b2d9dfffd7aeKonstantin Lopyrev Display.getDefault().syncExec(new Runnable() { 288f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 289bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void run() { 290c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev redraw(); 291bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 292bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev }); 293bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 294bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 295bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev private void loadImage() { 296901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mImage = mModel.getImage(); 297901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mImage != null) { 298901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mWidth = mImage.getBounds().width; 299901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mHeight = mImage.getBounds().height; 300bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } else { 301901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mWidth = 0; 302901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mHeight = 0; 303bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 304bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 305bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 306285f39916682c0f70a8dc9ccd930affa37365b97Konstantin Lopyrev // Note the syncExec and then synchronized... It avoids deadlock 307f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 308bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void imageLoaded() { 30970a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev Display.getDefault().syncExec(new Runnable() { 310f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 31170a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev public void run() { 31270a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (this) { 31370a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev loadImage(); 314901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairLocation = mModel.getCrosshairLocation(); 315901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mZoom = mModel.getZoom(); 316901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mOverlayImage = mModel.getOverlayImage(); 317901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mOverlayTransparency = mModel.getOverlayTransparency(); 31870a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 31970a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 32070a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev }); 321c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev doRedraw(); 322bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 323bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 324f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 325bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void imageChanged() { 32670a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev Display.getDefault().syncExec(new Runnable() { 327f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 32870a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev public void run() { 32970a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (this) { 33070a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev loadImage(); 33170a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 33270a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 33370a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev }); 334c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev doRedraw(); 335bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 336bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 337f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 338bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void crosshairMoved() { 339bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev synchronized (this) { 340901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mCrosshairLocation = mModel.getCrosshairLocation(); 341bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 342c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev doRedraw(); 343bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 344bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 345f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 346bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev public void selectionChanged() { 347bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev // pass 348bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 349bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev 350f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 35197b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev public void treeChanged() { 35297b0639645d9c387cdd9884272e053c467a240daKonstantin Lopyrev // pass 353bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev } 354c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev 355f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 356c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev public void zoomChanged() { 35770a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev Display.getDefault().syncExec(new Runnable() { 358f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 35970a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev public void run() { 36070a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (this) { 361901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev if (mGrid != null) { 36270a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev // To notify that the zoom level has changed, we get rid 36370a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev // of the 36470a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev // grid. 365901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mGrid.dispose(); 366901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mGrid = null; 36770a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 368901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mZoom = mModel.getZoom(); 36970a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 370c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 37170a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev }); 37270a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev doRedraw(); 37370a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 37470a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev 375f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 37670a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev public void overlayChanged() { 37770a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (this) { 378901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mOverlayImage = mModel.getOverlayImage(); 379901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mOverlayTransparency = mModel.getOverlayTransparency(); 38070a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 38170a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev doRedraw(); 38270a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev } 38370a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev 384f526dc3bb658b07c5a96d0372b17ffa2fbbe6079Xavier Ducrohet @Override 38570a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev public void overlayTransparencyChanged() { 38670a830d8ab396328ab984c75f923400af6a52e55Konstantin Lopyrev synchronized (this) { 387901803a3b2aaeafb4074974ec5bf9b08cc39d1c8Konstantin Lopyrev mOverlayTransparency = mModel.getOverlayTransparency(); 388c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 389c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev doRedraw(); 390c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev } 391bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev} 392