BlendComposite.java revision c0edf09b80fc3180e29632e208775e58a24e04fb
1f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta/*
2f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * Copyright (C) 2014 The Android Open Source Project
3f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta *
4f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
5f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * you may not use this file except in compliance with the License.
6f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * You may obtain a copy of the License at
7f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta *
8f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
9f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta *
10f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software
11f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
12f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * See the License for the specific language governing permissions and
14f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * limitations under the License.
15f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta */
16f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
17f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptapackage android.graphics;
18f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
19f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptaimport java.awt.Composite;
20f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptaimport java.awt.CompositeContext;
21f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptaimport java.awt.RenderingHints;
22f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptaimport java.awt.image.ColorModel;
23f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptaimport java.awt.image.DataBuffer;
24f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptaimport java.awt.image.Raster;
25f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptaimport java.awt.image.WritableRaster;
26f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
27f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta/*
28f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * (non-Javadoc)
29f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * The class is adapted from a demo tool for Blending Modes written by
30f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * Romain Guy (romainguy@android.com). The tool is available at
31f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
32c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta *
33c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta * This class has been adapted for applying color filters. When applying color filters, the src
34c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta * image should not extend beyond the dest image, but in our implementation of the filters, it does.
35c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta * To compensate for the effect, we recompute the alpha value of the src image before applying
36c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta * the color filter as it should have been applied.
37f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta */
38f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Guptapublic final class BlendComposite implements Composite {
39f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public enum BlendingMode {
40c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        MULTIPLY(Multiply),
41c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        SCREEN(Screen),
42c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        DARKEN(Darken),
43c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        LIGHTEN(Lighten),
44c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        OVERLAY(Overlay),
45c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        ADD(Add);
46c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
47c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        private BlendComposite mComposite;
48c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
49c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        BlendingMode(BlendComposite composite) {
50c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta            mComposite = composite;
51c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        }
52c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
53c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        BlendComposite getBlendComposite() {
54c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta            return mComposite;
55c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        }
56f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
57f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
58f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
59f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
60f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
61f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
62f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
63f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
64f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
65f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private float alpha;
66f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private BlendingMode mode;
67f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
68f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private BlendComposite(BlendingMode mode) {
69f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        this(mode, 1.0f);
70f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
71f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
72f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private BlendComposite(BlendingMode mode, float alpha) {
73f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        this.mode = mode;
74f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        setAlpha(alpha);
75f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
76f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
77f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static BlendComposite getInstance(BlendingMode mode) {
78c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        return mode.getBlendComposite();
79f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
80f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
81f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static BlendComposite getInstance(BlendingMode mode, float alpha) {
82c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        if (alpha > 0.9999f) {
83c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta            return getInstance(mode);
84c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        }
85f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return new BlendComposite(mode, alpha);
86f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
87f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
88f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public float getAlpha() {
89f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return alpha;
90f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
91f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
92f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public BlendingMode getMode() {
93f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return mode;
94f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
95f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
96f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private void setAlpha(float alpha) {
97f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        if (alpha < 0.0f || alpha > 1.0f) {
98f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            throw new IllegalArgumentException(
99f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    "alpha must be comprised between 0.0f and 1.0f");
100f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
101f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
102f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        this.alpha = alpha;
103f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
104f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
105f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    @Override
106f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public int hashCode() {
107f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
108f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
109f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
110f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    @Override
111f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public boolean equals(Object obj) {
112f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        if (!(obj instanceof BlendComposite)) {
113f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            return false;
114f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
115f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
116f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        BlendComposite bc = (BlendComposite) obj;
117f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
118c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        return mode == bc.mode && alpha == bc.alpha;
119f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
120f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
121f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public CompositeContext createContext(ColorModel srcColorModel,
122f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                                          ColorModel dstColorModel,
123f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                                          RenderingHints hints) {
124f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return new BlendingContext(this);
125f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
126f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
127f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private static final class BlendingContext implements CompositeContext {
128f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        private final Blender blender;
129f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        private final BlendComposite composite;
130f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
131f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        private BlendingContext(BlendComposite composite) {
132f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            this.composite = composite;
133f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            this.blender = Blender.getBlenderFor(composite);
134f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
135f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
136f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        public void dispose() {
137f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
138f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
139f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
140f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
141f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
142f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
143f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                throw new IllegalStateException(
144f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        "Source and destination must store pixels as INT.");
145f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            }
146f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
147f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int width = Math.min(src.getWidth(), dstIn.getWidth());
148f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int height = Math.min(src.getHeight(), dstIn.getHeight());
149f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
150f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            float alpha = composite.getAlpha();
151f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
152f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] srcPixel = new int[4];
153f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] dstPixel = new int[4];
154bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta            int[] result = new int[4];
155f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] srcPixels = new int[width];
156f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] dstPixels = new int[width];
157f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
158f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            for (int y = 0; y < height; y++) {
159f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstIn.getDataElements(0, y, width, 1, dstPixels);
160bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                if (alpha != 0) {
161bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                    src.getDataElements(0, y, width, 1, srcPixels);
162bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                    for (int x = 0; x < width; x++) {
163bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        // pixels are stored as INT_ARGB
164bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        // our arrays are [R, G, B, A]
165bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        int pixel = srcPixels[x];
166bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[0] = (pixel >> 16) & 0xFF;
167bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[1] = (pixel >>  8) & 0xFF;
168bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[2] = (pixel      ) & 0xFF;
169bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[3] = (pixel >> 24) & 0xFF;
170bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
171bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        pixel = dstPixels[x];
172bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[0] = (pixel >> 16) & 0xFF;
173bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[1] = (pixel >>  8) & 0xFF;
174bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[2] = (pixel      ) & 0xFF;
175bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[3] = (pixel >> 24) & 0xFF;
176bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
177c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        // ---- Modified from original ----
178c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        // recompute src pixel for transparency.
179c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        srcPixel[3] *= dstPixel[3] / 0xFF;
180c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        // ---- Modification ends ----
181c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
182bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        result = blender.blend(srcPixel, dstPixel, result);
183bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
184bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        // mixes the result with the opacity
185bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        if (alpha == 1) {
186bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            dstPixels[x] = (result[3] & 0xFF) << 24 |
187bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                           (result[0] & 0xFF) << 16 |
188bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                           (result[1] & 0xFF) <<  8 |
189bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                           result[2] & 0xFF;
190bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        } else {
191bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            dstPixels[x] =
192bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
193bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
194bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
195f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                                    (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
196bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        }
197bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
198bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                    }
199bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta            }
200f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstOut.setDataElements(0, y, width, 1, dstPixels);
201f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            }
202f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
203f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
204f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
205f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private static abstract class Blender {
206bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta        public abstract int[] blend(int[] src, int[] dst, int[] result);
207f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
208f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        public static Blender getBlenderFor(BlendComposite composite) {
209f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            switch (composite.getMode()) {
210f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case ADD:
211f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
212f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
213bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
214bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 4; i++) {
215bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = Math.min(255, src[i] + dst[i]);
216bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
217bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
218f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
219f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
220f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case DARKEN:
221f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
222f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
223bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
224bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
225bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = Math.min(src[i], dst[i]);
226bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
227bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
228bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
229f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
230f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
231f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case LIGHTEN:
232f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
233f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
234bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
235bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
236bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = Math.max(src[i], dst[i]);
237bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
238bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
239bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
240f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
241f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
242f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case MULTIPLY:
243f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
244f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
245bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
246bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
247bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = (src[i] * dst[i]) >> 8;
248bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
249c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
250bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
251f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
252f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
253f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case OVERLAY:
254f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
255f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
256bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
257bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
258bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
259bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
260bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
261bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
262bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
263f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
264f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
265c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                case SCREEN:
266f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
267f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
268bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
269c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
270c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
271c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
272f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
273f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                            return result;
274f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
275f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
276f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            }
277f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            throw new IllegalArgumentException("Blender not implement for " +
278f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                                               composite.getMode().name());
279f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
280f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
281f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta}
282