package mara.mybox.image; import java.awt.Color; import java.util.Arrays; import java.util.List; import java.util.Random; import mara.mybox.value.AppVariables; /** * @Author Mara * @CreateDate 2019-3-24 11:24:03 * @Version 1.0 * @Description * @License Apache License Version 2.0 */ // https://en.wikipedia.org/wiki/Blend_modes // https://blog.csdn.net/bravebean/article/details/51392440 // https://www.cnblogs.com/bigdream6/p/8385886.html // https://baike.baidu.com/item/%E6%B7%B7%E5%90%88%E6%A8%A1%E5%BC%8F/6700481?fr=aladdin public abstract class PixelBlend { public enum ImagesBlendMode { NORMAL, DISSOLVE, DARKEN, MULTIPLY, COLOR_BURN, LINEAR_BURN, SOFT_BURN, LIGHTEN, SCREEN, COLOR_DODGE, LINEAR_DODGE, SOFT_DODGE, DIVIDE, VIVID_LIGHT, LINEAR_LIGHT, SUBTRACT, AVERAGE, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DIFFERENCE, NEGATION, EXCLUSION, REFLECT, GLOW, FREEZE, HEAT, STAMP, RED, GREEN, BLUE, HUE, SATURATION, COLOR, LUMINOSITY } protected ImagesBlendMode blendMode; protected float alpha; protected Color foreColor, backColor; protected int red, green, blue; public PixelBlend() { } public PixelBlend(ImagesBlendMode blendMode) { this.blendMode = blendMode; } public PixelBlend(ImagesBlendMode blendMode, float alpha) { this.blendMode = blendMode; this.alpha = alpha; } public PixelBlend(float alpha) { this.alpha = alpha; } public static List<String> allBlendModes() { return Arrays.asList(AppVariables.message("NormalMode"), AppVariables.message("DissolveMode"), AppVariables.message("MultiplyMode"), AppVariables.message("ScreenMode"), AppVariables.message("OverlayMode"), AppVariables.message("HardLightMode"), AppVariables.message("SoftLightMode"), AppVariables.message("ColorDodgeMode"), AppVariables.message("LinearDodgeMode"), AppVariables.message("DivideMode"), AppVariables.message("ColorBurnMode"), AppVariables.message("LinearBurnMode"), AppVariables.message("VividLightMode"), AppVariables.message("LinearLightMode"), AppVariables.message("SubtractMode"), AppVariables.message("DifferenceMode"), AppVariables.message("ExclusionMode"), AppVariables.message("DarkenMode"), AppVariables.message("LightenMode"), AppVariables.message("HueMode"), AppVariables.message("SaturationMode"), AppVariables.message("ColorMode"), AppVariables.message("LuminosityMode")); } public static ImagesBlendMode getBlendModeByName(String mode) { if (AppVariables.message("NormalMode").equals(mode)) { return ImagesBlendMode.NORMAL; } else if (AppVariables.message("DissolveMode").equals(mode)) { return ImagesBlendMode.DISSOLVE; } else if (AppVariables.message("MultiplyMode").equals(mode)) { return ImagesBlendMode.MULTIPLY; } else if (AppVariables.message("ScreenMode").equals(mode)) { return ImagesBlendMode.SCREEN; } else if (AppVariables.message("OverlayMode").equals(mode)) { return ImagesBlendMode.OVERLAY; } else if (AppVariables.message("HardLightMode").equals(mode)) { return ImagesBlendMode.HARD_LIGHT; } else if (AppVariables.message("SoftLightMode").equals(mode)) { return ImagesBlendMode.SOFT_LIGHT; } else if (AppVariables.message("ColorDodgeMode").equals(mode)) { return ImagesBlendMode.COLOR_DODGE; } else if (AppVariables.message("LinearDodgeMode").equals(mode)) { return ImagesBlendMode.LINEAR_DODGE; } else if (AppVariables.message("DivideMode").equals(mode)) { return ImagesBlendMode.DIVIDE; } else if (AppVariables.message("ColorBurnMode").equals(mode)) { return ImagesBlendMode.COLOR_BURN; } else if (AppVariables.message("LinearBurnMode").equals(mode)) { return ImagesBlendMode.LINEAR_BURN; } else if (AppVariables.message("VividLightMode").equals(mode)) { return ImagesBlendMode.VIVID_LIGHT; } else if (AppVariables.message("LinearLightMode").equals(mode)) { return ImagesBlendMode.LINEAR_LIGHT; } else if (AppVariables.message("SubtractMode").equals(mode)) { return ImagesBlendMode.SUBTRACT; } else if (AppVariables.message("DifferenceMode").equals(mode)) { return ImagesBlendMode.DIFFERENCE; } else if (AppVariables.message("ExclusionMode").equals(mode)) { return ImagesBlendMode.EXCLUSION; } else if (AppVariables.message("DarkenMode").equals(mode)) { return ImagesBlendMode.DARKEN; } else if (AppVariables.message("LightenMode").equals(mode)) { return ImagesBlendMode.LIGHTEN; } else if (AppVariables.message("HueMode").equals(mode)) { return ImagesBlendMode.HUE; } else if (AppVariables.message("SaturationMode").equals(mode)) { return ImagesBlendMode.SATURATION; } else if (AppVariables.message("ColorMode").equals(mode)) { return ImagesBlendMode.COLOR; } else if (AppVariables.message("LuminosityMode").equals(mode)) { return ImagesBlendMode.LUMINOSITY; } else { return ImagesBlendMode.NORMAL; } } protected int blend(int forePixel, int backPixel) { if (forePixel == 0) { // Pass transparency return backPixel; } if (backPixel == 0) { // Pass transparency return forePixel; } foreColor = new Color(forePixel); backColor = new Color(backPixel); makeRGB(); Color newColor = new Color( Math.min(Math.max(red, 0), 255), Math.min(Math.max(green, 0), 255), Math.min(Math.max(blue, 0), 255), Math.min(foreColor.getAlpha() + backColor.getAlpha(), 255)); return newColor.getRGB(); } protected void makeRGB() { red = (int) (foreColor.getRed() * alpha + backColor.getRed() * (1.0f - alpha)); green = (int) (foreColor.getGreen() * alpha + backColor.getGreen() * (1.0f - alpha)); blue = (int) (foreColor.getBlue() * alpha + backColor.getBlue() * (1.0f - alpha)); } public static PixelBlend newColorBlend(ImagesBlendMode blendMode, float alpha) { switch (blendMode) { case NORMAL: return new NormalBlend(alpha); case DISSOLVE: return new DissolveBlend(); case MULTIPLY: return new MultiplyBlend(); case SCREEN: return new ScreenBlend(); case OVERLAY: return new OverlayBlend(); case HARD_LIGHT: return new HardLightBlend(); case SOFT_LIGHT: return new SoftLightBlend(); case COLOR_DODGE: return new ColorDodgeBlend(); case LINEAR_DODGE: return new LinearDodgeBlend(); case DIVIDE: return new DivideBlend(); case COLOR_BURN: return new ColorBurnBlend(); case LINEAR_BURN: return new LinearBurnBlend(); case VIVID_LIGHT: return new VividLightBlend(); case LINEAR_LIGHT: return new LinearLightBlend(); case SUBTRACT: return new SubtractBlend(); case DIFFERENCE: return new DifferenceBlend(); case EXCLUSION: return new ExclusionBlend(); case DARKEN: return new DarkenBlend(); case LIGHTEN: return new LightenBlend(); case HUE: return new HueBlend(); case SATURATION: return new SaturationBlend(); case LUMINOSITY: return new LuminosityBlend(); case COLOR: return new ColorBlend(); default: return new NormalBlend(alpha); } } public static class NormalBlend extends PixelBlend { public NormalBlend(float alpha) { this.blendMode = ImagesBlendMode.NORMAL; this.alpha = alpha; } } public static class DissolveBlend extends PixelBlend { public DissolveBlend() { this.blendMode = ImagesBlendMode.DISSOLVE; } @Override protected void makeRGB() { float opacity = new Random().nextInt(101) / 100.0f; red = (int) (foreColor.getRed() * opacity + backColor.getRed() * (1.0f - opacity)); green = (int) (foreColor.getGreen() * opacity + backColor.getGreen() * (1.0f - opacity)); blue = (int) (foreColor.getBlue() * opacity + backColor.getBlue() * (1.0f - opacity)); } } public static class MultiplyBlend extends PixelBlend { public MultiplyBlend() { this.blendMode = ImagesBlendMode.MULTIPLY; } @Override protected void makeRGB() { red = foreColor.getRed() * backColor.getRed() / 255; green = foreColor.getGreen() * backColor.getGreen() / 255; blue = foreColor.getBlue() * backColor.getBlue() / 255; } } public static class ScreenBlend extends PixelBlend { public ScreenBlend() { this.blendMode = ImagesBlendMode.SCREEN; } @Override protected void makeRGB() { red = 255 - (255 - foreColor.getRed()) * (255 - backColor.getRed()) / 255; green = 255 - (255 - foreColor.getGreen()) * (255 - backColor.getGreen()) / 255; blue = 255 - (255 - foreColor.getBlue()) * (255 - backColor.getBlue()) / 255; } } public static class OverlayBlend extends PixelBlend { public OverlayBlend() { this.blendMode = ImagesBlendMode.OVERLAY; } @Override protected void makeRGB() { if (backColor.getRed() < 128) { red = foreColor.getRed() * backColor.getRed() / 128; } else { red = 255 - (255 - foreColor.getRed()) * (255 - backColor.getRed()) / 128; } if (backColor.getGreen() < 128) { green = foreColor.getGreen() * backColor.getGreen() / 128; } else { green = 255 - (255 - foreColor.getGreen()) * (255 - backColor.getGreen()) / 128; } if (backColor.getBlue() < 128) { blue = foreColor.getBlue() * backColor.getBlue() / 128; } else { blue = 255 - (255 - foreColor.getBlue()) * (255 - backColor.getBlue()) / 128; } } } public static class HardLightBlend extends PixelBlend { public HardLightBlend() { this.blendMode = ImagesBlendMode.HARD_LIGHT; } @Override protected void makeRGB() { if (foreColor.getRed() < 128) { red = foreColor.getRed() * backColor.getRed() / 128; } else { red = 255 - (255 - foreColor.getRed()) * (255 - backColor.getRed()) / 128; } if (foreColor.getGreen() < 128) { green = foreColor.getGreen() * backColor.getGreen() / 128; } else { green = 255 - (255 - foreColor.getGreen()) * (255 - backColor.getGreen()) / 128; } if (foreColor.getBlue() < 128) { blue = foreColor.getBlue() * backColor.getBlue() / 128; } else { blue = 255 - (255 - foreColor.getBlue()) * (255 - backColor.getBlue()) / 128; } } } public static class SoftLightBlend extends PixelBlend { public SoftLightBlend() { this.blendMode = ImagesBlendMode.SOFT_LIGHT; } @Override protected void makeRGB() { if (foreColor.getRed() < 128) { red = backColor.getRed() + (2 * foreColor.getRed() - 255) * (backColor.getRed() - backColor.getRed() * backColor.getRed() / 255) / 255; } else { red = (int) (backColor.getRed() + (2 * foreColor.getRed() - 255) * (Math.sqrt(backColor.getRed() / 255.0f) * 255 - backColor.getRed()) / 255); } if (foreColor.getGreen() < 128) { green = backColor.getGreen() + (2 * foreColor.getGreen() - 255) * (backColor.getGreen() - backColor.getGreen() * backColor.getGreen() / 255) / 255; } else { green = (int) (backColor.getGreen() + (2 * foreColor.getGreen() - 255) * (Math.sqrt(backColor.getGreen() / 255.0f) * 255 - backColor.getGreen()) / 255); } if (foreColor.getBlue() < 128) { blue = backColor.getBlue() + (2 * foreColor.getBlue() - 255) * (backColor.getBlue() - backColor.getBlue() * backColor.getBlue() / 255) / 255; } else { blue = (int) (backColor.getBlue() + (2 * foreColor.getBlue() - 255) * (Math.sqrt(backColor.getBlue() / 255.0f) * 255 - backColor.getBlue()) / 255); } } } public static class ColorDodgeBlend extends PixelBlend { public ColorDodgeBlend() { this.blendMode = ImagesBlendMode.COLOR_DODGE; } @Override protected void makeRGB() { red = foreColor.getRed() == 255 ? 255 : (backColor.getRed() + (foreColor.getRed() * backColor.getRed()) / (255 - foreColor.getRed())); green = foreColor.getGreen() == 255 ? 255 : (backColor.getGreen() + (foreColor.getGreen() * backColor.getGreen()) / (255 - foreColor.getGreen())); blue = foreColor.getBlue() == 255 ? 255 : (backColor.getBlue() + (foreColor.getBlue() * backColor.getBlue()) / (255 - foreColor.getBlue())); } } public static class LinearDodgeBlend extends PixelBlend { public LinearDodgeBlend() { this.blendMode = ImagesBlendMode.LINEAR_DODGE; } @Override protected void makeRGB() { red = foreColor.getRed() + backColor.getRed(); green = foreColor.getGreen() + backColor.getGreen(); blue = foreColor.getBlue() + backColor.getBlue(); } } public static class DivideBlend extends PixelBlend { public DivideBlend() { this.blendMode = ImagesBlendMode.DIVIDE; } @Override protected void makeRGB() { red = foreColor.getRed() == 0 ? 255 : ((backColor.getRed() * 255) / foreColor.getRed()); green = foreColor.getGreen() == 0 ? 255 : ((backColor.getGreen() * 255) / foreColor.getGreen()); blue = foreColor.getBlue() == 0 ? 255 : ((backColor.getBlue() * 255) / foreColor.getBlue()); } } public static class ColorBurnBlend extends PixelBlend { public ColorBurnBlend() { this.blendMode = ImagesBlendMode.COLOR_BURN; } @Override protected void makeRGB() { red = foreColor.getRed() == 0 ? 0 : (backColor.getRed() - (255 - foreColor.getRed()) * 255 / foreColor.getRed()); green = foreColor.getGreen() == 0 ? 0 : (backColor.getGreen() - (255 - foreColor.getGreen()) * 255 / foreColor.getGreen()); blue = foreColor.getBlue() == 0 ? 0 : (backColor.getBlue() - (255 - foreColor.getBlue()) * 255 / foreColor.getBlue()); } } public static class LinearBurnBlend extends PixelBlend { public LinearBurnBlend() { this.blendMode = ImagesBlendMode.LINEAR_BURN; } @Override protected void makeRGB() { red = backColor.getRed() == 0 ? 0 : foreColor.getRed() + backColor.getRed() - 255; green = backColor.getGreen() == 0 ? 0 : foreColor.getGreen() + backColor.getGreen() - 255; blue = backColor.getBlue() == 0 ? 0 : foreColor.getBlue() + backColor.getBlue() - 255; } } public static class VividLightBlend extends PixelBlend { public VividLightBlend() { this.blendMode = ImagesBlendMode.VIVID_LIGHT; } @Override protected void makeRGB() { if (foreColor.getRed() < 128) { red = foreColor.getRed() == 0 ? backColor.getRed() : (backColor.getRed() - (255 - backColor.getRed()) * (255 - 2 * foreColor.getRed()) / (2 * foreColor.getRed())); } else { red = foreColor.getRed() == 255 ? backColor.getRed() : (backColor.getRed() + backColor.getRed() * (2 * foreColor.getRed() - 255) / (2 * (255 - foreColor.getRed()))); } if (foreColor.getGreen() < 128) { green = foreColor.getGreen() == 0 ? backColor.getGreen() : (backColor.getGreen() - (255 - backColor.getGreen()) * (255 - 2 * foreColor.getGreen()) / (2 * foreColor.getGreen())); } else { green = foreColor.getGreen() == 255 ? backColor.getGreen() : (backColor.getGreen() + backColor.getGreen() * (2 * foreColor.getGreen() - 255) / (2 * (255 - foreColor.getGreen()))); } if (foreColor.getBlue() < 128) { blue = foreColor.getBlue() == 0 ? backColor.getBlue() : (backColor.getBlue() - (255 - backColor.getBlue()) * (255 - 2 * foreColor.getBlue()) / (2 * foreColor.getBlue())); } else { blue = foreColor.getBlue() == 255 ? backColor.getBlue() : (backColor.getBlue() + backColor.getBlue() * (2 * foreColor.getBlue() - 255) / (2 * (255 - foreColor.getBlue()))); } } } public static class LinearLightBlend extends PixelBlend { public LinearLightBlend() { this.blendMode = ImagesBlendMode.LINEAR_LIGHT; } @Override protected void makeRGB() { red = 2 * foreColor.getRed() + backColor.getRed() - 255; green = 2 * foreColor.getGreen() + backColor.getGreen() - 255; blue = 2 * foreColor.getBlue() + backColor.getBlue() - 255; } } public static class SubtractBlend extends PixelBlend { public SubtractBlend() { this.blendMode = ImagesBlendMode.SUBTRACT; } @Override protected void makeRGB() { red = backColor.getRed() - foreColor.getRed(); green = backColor.getGreen() - foreColor.getGreen(); blue = backColor.getBlue() - foreColor.getBlue(); } } public static class DifferenceBlend extends PixelBlend { public DifferenceBlend() { this.blendMode = ImagesBlendMode.DIFFERENCE; } @Override protected void makeRGB() { red = Math.abs(backColor.getRed() - foreColor.getRed()); green = Math.abs(backColor.getGreen() - foreColor.getGreen()); blue = Math.abs(backColor.getBlue() - foreColor.getBlue()); } } public static class ExclusionBlend extends PixelBlend { public ExclusionBlend() { this.blendMode = ImagesBlendMode.EXCLUSION; } @Override protected void makeRGB() { red = backColor.getRed() + foreColor.getRed() - backColor.getRed() * foreColor.getRed() / 128; green = backColor.getGreen() + foreColor.getGreen() - backColor.getGreen() * foreColor.getGreen() / 128; blue = backColor.getBlue() + foreColor.getBlue() - backColor.getBlue() * foreColor.getBlue() / 128; } } public static class DarkenBlend extends PixelBlend { public DarkenBlend() { this.blendMode = ImagesBlendMode.DARKEN; } @Override protected void makeRGB() { red = Math.min(backColor.getRed(), foreColor.getRed()); green = Math.min(backColor.getGreen(), foreColor.getGreen()); blue = Math.min(backColor.getBlue(), foreColor.getBlue()); } } public static class LightenBlend extends PixelBlend { public LightenBlend() { this.blendMode = ImagesBlendMode.LIGHTEN; } @Override protected void makeRGB() { red = Math.max(backColor.getRed(), foreColor.getRed()); green = Math.max(backColor.getGreen(), foreColor.getGreen()); blue = Math.max(backColor.getBlue(), foreColor.getBlue()); } } public static class HueBlend extends PixelBlend { public HueBlend() { this.blendMode = ImagesBlendMode.HUE; } @Override protected int blend(int forePixel, int backPixel) { if (forePixel == 0) { // Pass transparency return backPixel; } if (backPixel == 0) { // Pass transparency return forePixel; } float[] hA = ImageColor.pixel2HSB(forePixel); float[] hB = ImageColor.pixel2HSB(backPixel); Color hColor = Color.getHSBColor(hA[0], hB[1], hB[2]); return hColor.getRGB(); } } public static class SaturationBlend extends PixelBlend { public SaturationBlend() { this.blendMode = ImagesBlendMode.SATURATION; } @Override protected int blend(int forePixel, int backPixel) { if (forePixel == 0) { // Pass transparency return backPixel; } if (backPixel == 0) { // Pass transparency return forePixel; } float[] sA = ImageColor.pixel2HSB(forePixel); float[] sB = ImageColor.pixel2HSB(backPixel); Color sColor = Color.getHSBColor(sB[0], sA[1], sB[2]); return sColor.getRGB(); } } public static class LuminosityBlend extends PixelBlend { public LuminosityBlend() { this.blendMode = ImagesBlendMode.LUMINOSITY; } @Override protected int blend(int forePixel, int backPixel) { if (forePixel == 0) { // Pass transparency return backPixel; } if (backPixel == 0) { // Pass transparency return forePixel; } float[] bA = ImageColor.pixel2HSB(forePixel); float[] bB = ImageColor.pixel2HSB(backPixel); Color newColor = Color.getHSBColor(bB[0], bB[1], bA[2]); return newColor.getRGB(); } } public static class ColorBlend extends PixelBlend { public ColorBlend() { this.blendMode = ImagesBlendMode.COLOR; } @Override protected int blend(int forePixel, int backPixel) { if (forePixel == 0) { // Pass transparency return backPixel; } if (backPixel == 0) { // Pass transparency return forePixel; } float[] cA = ImageColor.pixel2HSB(forePixel); float[] cB = ImageColor.pixel2HSB(backPixel); Color cColor = Color.getHSBColor(cA[0], cA[1], cB[2]); return cColor.getRGB(); } } }