/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2011 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jwildfire.transform; import java.awt.Color; import org.jwildfire.base.Property; import org.jwildfire.base.PropertyCategory; import org.jwildfire.image.Pixel; import org.jwildfire.image.SimpleImage; import org.jwildfire.image.WFImage; import org.jwildfire.swing.Buffer; import org.jwildfire.swing.NonHDRImageBufferComboBoxEditor; import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor; public class AlphaComposeTransformer extends Mesh2DTransformer { public enum HAlignment { OFF, CENTRE, LEFT, RIGHT } public enum VAlignment { OFF, CENTRE, TOP, BOTTOM } public enum Genlock { NONE, COLOR, IN_RANGE, OUT_RANGE } @Property(category = PropertyCategory.PRIMARY, description = "Image to put in foreground (background image is received from the input channel)", editorClass = NonHDRImageBufferComboBoxEditor.class) private Buffer foreground; private SimpleImage foregroundImage; // Alternative way to specify the foreground image directly @Property(category = PropertyCategory.PRIMARY, description = "Image which holds the alpha channel information", editorClass = NonHDRImageBufferComboBoxEditor.class) private Buffer alphaChannel; private SimpleImage alphaChannelImage; // Alternative way to specify the foreground image directly @Property(category = PropertyCategory.SECONDARY, description = "Left offset of the foreground image") private int fgLeft = 0; @Property(category = PropertyCategory.SECONDARY, description = "Top offset of the foreground image") private int fgTop = 0; @Property(category = PropertyCategory.SECONDARY, description = "Left offset of the alpha image") private int alphaLeft = 0; @Property(category = PropertyCategory.SECONDARY, description = "Top offset of the alpha image") private int alphaTop = 0; @Property(category = PropertyCategory.PRIMARY, description = "Horizontal alignment of the foreground image") private HAlignment fgHAlign = HAlignment.CENTRE; @Property(category = PropertyCategory.PRIMARY, description = "Vertical alignment of the foreground image") private VAlignment fgVAlign = VAlignment.CENTRE; @Property(category = PropertyCategory.PRIMARY, description = "Horizontal alignment of the alpha image") private HAlignment alphaHAlign = HAlignment.CENTRE; @Property(category = PropertyCategory.PRIMARY, description = "Vertical alignment of the alpha image") private VAlignment alphaVAlign = VAlignment.CENTRE; @Property(category = PropertyCategory.SECONDARY, description = "Genlock mode", editorClass = GenlockEditor.class) private Genlock genlock = Genlock.NONE; @Property(category = PropertyCategory.SECONDARY, description = "Genlock color A") protected Color colorA = new Color(0, 0, 0); @Property(category = PropertyCategory.SECONDARY, description = "Genlock color B") protected Color colorB = new Color(0, 0, 0); @Override protected void performPixelTransformation(WFImage pImg) { SimpleImage bImg = (SimpleImage) pImg; SimpleImage fImg = (foregroundImage != null) ? foregroundImage : foreground.getImage(); if (fImg == bImg) fImg = fImg.clone(); SimpleImage aImg = (alphaChannelImage != null) ? alphaChannelImage : alphaChannel.getImage(); if (aImg == bImg) aImg = aImg.clone(); Pixel hPixel = new Pixel(); Pixel bPixel = new Pixel(); // calculate left and top edge int left, top; if (this.fgHAlign == HAlignment.CENTRE) { left = (bImg.getImageWidth() - fImg.getImageWidth()) / 2; } else if (this.fgHAlign == HAlignment.LEFT) { left = 0; } else if (this.fgHAlign == HAlignment.RIGHT) { left = bImg.getImageWidth() - fImg.getImageWidth(); } else { left = this.fgLeft; } if (this.fgVAlign == VAlignment.CENTRE) { top = (bImg.getImageHeight() - fImg.getImageHeight()) / 2; } else if (this.fgVAlign == VAlignment.TOP) { top = 0; } else if (this.fgVAlign == VAlignment.BOTTOM) { top = bImg.getImageHeight() - fImg.getImageHeight(); } else { top = this.fgTop; } // // calculate alhpa left and top edge int aleft, atop; if (this.alphaHAlign == HAlignment.CENTRE) { aleft = (bImg.getImageWidth() - aImg.getImageWidth()) / 2; } else if (this.alphaHAlign == HAlignment.LEFT) { aleft = 0; } else if (this.alphaHAlign == HAlignment.RIGHT) { aleft = bImg.getImageWidth() - aImg.getImageWidth(); } else { aleft = this.alphaLeft; } if (this.alphaVAlign == VAlignment.CENTRE) { atop = (bImg.getImageHeight() - aImg.getImageHeight()) / 2; } else if (this.alphaVAlign == VAlignment.TOP) { atop = 0; } else if (this.alphaVAlign == VAlignment.BOTTOM) { atop = bImg.getImageHeight() - aImg.getImageHeight(); } else { atop = this.alphaTop; } // calculate affected region int hsize = 0, vsize = 0; int bgleft = 0, bgtop = 0; int sleft = 0, stop = 0; int swidth = fImg.getImageWidth(); int sheight = fImg.getImageHeight(); int bgwidth = bImg.getImageWidth(); int bgheight = bImg.getImageHeight(); /* case 1 */ if ((left >= 0) && (top >= 0)) { if ((left >= bgwidth) || (top >= bgheight)) return; hsize = bgwidth - left; if (hsize > swidth) hsize = swidth; vsize = bgheight - top; if (vsize > sheight) vsize = sheight; bgtop = top; bgleft = left; sleft = 0; stop = 0; } /* case 2 */ else if ((left < 0) && (top >= 0)) { if ((left <= (0 - swidth)) || (top >= bgheight)) return; hsize = swidth + left; if (hsize > bgwidth) hsize = bgwidth; vsize = bgheight - top; if (vsize > sheight) vsize = sheight; bgtop = top; bgleft = 0; sleft = 0 - left; stop = 0; } /* case 3 */ else if ((left >= 0) && (top < 0)) { if ((left >= bgwidth) || (top <= (0 - sheight))) return; hsize = bgwidth - left; if (hsize > swidth) hsize = swidth; vsize = sheight + top; if (vsize > bgheight) vsize = bgheight; bgtop = 0; bgleft = left; stop = 0 - top; sleft = 0; } /* case 4 */ else if ((left < 0) && (top < 0)) { if ((left <= (0 - swidth)) || (top <= (0 - sheight))) return; hsize = swidth + left; if (hsize > bgwidth) hsize = bgwidth; vsize = sheight + top; if (vsize > bgheight) vsize = bgheight; bgtop = 0; bgleft = 0; stop = 0 - top; sleft = 0 - left; } // Genlock colors int credA = this.colorA.getRed(); int cgreenA = this.colorA.getGreen(); int cblueA = this.colorA.getBlue(); int credB = this.colorB.getRed(); int cgreenB = this.colorB.getGreen(); int cblueB = this.colorB.getBlue(); { int tc; if (credA > credB) { tc = credA; credA = credB; credB = tc; } if (cgreenA > cgreenB) { tc = cgreenA; cgreenA = cgreenB; cgreenB = tc; } if (cblueA > cblueB) { tc = cblueA; cblueA = cblueB; cblueB = tc; } } // if (this.genlock == Genlock.NONE) { for (int i = 0; i < vsize; i++) { for (int j = 0; j < hsize; j++) { hPixel.setARGBValue(fImg.getARGBValue(sleft + j, stop + i)); int bgX = bgleft + j; int bgY = bgtop + i; int aX = bgX - aleft; int aY = bgY - atop; int mix = aImg.getRValueIgnoreBounds(aX, aY); int m1 = 255 - mix; int m2 = mix; bPixel.setARGBValue(bImg.getARGBValue(bgX, bgY)); int r = ((int) ((int) bPixel.r * m1) + (int) ((int) hPixel.r) * m2) / (int) 255; int g = ((int) ((int) bPixel.g * m1) + (int) ((int) hPixel.g) * m2) / (int) 255; int b = ((int) ((int) bPixel.b * m1) + (int) ((int) hPixel.b) * m2) / (int) 255; bImg.setRGB(bgX, bgY, r, g, b); } } } else if (this.genlock == Genlock.COLOR) { for (int i = 0; i < vsize; i++) { for (int j = 0; j < hsize; j++) { hPixel.setARGBValue(fImg.getARGBValue(sleft + j, stop + i)); if ((hPixel.r != credA) || (hPixel.g != cgreenA) || (hPixel.b != cblueA)) { int bgX = bgleft + j; int bgY = bgtop + i; int aX = bgX - aleft; int aY = bgY - atop; int mix = aImg.getRValueIgnoreBounds(aX, aY); int m1 = 255 - mix; int m2 = mix; bPixel.setARGBValue(bImg.getARGBValue(bgX, bgY)); int r = ((int) ((int) bPixel.r * m1) + (int) ((int) hPixel.r) * m2) / (int) 255; int g = ((int) ((int) bPixel.g * m1) + (int) ((int) hPixel.g) * m2) / (int) 255; int b = ((int) ((int) bPixel.b * m1) + (int) ((int) hPixel.b) * m2) / (int) 255; bImg.setRGB(bgX, bgY, r, g, b); } } } } else if (this.genlock == Genlock.IN_RANGE) { for (int i = 0; i < vsize; i++) { for (int j = 0; j < hsize; j++) { hPixel.setARGBValue(fImg.getARGBValue(sleft + j, stop + i)); if (((hPixel.r < credA) || (hPixel.r > credB)) && ((hPixel.g < cgreenA) || (hPixel.g > cgreenB)) && ((hPixel.b < cblueA) || (hPixel.b > cblueB))) { int bgX = bgleft + j; int bgY = bgtop + i; int aX = bgX - aleft; int aY = bgY - atop; int mix = aImg.getRValueIgnoreBounds(aX, aY); int m1 = 255 - mix; int m2 = mix; bPixel.setARGBValue(bImg.getARGBValue(bgX, bgY)); int r = ((int) ((int) bPixel.r * m1) + (int) ((int) hPixel.r) * m2) / (int) 255; int g = ((int) ((int) bPixel.g * m1) + (int) ((int) hPixel.g) * m2) / (int) 255; int b = ((int) ((int) bPixel.b * m1) + (int) ((int) hPixel.b) * m2) / (int) 255; bImg.setRGB(bgX, bgY, r, g, b); } } } } else if (this.genlock == Genlock.OUT_RANGE) { for (int i = 0; i < vsize; i++) { for (int j = 0; j < hsize; j++) { hPixel.setARGBValue(fImg.getARGBValue(sleft + j, stop + i)); if (((hPixel.r >= credA) && (hPixel.r <= credB)) && ((hPixel.g >= cgreenA) && (hPixel.g <= cgreenB)) && ((hPixel.b >= cblueA) && (hPixel.b <= cblueB))) { int bgX = bgleft + j; int bgY = bgtop + i; int aX = bgX - aleft; int aY = bgY - atop; int mix = aImg.getRValueIgnoreBounds(aX, aY); int m1 = 255 - mix; int m2 = mix; bPixel.setARGBValue(bImg.getARGBValue(bgX, bgY)); int r = ((int) ((int) bPixel.r * m1) + (int) ((int) hPixel.r) * m2) / (int) 255; int g = ((int) ((int) bPixel.g * m1) + (int) ((int) hPixel.g) * m2) / (int) 255; int b = ((int) ((int) bPixel.b * m1) + (int) ((int) hPixel.b) * m2) / (int) 255; bImg.setRGB(bgX, bgY, r, g, b); } } } } } @Override public void initDefaultParams(WFImage pImg) { fgLeft = 0; fgTop = 0; fgHAlign = HAlignment.CENTRE; fgVAlign = VAlignment.CENTRE; alphaLeft = 0; alphaTop = 0; alphaHAlign = HAlignment.CENTRE; alphaVAlign = VAlignment.CENTRE; genlock = Genlock.NONE; colorA = new Color(0, 0, 0); colorB = new Color(0, 0, 0); } public static class HAlignmentEditor extends ComboBoxPropertyEditor { public HAlignmentEditor() { super(); setAvailableValues(new HAlignment[] { HAlignment.OFF, HAlignment.CENTRE, HAlignment.LEFT, HAlignment.RIGHT }); } } public static class VAlignmentEditor extends ComboBoxPropertyEditor { public VAlignmentEditor() { super(); setAvailableValues(new VAlignment[] { VAlignment.OFF, VAlignment.CENTRE, VAlignment.TOP, VAlignment.BOTTOM }); } } public static class GenlockEditor extends ComboBoxPropertyEditor { public GenlockEditor() { super(); setAvailableValues(new Genlock[] { Genlock.NONE, Genlock.COLOR, Genlock.IN_RANGE, Genlock.OUT_RANGE }); } } public void setForegroundImage(SimpleImage pForegroundImage) { foregroundImage = pForegroundImage; } public void setAlphaChannelImage(SimpleImage pAlphaChannelImage) { alphaChannelImage = pAlphaChannelImage; } public Buffer getForeground() { return foreground; } public void setForeground(Buffer foreground) { this.foreground = foreground; } public Buffer getAlphaChannel() { return alphaChannel; } public void setAlphaChannel(Buffer alphaChannel) { this.alphaChannel = alphaChannel; } public int getFgLeft() { return fgLeft; } public void setFgLeft(int fgLeft) { this.fgLeft = fgLeft; } public int getFgTop() { return fgTop; } public void setFgTop(int fgTop) { this.fgTop = fgTop; } public int getAlphaLeft() { return alphaLeft; } public void setAlphaLeft(int alphaLeft) { this.alphaLeft = alphaLeft; } public int getAlphaTop() { return alphaTop; } public void setAlphaTop(int alphaTop) { this.alphaTop = alphaTop; } public HAlignment getFgHAlign() { return fgHAlign; } public void setFgHAlign(HAlignment fgHAlign) { this.fgHAlign = fgHAlign; } public VAlignment getFgVAlign() { return fgVAlign; } public void setFgVAlign(VAlignment fgVAlign) { this.fgVAlign = fgVAlign; } public HAlignment getAlphaHAlign() { return alphaHAlign; } public void setAlphaHAlign(HAlignment alphaHAlign) { this.alphaHAlign = alphaHAlign; } public VAlignment getAlphaVAlign() { return alphaVAlign; } public void setAlphaVAlign(VAlignment alphaVAlign) { this.alphaVAlign = alphaVAlign; } public Genlock getGenlock() { return genlock; } public void setGenlock(Genlock genlock) { this.genlock = genlock; } public Color getColorA() { return colorA; } public void setColorA(Color colorA) { this.colorA = colorA; } public Color getColorB() { return colorB; } public void setColorB(Color colorB) { this.colorB = colorB; } }