Home | History | Annotate | Download | only in graphics
      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 (at) 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 
    105     private BlendComposite(BlendingMode mode) {
    106         this(mode, 1.0f);
    107     }
    108 
    109     private BlendComposite(BlendingMode mode, float alpha) {
    110         this.mode = mode;
    111         setAlpha(alpha);
    112     }
    113 
    114     public static BlendComposite getInstance(BlendingMode mode) {
    115         return new BlendComposite(mode);
    116     }
    117 
    118     public static BlendComposite getInstance(BlendingMode mode, float alpha) {
    119         return new BlendComposite(mode, alpha);
    120     }
    121 
    122     public BlendComposite derive(BlendingMode mode) {
    123         return this.mode == mode ? this : new BlendComposite(mode, getAlpha());
    124     }
    125 
    126     public BlendComposite derive(float alpha) {
    127         return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha);
    128     }
    129 
    130     public float getAlpha() {
    131         return alpha;
    132     }
    133 
    134     public BlendingMode getMode() {
    135         return mode;
    136     }
    137 
    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
    148     public int hashCode() {
    149         return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
    150     }
    151 
    152     @Override
    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 
    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 
    177         private BlendingContext(BlendComposite composite) {
    178             this.composite = composite;
    179             this.blender = Blender.getBlenderFor(composite);
    180         }
    181 
    182         public void dispose() {
    183         }
    184 
    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 {
    247         public abstract int[] blend(int[] src, int[] dst, int[] result);
    248 
    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 
    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 
    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 
    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