⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpartwork.java

📁 this is best wamp jkbkgnkldjkb jkfbjdksgkjl bjkgsbkjfdb gjdsblkj gbfkjsd
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/*
	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.engine;

import java.util.*;

import chibipaint.*;
import chibipaint.engine.CPBrushManager.*;
import chibipaint.util.*;

//FIXME: BROKEN: use setForegroundColor and setBrush, controller's layerChanged replaced by the ICPArtworkListener mechanism

public class CPArtwork {

	public int width, height;

	Vector<CPLayer> layers;
	CPLayer curLayer;
	int activeLayer;

	CPRect curSelection = new CPRect();

	CPLayer fusion, undoBuffer, opacityBuffer;
	CPRect fusionArea, undoArea, opacityArea;

	Random rnd = new Random();

	public interface ICPArtworkListener {

		void updateRegion(CPArtwork artwork, CPRect region);

		void layerChange(CPArtwork artwork);
	}

	private LinkedList<ICPArtworkListener> artworkListeners = new LinkedList();

	// Clipboard

	static private class CPClip {

		CPColorBmp bmp;
		int x, y;

		CPClip(CPColorBmp bmp, int x, int y) {
			this.bmp = bmp;
			this.x = x;
			this.y = y;
		}
	};

	CPClip clipboard = null;

	LinkedList<CPUndo> undoList, redoList;

	private CPBrushInfo curBrush;

	// FIXME: shouldn't be public
	public CPBrushManager brushManager = new CPBrushManager();

	float lastX, lastY, lastPressure;
	int[] brushBuffer = null;

	private int maxUndo = 30;

	//
	// Current Engine Parameters
	//

	boolean sampleAllLayers = false;
	boolean lockAlpha = false;

	int curColor;

	CPBrushTool paintingModes[] = { new CPBrushToolSimpleBrush(), new CPBrushToolEraser(), new CPBrushToolDodge(),
			new CPBrushToolBurn(), new CPBrushToolWatercolor(), new CPBrushToolBlur(), new CPBrushToolSmudge(),
			new CPBrushToolOil(), };

	static final int BURN_CONSTANT = 260;
	static final int BLUR_MIN = 64;
	static final int BLUR_MAX = 1;

	public CPArtwork(int width, int height) {
		this.width = width;
		this.height = height;

		layers = new Vector();

		CPLayer defaultLayer = new CPLayer(width, height);
		defaultLayer.name = getDefaultLayerName();
		defaultLayer.clear(0xffffffff);
		layers.add(defaultLayer);

		curLayer = layers.get(0);
		fusionArea = new CPRect(0, 0, width, height);
		undoArea = new CPRect();
		opacityArea = new CPRect();
		activeLayer = 0;
		curSelection.makeEmpty();

		undoBuffer = new CPLayer(width, height);
		// we reserve a double sized buffer to be used as a 16bits per channel buffer
		opacityBuffer = new CPLayer(width, height);

		fusion = new CPLayer(width, height);

		undoList = new LinkedList();
		redoList = new LinkedList();
	}

	public long getDocMemoryUsed() {
		return (long) width * height * 4 * (3 + layers.size())
				+ (clipboard != null ? clipboard.bmp.getWidth() * clipboard.bmp.getHeight() * 4 : 0);
	}

	public long getUndoMemoryUsed() {
		long total = 0;

		CPColorBmp lastBitmap = clipboard != null ? clipboard.bmp : null;

		for (int i = redoList.size() - 1; i >= 0; i--) {
			CPUndo undo = redoList.get(i);

			total += undo.getMemoryUsed(true, lastBitmap);
		}

		for (CPUndo undo : undoList) {
			total += undo.getMemoryUsed(false, lastBitmap);
		}

		return total;
	}

	public CPLayer getDisplayBM() {
		fusionLayers();
		return fusion;

		// for(int i=0; i<opacityBuffer.data.length; i++)
		// opacityBuffer.data[i] |= 0xff000000;
		// return opacityBuffer;
	}

	public void fusionLayers() {
		if (fusionArea.isEmpty()) {
			return;
		}

		mergeOpacityBuffer(curColor, false);

		fusion.clear(fusionArea, 0x00ffffff);
		boolean fullAlpha = true, first = true;
		for (CPLayer l : layers) {
			if (!first) {
				fullAlpha = fullAlpha && fusion.hasAlpha(fusionArea);
			}

			if (l.visible) {
				first = false;
				if (fullAlpha) {
					l.fusionWithFullAlpha(fusion, fusionArea);
				} else {
					l.fusionWith(fusion, fusionArea);
				}
			}
		}

		fusionArea.makeEmpty();
	}

	// ///////////////////////////////////////////////////////////////////////////////////
	// Listeners
	// ///////////////////////////////////////////////////////////////////////////////////

	public void addListener(ICPArtworkListener listener) {
		artworkListeners.addLast(listener);
	}

	public void callListenersUpdateRegion(CPRect region) {
		for (ICPArtworkListener l : artworkListeners) {
			l.updateRegion(this, region);
		}
	}

	public void callListenersLayerChange() {
		for (ICPArtworkListener l : artworkListeners) {
			l.layerChange(this);
		}
	}

	// ///////////////////////////////////////////////////////////////////////////////////
	// Global Parameters
	// ///////////////////////////////////////////////////////////////////////////////////

	public void setSampleAllLayers(boolean b) {
		sampleAllLayers = b;
	}

	public void setLockAlpha(boolean b) {
		lockAlpha = b;
	}

	public void setForegroundColor(int color) {
		curColor = color;
	}

	public void setBrush(CPBrushInfo brush) {
		curBrush = brush;
	}

	// ///////////////////////////////////////////////////////////////////////////////////
	// Paint engine
	// ///////////////////////////////////////////////////////////////////////////////////

	public void beginStroke(float x, float y, float pressure) {
		if (curBrush == null) {
			return;
		}

		paintingModes[curBrush.paintMode].beginStroke(x, y, pressure);
	}

	public void continueStroke(float x, float y, float pressure) {
		if (curBrush == null) {
			return;
		}

		paintingModes[curBrush.paintMode].continueStroke(x, y, pressure);
	}

	public void endStroke() {
		if (curBrush == null) {
			return;
		}

		paintingModes[curBrush.paintMode].endStroke();
	}

	void mergeOpacityBuffer(int color, boolean clear) {
		if (!opacityArea.isEmpty()) {
			if (curBrush.paintMode != CPBrushInfo.M_ERASE || !lockAlpha) {
				paintingModes[curBrush.paintMode].mergeOpacityBuf(opacityArea, color);
			} else {
				// FIXME: it would be nice to be able to set the paper color
				paintingModes[CPBrushInfo.M_PAINT].mergeOpacityBuf(opacityArea, 0xffffff);
			}

			if (lockAlpha) {
				restoreAlpha(opacityArea);
			}

			if (clear) {
				opacityBuffer.clear(opacityArea, 0);
			}

			opacityArea.makeEmpty();
		}
	}

	void restoreAlpha(CPRect r) {
		getActiveLayer().copyAlphaFrom(undoBuffer, r);
	}

	// Extend this class to create new tools and brush types
	abstract class CPBrushTool {

		abstract public void beginStroke(float x, float y, float pressure);

		abstract public void continueStroke(float x, float y, float pressure);

		abstract public void endStroke();

		abstract public void mergeOpacityBuf(CPRect dstRect, int color);
	}

	abstract class CPBrushToolBase extends CPBrushTool {

		public void beginStroke(float x, float y, float pressure) {
			undoBuffer.copyFrom(curLayer);
			undoArea.makeEmpty();

			opacityBuffer.clear();
			opacityArea.makeEmpty();

			lastX = x;
			lastY = y;
			lastPressure = pressure;
			paintDab(x, y, pressure);
		}

		public void continueStroke(float x, float y, float pressure) {
			float dist = (float) Math.sqrt(((lastX - x) * (lastX - x) + (lastY - y) * (lastY - y)));
			float spacing = Math.max(curBrush.minSpacing, curBrush.curSize * curBrush.spacing);

			if (dist > spacing) {
				float nx = lastX, ny = lastY, np = lastPressure;

				float df = (spacing - 0.001f) / dist;
				for (float f = df; f <= 1.f; f += df) {
					nx = f * x + (1.f - f) * lastX;
					ny = f * y + (1.f - f) * lastY;
					np = f * pressure + (1.f - f) * lastPressure;
					paintDab(nx, ny, np);
				}
				lastX = nx;
				lastY = ny;
				lastPressure = np;
			}
		}

		public void endStroke() {
			undoArea.clip(getSize());
			if (!undoArea.isEmpty()) {
				mergeOpacityBuffer(curColor, false);
				addUndo(new CPUndoPaint());
			}
			brushBuffer = null;
		}

		void paintDab(float x, float y, float pressure) {
			curBrush.applyPressure(pressure);
			if (curBrush.scattering > 0f) {
				x += rnd.nextGaussian() * curBrush.curScattering / 4f;
				y += rnd.nextGaussian() * curBrush.curScattering / 4f;
				// x += (rnd.nextFloat() - .5f) * tool.scattering;
				// y += (rnd.nextFloat() - .5f) * tool.scattering;
			}
			CPBrushDab dab = brushManager.getDab(x, y, curBrush);
			paintDab(dab);
		}

		void paintDab(CPBrushDab dab) {
			CPRect srcRect = new CPRect(dab.width, dab.height);
			CPRect dstRect = new CPRect(dab.width, dab.height);
			dstRect.translate(dab.x, dab.y);

			clipSourceDest(srcRect, dstRect);

			// drawing entirely outside the canvas
			if (dstRect.isEmpty()) {
				return;
			}

			undoArea.union(dstRect);
			opacityArea.union(dstRect);
			invalidateFusion(dstRect);

			paintDabImplementation(srcRect, dstRect, dab);
		}

		abstract void paintDabImplementation(CPRect srcRect, CPRect dstRect, CPBrushDab dab);
	}

	class CPBrushToolSimpleBrush extends CPBrushToolBase {

		void paintDabImplementation(CPRect srcRect, CPRect dstRect, CPBrushDab dab) {
			// FIXME: there should be no reference to a specific tool here
			// create a new brush parameter instead
			if (curBrush.isAirbrush) {
				paintFlow(srcRect, dstRect, dab.brush, dab.width, Math.max(1, dab.alpha / 8));
			} else if (curBrush.toolNb == CPController.T_PEN) {
				paintFlow(srcRect, dstRect, dab.brush, dab.width, Math.max(1, dab.alpha / 2));
			} else {
				// paintOpacityFlow(srcRect, dstRect, brush, dab.stride, alpha, 255);
				// paintOpacityFlow(srcRect, dstRect, brush, dab.stride, 128, alpha);
				paintOpacity(srcRect, dstRect, dab.brush, dab.width, dab.alpha);
			}
		}

		public void mergeOpacityBuf(CPRect dstRect, int color) {
			int[] opacityData = opacityBuffer.data;
			int[] undoData = undoBuffer.data;

			for (int j = dstRect.top; j < dstRect.bottom; j++) {
				int dstOffset = dstRect.left + j * width;
				for (int i = dstRect.left; i < dstRect.right; i++, dstOffset++) {
					int opacityAlpha = opacityData[dstOffset] / 255;
					if (opacityAlpha > 0) {
						int destColor = undoData[dstOffset];

						int destAlpha = destColor >>> 24;
						int newLayerAlpha = opacityAlpha + destAlpha * (255 - opacityAlpha) / 255;
						int realAlpha = 255 * opacityAlpha / newLayerAlpha;
						int invAlpha = 255 - realAlpha;

						int newColor = (((color >>> 16 & 0xff) * realAlpha + (destColor >>> 16 & 0xff) * invAlpha) / 255) << 16
								& 0xff0000
								| (((color >>> 8 & 0xff) * realAlpha + (destColor >>> 8 & 0xff) * invAlpha) / 255) << 8
								& 0xff00 | (((color & 0xff) * realAlpha + (destColor & 0xff) * invAlpha) / 255) & 0xff;

						newColor |= newLayerAlpha << 24 & 0xff000000;
						curLayer.data[dstOffset] = newColor;
					}
				}
			}
		}

		void paintOpacity(CPRect srcRect, CPRect dstRect, byte[] brush, int w, int alpha) {
			int[] opacityData = opacityBuffer.data;

			int by = srcRect.top;
			for (int j = dstRect.top; j < dstRect.bottom; j++, by++) {
				int srcOffset = srcRect.left + by * w;
				int dstOffset = dstRect.left + j * width;
				for (int i = dstRect.left; i < dstRect.right; i++, srcOffset++, dstOffset++) {
					int brushAlpha = (brush[srcOffset] & 0xff) * alpha;
					if (brushAlpha != 0) {
						int opacityAlpha = opacityData[dstOffset];
						if (brushAlpha > opacityAlpha) {
							opacityData[dstOffset] = brushAlpha;
						}
					}

				}
			}
		}

		void paintFlow(CPRect srcRect, CPRect dstRect, byte[] brush, int w, int alpha) {
			int[] opacityData = opacityBuffer.data;

			int by = srcRect.top;
			for (int j = dstRect.top; j < dstRect.bottom; j++, by++) {
				int srcOffset = srcRect.left + by * w;
				int dstOffset = dstRect.left + j * width;
				for (int i = dstRect.left; i < dstRect.right; i++, srcOffset++, dstOffset++) {
					int brushAlpha = (brush[srcOffset] & 0xff) * alpha;
					if (brushAlpha != 0) {
						int opacityAlpha = Math.min(255 * 255, opacityData[dstOffset]
								+ (255 - opacityData[dstOffset] / 255) * brushAlpha / 255);
						opacityData[dstOffset] = opacityAlpha;
					}

				}
			}
		}

		void paintOpacityFlow(CPRect srcRect, CPRect dstRect, byte[] brush, int w, int opacity, int flow) {
			int[] opacityData = opacityBuffer.data;

			int by = srcRect.top;
			for (int j = dstRect.top; j < dstRect.bottom; j++, by++) {
				int srcOffset = srcRect.left + by * w;
				int dstOffset = dstRect.left + j * width;
				for (int i = dstRect.left; i < dstRect.right; i++, srcOffset++, dstOffset++) {
					int brushAlpha = (brush[srcOffset] & 0xff) * flow;
					if (brushAlpha != 0) {
						int opacityAlpha = opacityData[dstOffset];

						int newAlpha = Math.min(255 * 255, opacityAlpha + (opacity - opacityAlpha / 255) * brushAlpha
								/ 255);
						newAlpha = Math.min(opacity * (brush[srcOffset] & 0xff), newAlpha);
						if (newAlpha > opacityAlpha) {
							opacityData[dstOffset] = newAlpha;
						}
					}

				}
			}
		}
	}

	class CPBrushToolEraser extends CPBrushToolSimpleBrush {

		public void mergeOpacityBuf(CPRect dstRect, int color) {
			int[] opacityData = opacityBuffer.data;
			int[] undoData = undoBuffer.data;

			for (int j = dstRect.top; j < dstRect.bottom; j++) {
				int dstOffset = dstRect.left + j * width;
				for (int i = dstRect.left; i < dstRect.right; i++, dstOffset++) {
					int opacityAlpha = opacityData[dstOffset] / 255;
					if (opacityAlpha > 0) {
						int destColor = undoData[dstOffset];
						int destAlpha = destColor >>> 24;

						int realAlpha = destAlpha * (255 - opacityAlpha) / 255;
						curLayer.data[dstOffset] = destColor & 0xffffff | realAlpha << 24;
					}
				}
			}
		}
	}

	class CPBrushToolDodge extends CPBrushToolSimpleBrush {

		public void mergeOpacityBuf(CPRect dstRect, int color) {
			int[] opacityData = opacityBuffer.data;
			int[] undoData = undoBuffer.data;

			for (int j = dstRect.top; j < dstRect.bottom; j++) {
				int dstOffset = dstRect.left + j * width;
				for (int i = dstRect.left; i < dstRect.right; i++, dstOffset++) {
					int opacityAlpha = opacityData[dstOffset] / 255;
					if (opacityAlpha > 0) {
						int destColor = undoData[dstOffset];
						if ((destColor & 0xff000000) != 0) {
							opacityAlpha += 255;
							int r = (destColor >>> 16 & 0xff) * opacityAlpha / 255;
							int g = (destColor >>> 8 & 0xff) * opacityAlpha / 255;
							int b = (destColor & 0xff) * opacityAlpha / 255;

							if (r > 255) {
								r = 255;
							}
							if (g > 255) {
								g = 255;
							}
							if (b > 255) {
								b = 255;
							}

							int newColor = destColor & 0xff000000 | r << 16 | g << 8 | b;
							curLayer.data[dstOffset] = newColor;
						}
					}
				}
			}
		}
	}

	class CPBrushToolBurn extends CPBrushToolSimpleBrush {

		public void mergeOpacityBuf(CPRect dstRect, int color) {
			int[] opacityData = opacityBuffer.data;
			int[] undoData = undoBuffer.data;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -