13dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta/*
23dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * Copyright (C) 2014 The Android Open Source Project
33dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta *
43dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
53dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * you may not use this file except in compliance with the License.
63dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * You may obtain a copy of the License at
73dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta *
83dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
93dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta *
103dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software
113dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
123dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * See the License for the specific language governing permissions and
143dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * limitations under the License.
153dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta */
163dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
173dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptapackage android.graphics;
183dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
193dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptaimport java.awt.Composite;
203dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptaimport java.awt.CompositeContext;
213dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptaimport java.awt.RenderingHints;
223dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptaimport java.awt.image.ColorModel;
233dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptaimport java.awt.image.DataBuffer;
243dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptaimport java.awt.image.Raster;
253dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptaimport java.awt.image.WritableRaster;
263dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
273dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta/*
283dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * (non-Javadoc)
293dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * The class is adapted from a demo tool for Blending Modes written by
303dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * Romain Guy (romainguy@android.com). The tool is available at
313dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
321a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta *
331a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta * This class has been adapted for applying color filters. When applying color filters, the src
341a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta * image should not extend beyond the dest image, but in our implementation of the filters, it does.
351a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta * To compensate for the effect, we recompute the alpha value of the src image before applying
361a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta * the color filter as it should have been applied.
373dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta */
383dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Guptapublic final class BlendComposite implements Composite {
393dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public enum BlendingMode {
405521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        MULTIPLY(),
415521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        SCREEN(),
425521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        DARKEN(),
435521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        LIGHTEN(),
445521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        OVERLAY(),
455521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        ADD();
461a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta
475521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        private final BlendComposite mComposite;
481a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta
495521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta        BlendingMode() {
505521f65ecb0b91a6847bd78afc4e8c927b6b78efDeepanshu Gupta            mComposite = new BlendComposite(this);
511a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta        }
521a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta
531a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta        BlendComposite getBlendComposite() {
541a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta            return mComposite;
551a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta        }
563dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
573dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
583dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    private float alpha;
593dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    private BlendingMode mode;
603dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
613dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    private BlendComposite(BlendingMode mode) {
623dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        this(mode, 1.0f);
633dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
643dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
653dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    private BlendComposite(BlendingMode mode, float alpha) {
663dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        this.mode = mode;
673dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        setAlpha(alpha);
683dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
693dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
703dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public static BlendComposite getInstance(BlendingMode mode) {
711a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta        return mode.getBlendComposite();
723dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
733dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
743dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public static BlendComposite getInstance(BlendingMode mode, float alpha) {
751a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta        if (alpha > 0.9999f) {
761a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta            return getInstance(mode);
771a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta        }
783dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        return new BlendComposite(mode, alpha);
793dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
803dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
813dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public float getAlpha() {
823dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        return alpha;
833dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
843dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
853dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public BlendingMode getMode() {
863dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        return mode;
873dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
883dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
893dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    private void setAlpha(float alpha) {
903dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        if (alpha < 0.0f || alpha > 1.0f) {
913dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            throw new IllegalArgumentException(
923dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    "alpha must be comprised between 0.0f and 1.0f");
933dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        }
943dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
953dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        this.alpha = alpha;
963dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
973dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
983dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    @Override
993dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public int hashCode() {
1003dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
1013dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
1023dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1033dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    @Override
1043dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public boolean equals(Object obj) {
1053dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        if (!(obj instanceof BlendComposite)) {
1063dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            return false;
1073dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        }
1083dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1093dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        BlendComposite bc = (BlendComposite) obj;
1103dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1111a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta        return mode == bc.mode && alpha == bc.alpha;
1123dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
1133dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1143dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    public CompositeContext createContext(ColorModel srcColorModel,
1153dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                                          ColorModel dstColorModel,
1163dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                                          RenderingHints hints) {
1173dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        return new BlendingContext(this);
1183dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
1193dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1203dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    private static final class BlendingContext implements CompositeContext {
1213dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        private final Blender blender;
1223dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        private final BlendComposite composite;
1233dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1243dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        private BlendingContext(BlendComposite composite) {
1253dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            this.composite = composite;
1263dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            this.blender = Blender.getBlenderFor(composite);
1273dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        }
1283dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1293dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        public void dispose() {
1303dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        }
1313dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1323dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
1333dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
1343dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
1353dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
1363dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                throw new IllegalStateException(
1373dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        "Source and destination must store pixels as INT.");
1383dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            }
1393dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1403dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            int width = Math.min(src.getWidth(), dstIn.getWidth());
1413dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            int height = Math.min(src.getHeight(), dstIn.getHeight());
1423dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1433dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            float alpha = composite.getAlpha();
1443dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1453dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            int[] srcPixel = new int[4];
1463dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            int[] dstPixel = new int[4];
14747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta            int[] result = new int[4];
1483dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            int[] srcPixels = new int[width];
1493dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            int[] dstPixels = new int[width];
1503dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1513dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            for (int y = 0; y < height; y++) {
1523dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                dstIn.getDataElements(0, y, width, 1, dstPixels);
15347fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                if (alpha != 0) {
15447fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                    src.getDataElements(0, y, width, 1, srcPixels);
15547fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                    for (int x = 0; x < width; x++) {
15647fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        // pixels are stored as INT_ARGB
15747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        // our arrays are [R, G, B, A]
15847fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        int pixel = srcPixels[x];
15947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        srcPixel[0] = (pixel >> 16) & 0xFF;
16047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        srcPixel[1] = (pixel >>  8) & 0xFF;
16147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        srcPixel[2] = (pixel      ) & 0xFF;
16247fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        srcPixel[3] = (pixel >> 24) & 0xFF;
16347fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta
16447fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        pixel = dstPixels[x];
16547fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        dstPixel[0] = (pixel >> 16) & 0xFF;
16647fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        dstPixel[1] = (pixel >>  8) & 0xFF;
16747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        dstPixel[2] = (pixel      ) & 0xFF;
16847fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        dstPixel[3] = (pixel >> 24) & 0xFF;
16947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta
1701a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                        // ---- Modified from original ----
1711a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                        // recompute src pixel for transparency.
1721a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                        srcPixel[3] *= dstPixel[3] / 0xFF;
1731a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                        // ---- Modification ends ----
1741a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta
17547fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        result = blender.blend(srcPixel, dstPixel, result);
17647fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta
17747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        // mixes the result with the opacity
17847fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        if (alpha == 1) {
17947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            dstPixels[x] = (result[3] & 0xFF) << 24 |
18047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                           (result[0] & 0xFF) << 16 |
18147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                           (result[1] & 0xFF) <<  8 |
18247fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                           result[2] & 0xFF;
18347fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        } else {
18447fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            dstPixels[x] =
18547fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                    ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
18647fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                    ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
18747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                    ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
1883dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                                    (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
18947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        }
19047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta
19147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                    }
19247fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta            }
1933dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                dstOut.setDataElements(0, y, width, 1, dstPixels);
1943dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            }
1953dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        }
1963dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
1973dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
1983dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    private static abstract class Blender {
19947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta        public abstract int[] blend(int[] src, int[] dst, int[] result);
2003dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta
2013dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        public static Blender getBlenderFor(BlendComposite composite) {
2023dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            switch (composite.getMode()) {
2033dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                case ADD:
2043dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    return new Blender() {
2053dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        @Override
20647fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
20747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            for (int i = 0; i < 4; i++) {
20847fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                result[i] = Math.min(255, src[i] + dst[i]);
20947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            }
21047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            return result;
2113dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        }
2123dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    };
2133dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                case DARKEN:
2143dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    return new Blender() {
2153dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        @Override
21647fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
21747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            for (int i = 0; i < 3; i++) {
21847fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                result[i] = Math.min(src[i], dst[i]);
21947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            }
22047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
22147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            return result;
2223dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        }
2233dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    };
2243dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                case LIGHTEN:
2253dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    return new Blender() {
2263dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        @Override
22747fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
22847fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            for (int i = 0; i < 3; i++) {
22947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                result[i] = Math.max(src[i], dst[i]);
23047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            }
23147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
23247fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            return result;
2333dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        }
2343dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    };
2353dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                case MULTIPLY:
2363dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    return new Blender() {
2373dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        @Override
23847fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
23947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            for (int i = 0; i < 3; i++) {
24047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                result[i] = (src[i] * dst[i]) >> 8;
24147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            }
2421a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
24347fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            return result;
2443dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        }
2453dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    };
2463dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                case OVERLAY:
2473dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    return new Blender() {
2483dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        @Override
24947fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
25047fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            for (int i = 0; i < 3; i++) {
25147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
25247fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                                    255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
25347fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            }
25447fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
25547fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                            return result;
2563dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        }
2573dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    };
2581a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                case SCREEN:
2593dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    return new Blender() {
2603dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        @Override
26147fa5c920d6eb93e435794544b96a0e4ede4403aDeepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
2621a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                            result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
2631a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                            result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
2641a10ca7e526736b4fd143f7c9f3b29643c0062a4Deepanshu Gupta                            result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
2653dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
2663dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                            return result;
2673dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                        }
2683dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                    };
2693dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            }
2703dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta            throw new IllegalArgumentException("Blender not implement for " +
2713dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta                                               composite.getMode().name());
2723dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta        }
2733dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta    }
2743dfc1c21d58a7a6764a436cbf5c3c8ba09db45e5Deepanshu Gupta}
275