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 {
40f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        MULTIPLY(),
41f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        SCREEN(),
42f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        DARKEN(),
43f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        LIGHTEN(),
44f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        OVERLAY(),
45f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        ADD();
46c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
47f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        private final BlendComposite mComposite;
48c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
49f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta        BlendingMode() {
50f0fcede48a2a054991e500ea3f10d3f7df913b4fDeepanshu Gupta            mComposite = new BlendComposite(this);
51c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        }
52c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
53c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        BlendComposite getBlendComposite() {
54c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta            return mComposite;
55c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        }
56f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
57f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
58f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private float alpha;
59f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private BlendingMode mode;
60f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
61f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private BlendComposite(BlendingMode mode) {
62f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        this(mode, 1.0f);
63f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
64f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
65f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private BlendComposite(BlendingMode mode, float alpha) {
66f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        this.mode = mode;
67f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        setAlpha(alpha);
68f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
69f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
70f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static BlendComposite getInstance(BlendingMode mode) {
71c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        return mode.getBlendComposite();
72f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
73f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
74f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public static BlendComposite getInstance(BlendingMode mode, float alpha) {
75c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        if (alpha > 0.9999f) {
76c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta            return getInstance(mode);
77c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        }
78f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return new BlendComposite(mode, alpha);
79f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
80f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
81f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public float getAlpha() {
82f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return alpha;
83f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
84f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
85f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public BlendingMode getMode() {
86f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return mode;
87f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
88f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
89f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private void setAlpha(float alpha) {
90f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        if (alpha < 0.0f || alpha > 1.0f) {
910609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez            assert false : "alpha must be comprised between 0.0f and 1.0f";
920609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez            alpha = Math.min(alpha, 1.0f);
930609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez            alpha = Math.max(alpha, 0.0f);
94f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
95f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
96f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        this.alpha = alpha;
97f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
98f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
99f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    @Override
100f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public int hashCode() {
101f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
102f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
103f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
104f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    @Override
105f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public boolean equals(Object obj) {
106f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        if (!(obj instanceof BlendComposite)) {
107f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            return false;
108f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
109f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
110f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        BlendComposite bc = (BlendComposite) obj;
111f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
112c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta        return mode == bc.mode && alpha == bc.alpha;
113f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
114f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
115f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    public CompositeContext createContext(ColorModel srcColorModel,
116f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                                          ColorModel dstColorModel,
117f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                                          RenderingHints hints) {
118f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        return new BlendingContext(this);
119f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
120f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
121f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private static final class BlendingContext implements CompositeContext {
122f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        private final Blender blender;
123f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        private final BlendComposite composite;
124f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
125f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        private BlendingContext(BlendComposite composite) {
126f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            this.composite = composite;
127f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            this.blender = Blender.getBlenderFor(composite);
128f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
129f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
130f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        public void dispose() {
131f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
132f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
133f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
134f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
135f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
136f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
137f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                throw new IllegalStateException(
138f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        "Source and destination must store pixels as INT.");
139f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            }
140f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
141f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int width = Math.min(src.getWidth(), dstIn.getWidth());
142f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int height = Math.min(src.getHeight(), dstIn.getHeight());
143f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
144f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            float alpha = composite.getAlpha();
145f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
146f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] srcPixel = new int[4];
147f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] dstPixel = new int[4];
148bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta            int[] result = new int[4];
149f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] srcPixels = new int[width];
150f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            int[] dstPixels = new int[width];
151f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
152f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            for (int y = 0; y < height; y++) {
153f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstIn.getDataElements(0, y, width, 1, dstPixels);
154bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                if (alpha != 0) {
155bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                    src.getDataElements(0, y, width, 1, srcPixels);
156bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                    for (int x = 0; x < width; x++) {
157bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        // pixels are stored as INT_ARGB
158bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        // our arrays are [R, G, B, A]
159bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        int pixel = srcPixels[x];
160bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[0] = (pixel >> 16) & 0xFF;
161bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[1] = (pixel >>  8) & 0xFF;
162bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[2] = (pixel      ) & 0xFF;
163bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        srcPixel[3] = (pixel >> 24) & 0xFF;
164bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
165bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        pixel = dstPixels[x];
166bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[0] = (pixel >> 16) & 0xFF;
167bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[1] = (pixel >>  8) & 0xFF;
168bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[2] = (pixel      ) & 0xFF;
169bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        dstPixel[3] = (pixel >> 24) & 0xFF;
170bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
171c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        // ---- Modified from original ----
172c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        // recompute src pixel for transparency.
173c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        srcPixel[3] *= dstPixel[3] / 0xFF;
174c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                        // ---- Modification ends ----
175c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta
176bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        result = blender.blend(srcPixel, dstPixel, result);
177bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
178bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        // mixes the result with the opacity
179bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        if (alpha == 1) {
180bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            dstPixels[x] = (result[3] & 0xFF) << 24 |
181bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                           (result[0] & 0xFF) << 16 |
182bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                           (result[1] & 0xFF) <<  8 |
183bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                           result[2] & 0xFF;
184bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        } else {
185bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            dstPixels[x] =
186bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
187bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
188bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
189f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                                    (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
190bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        }
191bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta
192bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                    }
193bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta            }
194f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                dstOut.setDataElements(0, y, width, 1, dstPixels);
195f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            }
196f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
197f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
198f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
199f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    private static abstract class Blender {
200bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta        public abstract int[] blend(int[] src, int[] dst, int[] result);
201f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta
202f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        public static Blender getBlenderFor(BlendComposite composite) {
203f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            switch (composite.getMode()) {
204f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case ADD:
205f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
206f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
207bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
208bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 4; i++) {
209bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = Math.min(255, src[i] + dst[i]);
210bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
211bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
212f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
213f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
214f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case DARKEN:
215f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
216f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
217bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
218bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
219bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = Math.min(src[i], dst[i]);
220bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
221bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
222bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
223f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
224f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
225f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case LIGHTEN:
226f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
227f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
228bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
229bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
230bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = Math.max(src[i], dst[i]);
231bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
232bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
233bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
234f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
235f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
236f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case MULTIPLY:
237f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
238f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
239bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
240bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
241bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = (src[i] * dst[i]) >> 8;
242bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
243c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
244bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
245f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
246f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
247f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                case OVERLAY:
248f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
249f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
250bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
251bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            for (int i = 0; i < 3; i++) {
252bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
253bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                                    255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
254bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            }
255bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
256bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                            return result;
257f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
258f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
259c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                case SCREEN:
260f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    return new Blender() {
261f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        @Override
262bcf72b14d7b10bda2a62e7b714ee8858e5fa0036Deepanshu Gupta                        public int[] blend(int[] src, int[] dst, int[] result) {
263c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
264c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
265c0edf09b80fc3180e29632e208775e58a24e04fbDeepanshu Gupta                            result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
266f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                            result[3] = Math.min(255, src[3] + dst[3]);
267f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                            return result;
268f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                        }
269f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta                    };
2700609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                default:
2710609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                    assert false : "Blender not implement for " + composite.getMode().name();
2720609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez
2730609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                    // Ignore the blend
2740609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                    return new Blender() {
2750609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                        @Override
2760609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                        public int[] blend(int[] src, int[] dst, int[] result) {
2770609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                            result[0] = dst[0];
2780609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                            result[1] = dst[1];
2790609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                            result[2] = dst[2];
2800609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                            result[3] = dst[3];
2810609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                            return result;
2820609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                        }
2830609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez                    };
284f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta            }
285f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta        }
286f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta    }
287f39dce266913d50b9b484b29fddf655b14cddef6Deepanshu Gupta}
288