📄 cplayer.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.engine;
import java.util.*;
import chibipaint.util.*;
public class CPLayer extends CPColorBmp {
public int blendMode = LM_NORMAL, alpha = 100;
public String name;
public boolean visible = true;
public final static int LM_NORMAL = 0;
public final static int LM_MULTIPLY = 1;
public final static int LM_ADD = 2;
public final static int LM_SCREEN = 3;
public final static int LM_LIGHTEN = 4;
public final static int LM_DARKEN = 5;
public final static int LM_SUBTRACT = 6;
public final static int LM_DODGE = 7;
public final static int LM_BURN = 8;
public final static int LM_OVERLAY = 9;
public final static int LM_HARDLIGHT = 10;
public final static int LM_SOFTLIGHT = 11;
public final static int LM_VIVIDLIGHT = 12;
public final static int LM_LINEARLIGHT = 13;
public final static int LM_PINLIGHT = 14;
//
// Look-Up tables for some of the blend modes
//
private static int softLightLUTSquare[];
private static int softLightLUTSquareRoot[];
public CPLayer(int width, int height) {
super(width, height);
name = new String("");
clear(0xffffff);
if (softLightLUTSquare == null) {
makeLookUpTables();
}
}
public void makeLookUpTables() {
// V - V^2 table
softLightLUTSquare = new int[256];
for (int i = 0; i < 256; i++) {
double v = i / 255.;
softLightLUTSquare[i] = (int) ((v - v * v) * 255.);
}
// sqrt(V) - V table
softLightLUTSquareRoot = new int[256];
for (int i = 0; i < 256; i++) {
double v = i / 255.;
softLightLUTSquareRoot[i] = (int) ((Math.sqrt(v) - v) * 255.);
}
}
public void clear() {
clear(0);
}
public void clear(int color) {
for (int i = 0; i < width * height; i++) {
data[i] = color;
}
}
public void clear(CPRect r, int color) {
CPRect rect = new CPRect(0, 0, width, height);
rect.clip(r);
for (int j = rect.top; j < rect.bottom; j++) {
for (int i = rect.left; i < rect.right; i++) {
data[i + j * width] = color;
}
}
}
public int[] getData() {
return data;
}
public int getAlpha() {
return alpha;
}
public int getBlendMode() {
return blendMode;
}
public void copyFrom(CPLayer l) {
name = l.name;
blendMode = l.blendMode;
alpha = l.alpha;
visible = l.visible;
copyDataFrom(l);
}
// Layer blending functions
//
// The FullAlpha versions are the ones that work in all cases
// others need the bottom layer to be 100% opaque but are faster
public void fusionWith(CPLayer fusion, CPRect r) {
if (alpha <= 0) {
return;
}
switch (blendMode) {
case LM_NORMAL:
if (alpha >= 100) {
fusionWithNormalNoAlpha(fusion, r);
} else {
fusionWithNormal(fusion, r);
}
break;
case LM_MULTIPLY:
fusionWithMultiply(fusion, r);
break;
case LM_ADD:
fusionWithAdd(fusion, r);
break;
case LM_SCREEN:
fusionWithScreenFullAlpha(fusion, r);
break;
case LM_LIGHTEN:
fusionWithLightenFullAlpha(fusion, r);
break;
case LM_DARKEN:
fusionWithDarkenFullAlpha(fusion, r);
break;
case LM_SUBTRACT:
fusionWithSubtractFullAlpha(fusion, r);
break;
case LM_DODGE:
fusionWithDodgeFullAlpha(fusion, r);
break;
case LM_BURN:
fusionWithBurnFullAlpha(fusion, r);
break;
case LM_OVERLAY:
fusionWithOverlayFullAlpha(fusion, r);
break;
case LM_HARDLIGHT:
fusionWithHardLightFullAlpha(fusion, r);
break;
case LM_SOFTLIGHT:
fusionWithSoftLightFullAlpha(fusion, r);
break;
case LM_VIVIDLIGHT:
fusionWithVividLightFullAlpha(fusion, r);
break;
case LM_LINEARLIGHT:
fusionWithLinearLightFullAlpha(fusion, r);
break;
case LM_PINLIGHT:
fusionWithPinLightFullAlpha(fusion, r);
break;
}
}
public void fusionWithFullAlpha(CPLayer fusion, CPRect r) {
if (alpha <= 0) {
return;
}
switch (blendMode) {
case LM_NORMAL:
fusionWithNormalFullAlpha(fusion, r);
break;
case LM_MULTIPLY:
fusionWithMultiplyFullAlpha(fusion, r);
break;
case LM_ADD:
fusionWithAddFullAlpha(fusion, r);
break;
case LM_SCREEN:
fusionWithScreenFullAlpha(fusion, r);
break;
case LM_LIGHTEN:
fusionWithLightenFullAlpha(fusion, r);
break;
case LM_DARKEN:
fusionWithDarkenFullAlpha(fusion, r);
break;
case LM_SUBTRACT:
fusionWithSubtractFullAlpha(fusion, r);
break;
case LM_DODGE:
fusionWithDodgeFullAlpha(fusion, r);
break;
case LM_BURN:
fusionWithBurnFullAlpha(fusion, r);
break;
case LM_OVERLAY:
fusionWithOverlayFullAlpha(fusion, r);
break;
case LM_HARDLIGHT:
fusionWithHardLightFullAlpha(fusion, r);
break;
case LM_SOFTLIGHT:
fusionWithSoftLightFullAlpha(fusion, r);
break;
case LM_VIVIDLIGHT:
fusionWithVividLightFullAlpha(fusion, r);
break;
case LM_LINEARLIGHT:
fusionWithLinearLightFullAlpha(fusion, r);
break;
case LM_PINLIGHT:
fusionWithPinLightFullAlpha(fusion, r);
break;
}
}
public void fusionWithMultiply(CPLayer fusion, CPRect rc) {
CPRect rect = new CPRect(0, 0, width, height);
rect.clip(rc);
for (int j = rect.top; j < rect.bottom; j++) {
int off = rect.left + j * width;
for (int i = rect.left; i < rect.right; i++, off++) {
int color1 = data[off];
int alpha = (color1 >>> 24) * this.alpha / 100;
if (alpha == 0) {
continue;
} else {
int color2 = fusion.data[off];
color1 = color1 ^ 0xffffffff;
fusion.data[off] = 0xff000000
| ((color2 >>> 16 & 0xff) - (color1 >>> 16 & 0xff) * (color2 >>> 16 & 0xff) * alpha
/ (255 * 255)) << 16
| ((color2 >>> 8 & 0xff) - (color1 >>> 8 & 0xff) * (color2 >>> 8 & 0xff) * alpha
/ (255 * 255)) << 8
| ((color2 & 0xff) - (color1 & 0xff) * (color2 & 0xff) * alpha / (255 * 255));
}
}
}
}
public void fusionWithNormal(CPLayer fusion, CPRect rc) {
CPRect rect = new CPRect(0, 0, width, height);
rect.clip(rc);
for (int j = rect.top; j < rect.bottom; j++) {
int off = rect.left + j * width;
for (int i = rect.left; i < rect.right; i++, off++) {
int color1 = data[off];
int alpha = (color1 >>> 24) * this.alpha / 100;
if (alpha == 0) {
continue;
} else if (alpha == 255) {
fusion.data[off] = color1;
} else {
int color2 = fusion.data[off];
int invAlpha = 255 - alpha;
fusion.data[off] = 0xff000000
| (((color1 >>> 16 & 0xff) * alpha + (color2 >>> 16 & 0xff) * invAlpha) / 255) << 16
| (((color1 >>> 8 & 0xff) * alpha + (color2 >>> 8 & 0xff) * invAlpha) / 255) << 8
| (((color1 & 0xff) * alpha + (color2 & 0xff) * invAlpha) / 255);
}
}
}
}
public void fusionWithNormalNoAlpha(CPLayer fusion, CPRect rc) {
CPRect rect = new CPRect(0, 0, width, height);
rect.clip(rc);
for (int j = rect.top; j < rect.bottom; j++) {
int off = rect.left + j * width;
for (int i = rect.left; i < rect.right; i++, off++) {
int color1 = data[off];
int alpha = color1 >>> 24;
if (alpha == 0) {
continue;
} else if (alpha == 255) {
fusion.data[off] = color1;
} else {
int color2 = fusion.data[off];
int invAlpha = 255 - alpha;
fusion.data[off] = 0xff000000
| (((color1 >>> 16 & 0xff) * alpha + (color2 >>> 16 & 0xff) * invAlpha) / 255) << 16
| (((color1 >>> 8 & 0xff) * alpha + (color2 >>> 8 & 0xff) * invAlpha) / 255) << 8
| (((color1 & 0xff) * alpha + (color2 & 0xff) * invAlpha) / 255);
}
}
}
}
public void fusionWithAdd(CPLayer fusion, CPRect rc) {
CPRect rect = new CPRect(0, 0, width, height);
rect.clip(rc);
for (int j = rect.top; j < rect.bottom; j++) {
int off = rect.left + j * width;
for (int i = rect.left; i < rect.right; i++, off++) {
int color1 = data[off];
int alpha = (color1 >>> 24) * this.alpha / 100;
if (alpha == 0) {
continue;
} else {
int color2 = fusion.data[off];
int r = Math.min(255, (color2 >>> 16 & 0xff) + alpha * (color1 >>> 16 & 0xff) / 255);
int g = Math.min(255, (color2 >>> 8 & 0xff) + alpha * (color1 >>> 8 & 0xff) / 255);
int b = Math.min(255, (color2 & 0xff) + alpha * (color1 & 0xff) / 255);
fusion.data[off] = 0xff000000 | r << 16 | g << 8 | b;
}
}
}
}
// Normal Alpha Mode
// C = A*d + B*(1-d) and d = aa / (aa + ab - aa*ab)
public void fusionWithNormalFullAlpha(CPLayer fusion, CPRect rc) {
CPRect rect = new CPRect(0, 0, width, height);
rect.clip(rc);
for (int j = rect.top; j < rect.bottom; j++) {
int off = rect.left + j * width;
for (int i = rect.left; i < rect.right; i++, off++) {
int color1 = data[off];
int alpha1 = (color1 >>> 24) * alpha / 100;
int color2 = fusion.data[off];
int alpha2 = (color2 >>> 24) * fusion.alpha / 100;
int newAlpha = alpha1 + alpha2 - alpha1 * alpha2 / 255;
if (newAlpha > 0) {
int realAlpha = alpha1 * 255 / newAlpha;
int invAlpha = 255 - realAlpha;
fusion.data[off] = newAlpha << 24
| (((color1 >>> 16 & 0xff) * realAlpha + (color2 >>> 16 & 0xff) * invAlpha) / 255) << 16
| (((color1 >>> 8 & 0xff) * realAlpha + (color2 >>> 8 & 0xff) * invAlpha) / 255) << 8
| (((color1 & 0xff) * realAlpha + (color2 & 0xff) * invAlpha) / 255);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -