?? cpcanvas.java
字號:
/*
ChibiPaint
Copyright (c) 2006-2008 Marc Schefer
This file is part of ChibiPaint.
ChibiPaint is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ChibiPaint is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ChibiPaint. If not, see <http://www.gnu.org/licenses/>.
*/
package chibipaint.gui;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;
import chibipaint.*;
import chibipaint.engine.*;
import chibipaint.util.*;
public class CPCanvas extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener,
ComponentListener, KeyListener, CPController.ICPToolListener, CPController.ICPModeListener,
CPArtwork.ICPArtworkListener {
CPController controller;
// FIXME: this should not be public
public Image img;
BufferedImage checkerboardPattern;
MemoryImageSource imgSource;
CPRect updateRegion = new CPRect();
int[] buffer;
CPArtwork artwork;
boolean spacePressed = false;
// canvas transformations
float zoom = 1, minZoom = .05f, maxZoom = 16.f;
int offsetX, offsetY;
float canvasRotation = 0.f;
AffineTransform transform = new AffineTransform();
boolean interpolation = false;
int mouseX, mouseY;
boolean brushPreview = false;
Rectangle oldPreviewRect;
Cursor defaultCursor, hideCursor, moveCursor, crossCursor;
boolean mouseIn;
boolean dontStealFocus = false;
// Grid options
// FIXME: these shouldn't be public
public int gridSize = 32;
public boolean showGrid = false;
//
// Modes system: modes control the way the GUI is reacting to the user input
// All the tools are implemented through modes
//
CPMode defaultMode = new CPDefaultMode();
CPMode colorPickerMode = new CPColorPickerMode();
CPMode moveCanvasMode = new CPMoveCanvasMode();
CPMode rotateCanvasMode = new CPRotateCanvasMode();
CPMode floodFillMode = new CPFloodFillMode();
CPMode rectSelectionMode = new CPRectSelectionMode();
CPMode moveToolMode = new CPMoveToolMode();
// this must correspond to the stroke modes defined in CPToolInfo
CPMode drawingModes[] = { new CPFreehandMode(), new CPLineMode(), new CPBezierMode(), };
CPMode curDrawMode = drawingModes[CPBrushInfo.SM_FREEHAND];
CPMode curSelectedMode = curDrawMode;
CPMode activeMode = defaultMode;
// Container with scrollbars
JPanel container;
JScrollBar horizScroll, vertScroll;
public CPCanvas(CPController ctrl) {
this.controller = ctrl;
artwork = ctrl.getArtwork();
buffer = artwork.getDisplayBM().getData();
int w = artwork.width;
int h = artwork.height;
imgSource = new MemoryImageSource(w, h, buffer, 0, w);
imgSource.setAnimated(true);
// imgSource.setFullBufferUpdates(false);
img = createImage(imgSource);
centerCanvas();
ctrl.setCanvas(this);
int[] pixels = new int[16 * 16];
Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
hideCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "invisiblecursor");
defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
moveCursor = new Cursor(Cursor.MOVE_CURSOR);
crossCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
// Creates the checkboard pattern seen in transparent areas
checkerboardPattern = new BufferedImage(64, 64, BufferedImage.TYPE_INT_RGB);
pixels = new int[64 * 64];
for (int j = 0; j < 64; j++) {
for (int i = 0; i < 64; i++) {
if ((i & 0x8) != 0 ^ (j & 0x8) != 0) {
pixels[i + j * 64] = 0xffffffff;
} else {
pixels[i + j * 64] = 0xffcccccc;
}
}
}
checkerboardPattern.setRGB(0, 0, 64, 64, pixels, 0, 64);
/*
* KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false); Action escapeAction = new
* AbstractAction() { public void actionPerformed(ActionEvent e) { controller.setAlpha((int)(.5f*255)); } };
*
* getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW). put(escapeKeyStroke, "SPACE"); getActionMap().put("SPACE",
* escapeAction);
*/
// register as a listener for Mouse & MouseMotion events
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
addComponentListener(this);
addKeyListener(this);
controller.addToolListener(this);
controller.addModeListener(this);
artwork.addListener(this);
// We'll need to refresh the whole thing at least once
updateRegion = new CPRect(w, h);
// So that the tab key will work
setFocusTraversalKeysEnabled(false);
}
public boolean isOpaque() {
return true;
}
// //////////////////////////////////////////////////////////////////////////////////////
// Container and ScrollBars
// //////////////////////////////////////////////////////////////////////////////////////
public JPanel getContainer() {
if (container != null) {
return container;
}
container = new JPanel();
container.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1.;
gbc.weighty = 1.;
container.add(this, gbc);
vertScroll = new JScrollBar(JScrollBar.VERTICAL);
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.VERTICAL;
container.add(vertScroll, gbc);
horizScroll = new JScrollBar(JScrollBar.HORIZONTAL);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
container.add(horizScroll, gbc);
updateScrollBars();
horizScroll.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
Point p = getOffset();
p.x = - e.getValue();
setOffset(p);
}
});
vertScroll.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
Point p = getOffset();
p.y = - e.getValue();
setOffset(p);
}
});
return container;
}
void updateScrollBars() {
if (horizScroll == null || vertScroll == null
|| horizScroll.getValueIsAdjusting() || vertScroll.getValueIsAdjusting() ) {
return;
}
if (img == null) {
horizScroll.setEnabled(false);
vertScroll.setEnabled(false);
}
Rectangle visibleRect = getRefreshArea(new CPRect(img.getWidth(null), img.getHeight(null)));
updateScrollBar(horizScroll, visibleRect.x, visibleRect.width, getWidth(), -getOffset().x);
updateScrollBar(vertScroll, visibleRect.y, visibleRect.height, getHeight(), -getOffset().y);
}
void updateScrollBar(JScrollBar scroll, int visMin, int visWidth, int viewSize, int offset) {
if (visMin >= 0 && visMin + visWidth < viewSize) {
scroll.setEnabled(false);
} else {
scroll.setEnabled(true);
int xMin = Math.min(0, (visMin - viewSize / 4));
int xMax = Math.max(viewSize, visMin + visWidth + viewSize / 4);
scroll.setValues(offset, viewSize, xMin + offset, xMax + offset);
scroll.setBlockIncrement(Math.max(1, (int) (viewSize * .66)));
scroll.setUnitIncrement(Math.max(1, (int) (viewSize * .05)));
}
}
// //////////////////////////////////////////////////////////////////////////////////////
// painting methods
// //////////////////////////////////////////////////////////////////////////////////////
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(0x606060));
g2d.fillRect(0, 0, getWidth(), getHeight());
if (!updateRegion.isEmpty()) {
artwork.fusionLayers();
imgSource.newPixels(updateRegion.left, updateRegion.top, updateRegion.getWidth(), updateRegion.getHeight());
updateRegion.makeEmpty();
}
int w = img.getWidth(null);
int h = img.getHeight(null);
Graphics2D g2doc = (Graphics2D) g2d.create();
g2doc.transform(transform);
// Draw the checkerboard pattern
// we'll draw the pattern over an area larger than the image
// and then remove the extra to avoid display problems
// when the displayed bitmap doesn't match exactly with the checkerboard area
GeneralPath path = getCheckerboardBackgroundPath(new Rectangle2D.Float(0, 0, w, h));
// get the bounding rect and make it a bit larger to be sure to include everything
Rectangle pathRect = path.getBounds();
pathRect.x -= 2;
pathRect.y -= 2;
pathRect.width += 4;
pathRect.height += 4;
g2d.setPaint(new TexturePaint(checkerboardPattern, new Rectangle(0, 0, 64, 64)));
g2d.fill(pathRect);
// Draw the image on the canvas
if (interpolation) {
RenderingHints hints = g2doc.getRenderingHints();
hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2doc.addRenderingHints(hints);
}
g2doc.drawImage(img, 0, 0, w, h, 0, 0, img.getWidth(null), img.getHeight(null), null);
// Redraw over the checkerboard border, removing a just a little bit of the image to avoid display problems
path.append(pathRect, false);
path.setWindingRule(GeneralPath.WIND_EVEN_ODD);
g2d.setColor(new Color(0x606060));
g2d.fill(path);
// This XOR mode guaranties contrast over all colors
g2d.setColor(Color.black);
g2d.setXORMode(new Color(0x808080));
// Draw selection
if (!artwork.getSelection().isEmpty()) {
Stroke stroke = g2d.getStroke();
g2d
.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1f,
new float[] { 2f }, 0f));
g2d.draw(coordToDisplay(artwork.getSelection()));
g2d.setStroke(stroke);
}
// Draw grid
if (showGrid) {
Stroke stroke = g2d.getStroke();
g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1f, null, 0f));
CPRect size = artwork.getSize();
for (int i = gridSize - 1; i < size.getWidth(); i += gridSize) {
Point2D.Float p1 = coordToDisplay(new Point2D.Float(i, 0));
Point2D.Float p2 = coordToDisplay(new Point2D.Float(i, size.getHeight() - 1));
g2d.draw(new Line2D.Float(p1, p2));
}
for (int i = gridSize - 1; i < size.getHeight(); i += gridSize) {
Point2D.Float p1 = coordToDisplay(new Point2D.Float(0, i));
Point2D.Float p2 = coordToDisplay(new Point2D.Float(size.getWidth() - 1, i));
g2d.draw(new Line2D.Float(p1, p2));
}
g2d.setStroke(stroke);
}
// Additional drawing by the current mode
activeMode.paint(g2d);
// This bit of code is used to test repaint areas
/*
* if((test++ & 16) == 0) { g.setColor(Color.magenta); Dimension dd = getSize();
* g.fillRect(0,0,dd.width,dd.height); g.setColor(Color.black); }
*/
}
private GeneralPath getCheckerboardBackgroundPath(Rectangle2D r) {
GeneralPath path = new GeneralPath();
float delta = (canvasRotation == 0f ? 0f : 1f / zoom);
Point2D.Float p = coordToDisplay(new Point2D.Float((float) r.getX() + delta, (float) r.getY() + delta));
path.moveTo(p.x, p.y);
p = coordToDisplay(new Point2D.Float((float) r.getMaxX() - delta, (float) r.getY() + delta));
path.lineTo(p.x, p.y);
p = coordToDisplay(new Point2D.Float((float) r.getMaxX() - delta, (float) r.getMaxY() - delta));
path.lineTo(p.x, p.y);
p = coordToDisplay(new Point2D.Float((float) r.getX() + delta, (float) r.getMaxY() - delta));
path.lineTo(p.x, p.y);
path.closePath();
return path;
}
//
// Mouse input methods
//
public void mouseEntered(MouseEvent e) {
mouseIn = true;
}
public void mouseExited(MouseEvent e) {
mouseIn = false;
repaint();
}
public void mousePressed(MouseEvent e) {
requestFocusInWindow();
activeMode.mousePressed(e);
}
public void mouseDragged(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
activeMode.mouseDragged(e);
}
public void mouseReleased(MouseEvent e) {
activeMode.mouseReleased(e);
}
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
if (!dontStealFocus) {
requestFocusInWindow();
}
activeMode.mouseMoved(e);
CPTablet.getRef().mouseDetect();
}
public void mouseWheelMoved(MouseWheelEvent e) {
float factor = 1;
if (e.getWheelRotation() > 0) {
factor = 1 / 1.15f;
}
if (e.getWheelRotation() < 0) {
factor = 1.15f;
}
Point2D.Float pf = coordToDocument(new Point2D.Float(mouseX, mouseY));
if (artwork.isPointWithin(pf.x, pf.y)) {
zoomOnPoint(getZoom() * factor, mouseX, mouseY);
} else {
zoomOnPoint(getZoom() * factor, offsetX + (int) (artwork.width * zoom / 2), offsetY
+ (int) (artwork.height * zoom / 2));
}
// FIXME: clean the above code, some coordinates get transformed multiple times for nothing
}
// //////////////////////////////////////////////////////////////////////////////////////
// Transformation methods
// //////////////////////////////////////////////////////////////////////////////////////
// Zoom
public void setZoom(float zoom) {
this.zoom = zoom;
updateTransform();
}
public float getZoom() {
return zoom;
}
// Offset
public void setOffset(Point p) {
setOffset(p.x, p.y);
}
public void setOffset(int x, int y) {
offsetX = x;
offsetY = y;
updateTransform();
}
public Point getOffset() {
return new Point(offsetX, offsetY);
}
// Rotation
public void setRotation(float angle) {
canvasRotation = (float) (angle % (2 * Math.PI));
updateTransform();
}
public float getRotation() {
return canvasRotation;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -