• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import java.awt.Composite;
20 import java.awt.CompositeContext;
21 import java.awt.RenderingHints;
22 import java.awt.image.ColorModel;
23 import java.awt.image.DataBuffer;
24 import java.awt.image.Raster;
25 import java.awt.image.WritableRaster;
26 
27 /*
28  * (non-Javadoc)
29  * The class is adapted from a demo tool for Blending Modes written by
30  * Romain Guy (romainguy@android.com). The tool is available at
31  * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
32  */
33 public final class BlendComposite implements Composite {
34     public enum BlendingMode {
35         NORMAL,
36         AVERAGE,
37         MULTIPLY,
38         SCREEN,
39         DARKEN,
40         LIGHTEN,
41         OVERLAY,
42         HARD_LIGHT,
43         SOFT_LIGHT,
44         DIFFERENCE,
45         NEGATION,
46         EXCLUSION,
47         COLOR_DODGE,
48         INVERSE_COLOR_DODGE,
49         SOFT_DODGE,
50         COLOR_BURN,
51         INVERSE_COLOR_BURN,
52         SOFT_BURN,
53         REFLECT,
54         GLOW,
55         FREEZE,
56         HEAT,
57         ADD,
58         SUBTRACT,
59         STAMP,
60         RED,
61         GREEN,
62         BLUE,
63         HUE,
64         SATURATION,
65         COLOR,
66         LUMINOSITY
67     }
68 
69     public static final BlendComposite Normal = new BlendComposite(BlendingMode.NORMAL);
70     public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE);
71     public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
72     public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
73     public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
74     public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
75     public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
76     public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT);
77     public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT);
78     public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE);
79     public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION);
80     public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION);
81     public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE);
82     public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE);
83     public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE);
84     public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN);
85     public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN);
86     public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN);
87     public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT);
88     public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW);
89     public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE);
90     public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT);
91     public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
92     public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT);
93     public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP);
94     public static final BlendComposite Red = new BlendComposite(BlendingMode.RED);
95     public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN);
96     public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE);
97     public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE);
98     public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION);
99     public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR);
100     public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY);
101 
102     private float alpha;
103     private BlendingMode mode;
104 
BlendComposite(BlendingMode mode)105     private BlendComposite(BlendingMode mode) {
106         this(mode, 1.0f);
107     }
108 
BlendComposite(BlendingMode mode, float alpha)109     private BlendComposite(BlendingMode mode, float alpha) {
110         this.mode = mode;
111         setAlpha(alpha);
112     }
113 
getInstance(BlendingMode mode)114     public static BlendComposite getInstance(BlendingMode mode) {
115         return new BlendComposite(mode);
116     }
117 
getInstance(BlendingMode mode, float alpha)118     public static BlendComposite getInstance(BlendingMode mode, float alpha) {
119         return new BlendComposite(mode, alpha);
120     }
121 
derive(BlendingMode mode)122     public BlendComposite derive(BlendingMode mode) {
123         return this.mode == mode ? this : new BlendComposite(mode, getAlpha());
124     }
125 
derive(float alpha)126     public BlendComposite derive(float alpha) {
127         return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha);
128     }
129 
getAlpha()130     public float getAlpha() {
131         return alpha;
132     }
133 
getMode()134     public BlendingMode getMode() {
135         return mode;
136     }
137 
setAlpha(float alpha)138     private void setAlpha(float alpha) {
139         if (alpha < 0.0f || alpha > 1.0f) {
140             throw new IllegalArgumentException(
141                     "alpha must be comprised between 0.0f and 1.0f");
142         }
143 
144         this.alpha = alpha;
145     }
146 
147     @Override
hashCode()148     public int hashCode() {
149         return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
150     }
151 
152     @Override
equals(Object obj)153     public boolean equals(Object obj) {
154         if (!(obj instanceof BlendComposite)) {
155             return false;
156         }
157 
158         BlendComposite bc = (BlendComposite) obj;
159 
160         if (mode != bc.mode) {
161             return false;
162         }
163 
164         return alpha == bc.alpha;
165     }
166 
createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints)167     public CompositeContext createContext(ColorModel srcColorModel,
168                                           ColorModel dstColorModel,
169                                           RenderingHints hints) {
170         return new BlendingContext(this);
171     }
172 
173     private static final class BlendingContext implements CompositeContext {
174         private final Blender blender;
175         private final BlendComposite composite;
176 
BlendingContext(BlendComposite composite)177         private BlendingContext(BlendComposite composite) {
178             this.composite = composite;
179             this.blender = Blender.getBlenderFor(composite);
180         }
181 
dispose()182         public void dispose() {
183         }
184 
compose(Raster src, Raster dstIn, WritableRaster dstOut)185         public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
186             if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
187                 dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
188                 dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
189                 throw new IllegalStateException(
190                         "Source and destination must store pixels as INT.");
191             }
192 
193             int width = Math.min(src.getWidth(), dstIn.getWidth());
194             int height = Math.min(src.getHeight(), dstIn.getHeight());
195 
196             float alpha = composite.getAlpha();
197 
198             int[] srcPixel = new int[4];
199             int[] dstPixel = new int[4];
200             int[] result = new int[4];
201             int[] srcPixels = new int[width];
202             int[] dstPixels = new int[width];
203 
204             for (int y = 0; y < height; y++) {
205                 dstIn.getDataElements(0, y, width, 1, dstPixels);
206                 if (alpha != 0) {
207                     src.getDataElements(0, y, width, 1, srcPixels);
208                     for (int x = 0; x < width; x++) {
209                         // pixels are stored as INT_ARGB
210                         // our arrays are [R, G, B, A]
211                         int pixel = srcPixels[x];
212                         srcPixel[0] = (pixel >> 16) & 0xFF;
213                         srcPixel[1] = (pixel >>  8) & 0xFF;
214                         srcPixel[2] = (pixel      ) & 0xFF;
215                         srcPixel[3] = (pixel >> 24) & 0xFF;
216 
217                         pixel = dstPixels[x];
218                         dstPixel[0] = (pixel >> 16) & 0xFF;
219                         dstPixel[1] = (pixel >>  8) & 0xFF;
220                         dstPixel[2] = (pixel      ) & 0xFF;
221                         dstPixel[3] = (pixel >> 24) & 0xFF;
222 
223                         result = blender.blend(srcPixel, dstPixel, result);
224 
225                         // mixes the result with the opacity
226                         if (alpha == 1) {
227                             dstPixels[x] = (result[3] & 0xFF) << 24 |
228                                            (result[0] & 0xFF) << 16 |
229                                            (result[1] & 0xFF) <<  8 |
230                                            result[2] & 0xFF;
231                         } else {
232                             dstPixels[x] =
233                                     ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
234                                     ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
235                                     ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
236                                     (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
237                         }
238 
239                     }
240             }
241                 dstOut.setDataElements(0, y, width, 1, dstPixels);
242             }
243         }
244     }
245 
246     private static abstract class Blender {
blend(int[] src, int[] dst, int[] result)247         public abstract int[] blend(int[] src, int[] dst, int[] result);
248 
RGBtoHSL(int r, int g, int b, float[] hsl)249         private static void RGBtoHSL(int r, int g, int b, float[] hsl) {
250             float var_R = (r / 255f);
251             float var_G = (g / 255f);
252             float var_B = (b / 255f);
253 
254             float var_Min;
255             float var_Max;
256             float del_Max;
257 
258             if (var_R > var_G) {
259                 var_Min = var_G;
260                 var_Max = var_R;
261             } else {
262                 var_Min = var_R;
263                 var_Max = var_G;
264             }
265             if (var_B > var_Max) {
266                 var_Max = var_B;
267             }
268             if (var_B < var_Min) {
269                 var_Min = var_B;
270             }
271 
272             del_Max = var_Max - var_Min;
273 
274             float H, S, L;
275             L = (var_Max + var_Min) / 2f;
276 
277             if (del_Max - 0.01f <= 0.0f) {
278                 H = 0;
279                 S = 0;
280             } else {
281                 if (L < 0.5f) {
282                     S = del_Max / (var_Max + var_Min);
283                 } else {
284                     S = del_Max / (2 - var_Max - var_Min);
285                 }
286 
287                 float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max;
288                 float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max;
289                 float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max;
290 
291                 if (var_R == var_Max) {
292                     H = del_B - del_G;
293                 } else if (var_G == var_Max) {
294                     H = (1 / 3f) + del_R - del_B;
295                 } else {
296                     H = (2 / 3f) + del_G - del_R;
297                 }
298                 if (H < 0) {
299                     H += 1;
300                 }
301                 if (H > 1) {
302                     H -= 1;
303                 }
304             }
305 
306             hsl[0] = H;
307             hsl[1] = S;
308             hsl[2] = L;
309         }
310 
HSLtoRGB(float h, float s, float l, int[] rgb)311         private static void HSLtoRGB(float h, float s, float l, int[] rgb) {
312             int R, G, B;
313 
314             if (s - 0.01f <= 0.0f) {
315                 R = (int) (l * 255.0f);
316                 G = (int) (l * 255.0f);
317                 B = (int) (l * 255.0f);
318             } else {
319                 float var_1, var_2;
320                 if (l < 0.5f) {
321                     var_2 = l * (1 + s);
322                 } else {
323                     var_2 = (l + s) - (s * l);
324                 }
325                 var_1 = 2 * l - var_2;
326 
327                 R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f)));
328                 G = (int) (255.0f * hue2RGB(var_1, var_2, h));
329                 B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f)));
330             }
331 
332             rgb[0] = R;
333             rgb[1] = G;
334             rgb[2] = B;
335         }
336 
hue2RGB(float v1, float v2, float vH)337         private static float hue2RGB(float v1, float v2, float vH) {
338             if (vH < 0.0f) {
339                 vH += 1.0f;
340             }
341             if (vH > 1.0f) {
342                 vH -= 1.0f;
343             }
344             if ((6.0f * vH) < 1.0f) {
345                 return (v1 + (v2 - v1) * 6.0f * vH);
346             }
347             if ((2.0f * vH) < 1.0f) {
348                 return (v2);
349             }
350             if ((3.0f * vH) < 2.0f) {
351                 return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
352             }
353             return (v1);
354         }
355 
getBlenderFor(BlendComposite composite)356         public static Blender getBlenderFor(BlendComposite composite) {
357             switch (composite.getMode()) {
358                 case NORMAL:
359                     return new Blender() {
360                         @Override
361                         public int[] blend(int[] src, int[] dst, int[] result) {
362                             System.arraycopy(src, 0, result, 0, 4);
363                             return result;
364                         }
365                     };
366                 case ADD:
367                     return new Blender() {
368                         @Override
369                         public int[] blend(int[] src, int[] dst, int[] result) {
370                             for (int i = 0; i < 4; i++) {
371                                 result[i] = Math.min(255, src[i] + dst[i]);
372                             }
373                             return result;
374                         }
375                     };
376                 case AVERAGE:
377                     return new Blender() {
378                         @Override
379                         public int[] blend(int[] src, int[] dst, int[] result) {
380                             for (int i = 0; i < 3; i++) {
381                                 result[i] = (src[i] + dst[i]) >> 1;
382                             }
383                             result[3] = Math.min(255, src[3] + dst[3]);
384                             return result;
385                         }
386                     };
387                 case BLUE:
388                     return new Blender() {
389                         @Override
390                         public int[] blend(int[] src, int[] dst, int[] result) {
391                             System.arraycopy(dst, 0, result, 0, 3);
392                             result[3] = Math.min(255, src[3] + dst[3]);
393                             return result;
394                         }
395                     };
396                 case COLOR:
397                     return new Blender() {
398                         @Override
399                         public int[] blend(int[] src, int[] dst, int[] result) {
400                             float[] srcHSL = new float[3];
401                             RGBtoHSL(src[0], src[1], src[2], srcHSL);
402                             float[] dstHSL = new float[3];
403                             RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
404 
405                             HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result);
406                             result[3] = Math.min(255, src[3] + dst[3]);
407 
408                             return result;
409                         }
410                     };
411                 case COLOR_BURN:
412                     return new Blender() {
413                         @Override
414                         public int[] blend(int[] src, int[] dst, int[] result) {
415                             for (int i = 0; i < 3; i++) {
416                                 result[i] = src[i] == 0 ? 0 :
417                                     Math.max(0, 255 - (((255 - dst[i]) << 8) / src[i]));
418                             }
419                             result[3] = Math.min(255, src[3] + dst[3]);
420                             return result;
421                         }
422                     };
423                 case COLOR_DODGE:
424                     return new Blender() {
425                         @Override
426                         public int[] blend(int[] src, int[] dst, int[] result) {
427                             for (int i = 0; i < 3; i++) {
428                                 result[i] = src[i] == 255 ? 255 :
429                                     Math.min((dst[i] << 8) / (255 - src[i]), 255);
430                             }
431                             result[3] = Math.min(255, src[3] + dst[3]);
432                             return result;
433                         }
434                     };
435                 case DARKEN:
436                     return new Blender() {
437                         @Override
438                         public int[] blend(int[] src, int[] dst, int[] result) {
439                             for (int i = 0; i < 3; i++) {
440                                 result[i] = Math.min(src[i], dst[i]);
441                             }
442                             result[3] = Math.min(255, src[3] + dst[3]);
443                             return result;
444                         }
445                     };
446                 case DIFFERENCE:
447                     return new Blender() {
448                         @Override
449                         public int[] blend(int[] src, int[] dst, int[] result) {
450                             for (int i = 0; i < 3; i++) {
451                                 result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7);
452                             }
453                             result[3] = Math.min(255, src[3] + dst[3]);
454                             return result;
455                         }
456                     };
457                 case EXCLUSION:
458                     return new Blender() {
459                         @Override
460                         public int[] blend(int[] src, int[] dst, int[] result) {
461                             for (int i = 0; i < 3; i++) {
462                                 result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7);
463                             }
464                             result[3] = Math.min(255, src[3] + dst[3]);
465                             return result;
466                         }
467                     };
468                 case FREEZE:
469                     return new Blender() {
470                         @Override
471                         public int[] blend(int[] src, int[] dst, int[] result) {
472                             for (int i = 0; i < 3; i++) {
473                                 result[i] = src[i] == 0 ? 0 :
474                                     Math.max(0, 255 - (255 - dst[i]) * (255 - dst[i]) / src[i]);
475                             }
476                             result[3] = Math.min(255, src[3] + dst[3]);
477                             return result;
478                         }
479                     };
480                 case GLOW:
481                     return new Blender() {
482                         @Override
483                         public int[] blend(int[] src, int[] dst, int[] result) {
484                             for (int i = 0; i < 3; i++) {
485                                 result[i] = dst[i] == 255 ? 255 :
486                                     Math.min(255, src[i] * src[i] / (255 - dst[i]));
487                             }
488                             result[3] = Math.min(255, src[3] + dst[3]);
489                             return result;
490                         }
491                     };
492                 case GREEN:
493                     return new Blender() {
494                         @Override
495                         public int[] blend(int[] src, int[] dst, int[] result) {
496                             return new int[] {
497                                 dst[0],
498                                 dst[1],
499                                 src[2],
500                                 Math.min(255, src[3] + dst[3])
501                             };
502                         }
503                     };
504                 case HARD_LIGHT:
505                     return new Blender() {
506                         @Override
507                         public int[] blend(int[] src, int[] dst, int[] result) {
508                             return new int[] {
509                                 src[0] < 128 ? dst[0] * src[0] >> 7 :
510                                     255 - ((255 - src[0]) * (255 - dst[0]) >> 7),
511                                 src[1] < 128 ? dst[1] * src[1] >> 7 :
512                                     255 - ((255 - src[1]) * (255 - dst[1]) >> 7),
513                                 src[2] < 128 ? dst[2] * src[2] >> 7 :
514                                     255 - ((255 - src[2]) * (255 - dst[2]) >> 7),
515                                 Math.min(255, src[3] + dst[3])
516                             };
517                         }
518                     };
519                 case HEAT:
520                     return new Blender() {
521                         @Override
522                         public int[] blend(int[] src, int[] dst, int[] result) {
523                             return new int[] {
524                                 dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]),
525                                 dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]),
526                                 dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) / dst[2]),
527                                 Math.min(255, src[3] + dst[3])
528                             };
529                         }
530                     };
531                 case HUE:
532                     return new Blender() {
533                         @Override
534                         public int[] blend(int[] src, int[] dst, int[] result) {
535                             float[] srcHSL = new float[3];
536                             RGBtoHSL(src[0], src[1], src[2], srcHSL);
537                             float[] dstHSL = new float[3];
538                             RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
539 
540                             HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result);
541                             result[3] = Math.min(255, src[3] + dst[3]);
542 
543                             return result;
544                         }
545                     };
546                 case INVERSE_COLOR_BURN:
547                     return new Blender() {
548                         @Override
549                         public int[] blend(int[] src, int[] dst, int[] result) {
550                             return new int[] {
551                                 dst[0] == 0 ? 0 :
552                                     Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])),
553                                 dst[1] == 0 ? 0 :
554                                     Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])),
555                                 dst[2] == 0 ? 0 :
556                                     Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])),
557                                 Math.min(255, src[3] + dst[3])
558                             };
559                         }
560                     };
561                 case INVERSE_COLOR_DODGE:
562                     return new Blender() {
563                         @Override
564                         public int[] blend(int[] src, int[] dst, int[] result) {
565                             return new int[] {
566                                 dst[0] == 255 ? 255 :
567                                     Math.min((src[0] << 8) / (255 - dst[0]), 255),
568                                 dst[1] == 255 ? 255 :
569                                     Math.min((src[1] << 8) / (255 - dst[1]), 255),
570                                 dst[2] == 255 ? 255 :
571                                     Math.min((src[2] << 8) / (255 - dst[2]), 255),
572                                 Math.min(255, src[3] + dst[3])
573                             };
574                         }
575                     };
576                 case LIGHTEN:
577                     return new Blender() {
578                         @Override
579                         public int[] blend(int[] src, int[] dst, int[] result) {
580                             for (int i = 0; i < 3; i++) {
581                                 result[i] = Math.max(src[i], dst[i]);
582                             }
583                             result[3] = Math.min(255, src[3] + dst[3]);
584                             return result;
585                         }
586                     };
587                 case LUMINOSITY:
588                     return new Blender() {
589                         @Override
590                         public int[] blend(int[] src, int[] dst, int[] result) {
591                             float[] srcHSL = new float[3];
592                             RGBtoHSL(src[0], src[1], src[2], srcHSL);
593                             float[] dstHSL = new float[3];
594                             RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
595 
596                             HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result);
597                             result[3] = Math.min(255, src[3] + dst[3]);
598 
599                             return result;
600                         }
601                     };
602                 case MULTIPLY:
603                     return new Blender() {
604                         @Override
605                         public int[] blend(int[] src, int[] dst, int[] result) {
606                             for (int i = 0; i < 3; i++) {
607                                 result[i] = (src[i] * dst[i]) >> 8;
608                             }
609                             result[3] = Math.min(255, src[3] + dst[3]);
610                             return result;
611                         }
612                     };
613                 case NEGATION:
614                     return new Blender() {
615                         @Override
616                         public int[] blend(int[] src, int[] dst, int[] result) {
617                             return new int[] {
618                                 255 - Math.abs(255 - dst[0] - src[0]),
619                                 255 - Math.abs(255 - dst[1] - src[1]),
620                                 255 - Math.abs(255 - dst[2] - src[2]),
621                                 Math.min(255, src[3] + dst[3])
622                             };
623                         }
624                     };
625                 case OVERLAY:
626                     return new Blender() {
627                         @Override
628                         public int[] blend(int[] src, int[] dst, int[] result) {
629                             for (int i = 0; i < 3; i++) {
630                                 result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
631                                     255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
632                             }
633                             result[3] = Math.min(255, src[3] + dst[3]);
634                             return result;
635                         }
636                     };
637                 case RED:
638                     return new Blender() {
639                         @Override
640                         public int[] blend(int[] src, int[] dst, int[] result) {
641                             return new int[] {
642                                 src[0],
643                                 dst[1],
644                                 dst[2],
645                                 Math.min(255, src[3] + dst[3])
646                             };
647                         }
648                     };
649                 case REFLECT:
650                     return new Blender() {
651                         @Override
652                         public int[] blend(int[] src, int[] dst, int[] result) {
653                             return new int[] {
654                                 src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])),
655                                 src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])),
656                                 src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])),
657                                 Math.min(255, src[3] + dst[3])
658                             };
659                         }
660                     };
661                 case SATURATION:
662                     return new Blender() {
663                         @Override
664                         public int[] blend(int[] src, int[] dst, int[] result) {
665                             float[] srcHSL = new float[3];
666                             RGBtoHSL(src[0], src[1], src[2], srcHSL);
667                             float[] dstHSL = new float[3];
668                             RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
669 
670                             HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result);
671                             result[3] = Math.min(255, src[3] + dst[3]);
672 
673                             return result;
674                         }
675                     };
676                 case SCREEN:
677                     return new Blender() {
678                         @Override
679                         public int[] blend(int[] src, int[] dst, int[] result) {
680                             return new int[] {
681                                 255 - ((255 - src[0]) * (255 - dst[0]) >> 8),
682                                 255 - ((255 - src[1]) * (255 - dst[1]) >> 8),
683                                 255 - ((255 - src[2]) * (255 - dst[2]) >> 8),
684                                 Math.min(255, src[3] + dst[3])
685                             };
686                         }
687                     };
688                 case SOFT_BURN:
689                     return new Blender() {
690                         @Override
691                         public int[] blend(int[] src, int[] dst, int[] result) {
692                             return new int[] {
693                                 dst[0] + src[0] < 256 ?
694 	                                (dst[0] == 255 ? 255 :
695                                         Math.min(255, (src[0] << 7) / (255 - dst[0]))) :
696                                             Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])),
697                                 dst[1] + src[1] < 256 ?
698 	                                (dst[1] == 255 ? 255 :
699                                         Math.min(255, (src[1] << 7) / (255 - dst[1]))) :
700                                             Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])),
701                                 dst[2] + src[2] < 256 ?
702 	                                (dst[2] == 255 ? 255 :
703                                         Math.min(255, (src[2] << 7) / (255 - dst[2]))) :
704                                             Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])),
705                                 Math.min(255, src[3] + dst[3])
706                             };
707                         }
708                     };
709                 case SOFT_DODGE:
710                     return new Blender() {
711                         @Override
712                         public int[] blend(int[] src, int[] dst, int[] result) {
713                             return new int[] {
714                                 dst[0] + src[0] < 256 ?
715                                     (src[0] == 255 ? 255 :
716                                         Math.min(255, (dst[0] << 7) / (255 - src[0]))) :
717                                             Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])),
718                                 dst[1] + src[1] < 256 ?
719                                     (src[1] == 255 ? 255 :
720                                         Math.min(255, (dst[1] << 7) / (255 - src[1]))) :
721                                             Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])),
722                                 dst[2] + src[2] < 256 ?
723                                     (src[2] == 255 ? 255 :
724                                         Math.min(255, (dst[2] << 7) / (255 - src[2]))) :
725                                             Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])),
726                                 Math.min(255, src[3] + dst[3])
727                             };
728                         }
729                     };
730                 case SOFT_LIGHT:
731                     break;
732                 case STAMP:
733                     return new Blender() {
734                         @Override
735                         public int[] blend(int[] src, int[] dst, int[] result) {
736                             return new int[] {
737                                 Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)),
738                                 Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)),
739                                 Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)),
740                                 Math.min(255, src[3] + dst[3])
741                             };
742                         }
743                     };
744                 case SUBTRACT:
745                     return new Blender() {
746                         @Override
747                         public int[] blend(int[] src, int[] dst, int[] result) {
748                             return new int[] {
749                                 Math.max(0, src[0] + dst[0] - 256),
750                                 Math.max(0, src[1] + dst[1] - 256),
751                                 Math.max(0, src[2] + dst[2] - 256),
752                                 Math.min(255, src[3] + dst[3])
753                             };
754                         }
755                     };
756             }
757             throw new IllegalArgumentException("Blender not implement for " +
758                                                composite.getMode().name());
759         }
760     }
761 }
762