001/*
002 * $Id: BlendComposite.java 4011 2011-05-05 16:19:34Z kschaefe $
003 *
004 * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
005 *
006 * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
007 * Santa Clara, California 95054, U.S.A. All rights reserved.
008 *
009 * Copyright (c) 2006 Romain Guy <romain.guy@mac.com>
010 * All rights reserved.
011 *
012 * Redistribution and use in source and binary forms, with or without
013 * modification, are permitted provided that the following conditions
014 * are met:
015 * 1. Redistributions of source code must retain the above copyright
016 *    notice, this list of conditions and the following disclaimer.
017 * 2. Redistributions in binary form must reproduce the above copyright
018 *    notice, this list of conditions and the following disclaimer in the
019 *    documentation and/or other materials provided with the distribution.
020 * 3. The name of the author may not be used to endorse or promote products
021 *    derived from this software without specific prior written permission.
022 *
023 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
024 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
025 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
026 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
028 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
029 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
030 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
032 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
033 */
034
035package org.jdesktop.swingx.graphics;
036
037import java.awt.Composite;
038import java.awt.CompositeContext;
039import java.awt.RenderingHints;
040import java.awt.image.ColorModel;
041import java.awt.image.DataBuffer;
042import java.awt.image.DirectColorModel;
043import java.awt.image.Raster;
044import java.awt.image.RasterFormatException;
045import java.awt.image.WritableRaster;
046
047/**
048 * <p>A blend composite defines the rule according to which a drawing primitive
049 * (known as the source) is mixed with existing graphics (know as the
050 * destination.)</p>
051 * <p><code>BlendComposite</code> is an implementation of the
052 * {@link java.awt.Composite} interface and must therefore be set as a state on
053 * a {@link java.awt.Graphics2D} surface.</p>
054 * <p>Please refer to {@link java.awt.Graphics2D#setComposite(java.awt.Composite)}
055 * for more information on how to use this class with a graphics surface.</p>
056 * <h2>Blending Modes</h2>
057 * <p>This class offers a certain number of blending modes, or compositing
058 * rules. These rules are inspired from graphics editing software packages,
059 * like <em>Adobe Photoshop</em> or <em>The GIMP</em>.</p>
060 * <p>Given the wide variety of implemented blending modes and the difficulty
061 * to describe them with words, please refer to those tools to visually see
062 * the result of these blending modes.</p>
063 * <h2>Opacity</h2>
064 * <p>Each blending mode has an associated opacity, defined as a float value
065 * between 0.0 and 1.0. Changing the opacity controls the force with which the
066 * compositing operation is applied. For instance, a composite with an opacity
067 * of 0.0 will not draw the source onto the destination. With an opacity of
068 * 1.0, the source will be fully drawn onto the destination, according to the
069 * selected blending mode rule.</p>
070 * <p>The opacity, or alpha value, is used by the composite instance to mutiply
071 * the alpha value of each pixel of the source when being composited over the
072 * destination.</p>
073 * <h2>Creating a Blend Composite</h2>
074 * <p>Blend composites can be created in various manners:</p>
075 * <ul>
076 *   <li>Use one of the pre-defined instance. Example:
077 *     <code>BlendComposite.Average</code>.</li>
078 *   <li>Derive one of the pre-defined instances by calling
079 *     {@link #derive(float)} or {@link #derive(BlendingMode)}. Deriving allows
080 *     you to change either the opacity or the blending mode. Example:
081 *     <code>BlendComposite.Average.derive(0.5f)</code>.</li>
082 *   <li>Use a factory method: {@link #getInstance(BlendingMode)} or
083 *     {@link #getInstance(BlendingMode, float)}.</li>
084 * </ul>
085 * <h2>Functionality Change in SwingX 1.6.3</h2>
086 * <p>Due to incorrect implementations of various blending modes incompatible changes have occurred.
087 * The following will help users alleviate problems during migration:
088 * <ul>
089 * <li>{@link BlendingMode#BLUE} and {@link BlendingMode#GREEN} have been swapped.</li>
090 * </ul>
091 * <p>
092 * 
093 * @see org.jdesktop.swingx.graphics.BlendComposite.BlendingMode
094 * @see java.awt.Graphics2D
095 * @see java.awt.Composite
096 * @see java.awt.AlphaComposite
097 * @author Romain Guy <romain.guy@mac.com>
098 * @author Karl Schaefer (support and additional modes)
099 */
100public final class BlendComposite implements Composite {
101    /**
102     * A blending mode defines the compositing rule of a
103     * {@link org.jdesktop.swingx.graphics.BlendComposite}.
104     * 
105     * @author Romain Guy <romain.guy@mac.com>
106     * @author Karl Schaefer (support and additional modes)
107     */
108    public enum BlendingMode {
109        /**
110         * The {@code Average} blending mode produces an average of the source and blend colors. The
111         * image will push colors toward the middle, reducing the extremes.
112         */
113        AVERAGE {
114            @Override
115            void blend(int[] src, int[] dst, int[] result) {
116                result[0] = (src[0] + dst[0]) >> 1;
117                result[1] = (src[1] + dst[1]) >> 1;
118                result[2] = (src[2] + dst[2]) >> 1;
119                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
120            }
121        },
122        
123        /**
124         * Similar to {@link #AVERAGE}, but more severely lightens or darkens the edge colors.
125         */
126        STAMP {
127            @Override
128            void blend(int[] src, int[] dst, int[] result) {
129                result[0] = Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256));
130                result[1] = Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256));
131                result[2] = Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256));
132                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
133            }
134        },
135        
136        /**
137         * The {@code Darken} blend mode compares the color information for each pixel of the base
138         * and the blend color and applies the darker color as the result. Any pixels in the base
139         * image that are lighter than the blend color are replaced, and pixels that are darker are
140         * left unchanged. No part of the image will become lighter.
141         */
142        DARKEN {
143            @Override
144            void blend(int[] src, int[] dst, int[] result) {
145                result[0] = Math.min(src[0], dst[0]);
146                result[1] = Math.min(src[1], dst[1]);
147                result[2] = Math.min(src[2], dst[2]);
148                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
149            }
150        },
151
152        /**
153         * The {@code Multiply} blend mode multiplies the base color with the blend color. The
154         * resulting color will always be darker, unless the blend color is white, which will result
155         * in no change. 100% opaque black multiplied with any color will result in black. As you
156         * overlay strokes of color with the Multiply blending mode, each stroke will result in
157         * darker and darker color.
158         */
159        MULTIPLY {
160            @Override
161            void blend(int[] src, int[] dst, int[] result) {
162                result[0] = (src[0] * dst[0] + 2) >> 8;
163                result[1] = (src[1] * dst[1] + 2) >> 8;
164                result[2] = (src[2] * dst[2] + 2) >> 8;
165                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
166            }
167        },
168
169        /**
170         * The {@code Color Burn} blending mode increases the contrast to darken the base color
171         * while reflecting the blend color. The darker the blend color, the more intensely the
172         * color will be applied in the base image. White as the blend color produces no change.
173         */
174        COLOR_BURN {
175            @Override
176            void blend(int[] src, int[] dst, int[] result) {
177                result[0] = src[0] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[0]) << 8) / src[0]));
178                result[1] = src[1] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[1]) << 8) / src[1]));
179                result[2] = src[2] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[2]) << 8) / src[2]));
180                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
181            }
182        },
183
184        /**
185         * {@code Inverse Color Burn} is the same as {@link #COLOR_BURN Color Burn} with the source
186         * and destination swapped.
187         */
188        INVERSE_COLOR_BURN {
189            @Override
190            void blend(int[] src, int[] dst, int[] result) {
191                result[0] = dst[0] == 0 ? 0 : Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0]));
192                result[1] = dst[1] == 0 ? 0 : Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1]));
193                result[2] = dst[2] == 0 ? 0 : Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2]));
194                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
195            }
196        },
197        
198        SOFT_BURN {
199            @Override
200            void blend(int[] src, int[] dst, int[] result) {
201                result[0] = dst[0] + src[0] < 256
202                        ? (dst[0] == 255 ? 255 : Math.min(255, (src[0] << 7) / (255 - dst[0])))
203                        : Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0]));
204                result[1] = dst[1] + src[1] < 256 
205                        ? (dst[1] == 255 ? 255 : Math.min(255, (src[1] << 7) / (255 - dst[1]))) 
206                        : Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1]));
207                result[2] = dst[2] + src[2] < 256 
208                        ? (dst[2] == 255 ? 255 : Math.min(255, (src[2] << 7) / (255 - dst[2]))) 
209                        : Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2]));
210                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
211            }
212        },
213        
214        /**
215         * The {@code Subtract} blend mode is similar to {@link #COLOR_BURN Color Burn} but instead of increasing
216         * contrast, it decreases brightness to darken the base color and reflect the blend color.
217         * It is also similar to the Multiply blend mode, but produces a much more intense result.
218         * White as the blend color produces no change.
219         * <p>
220         * This mode is also known as {@code Linear Burn}.
221         */
222        SUBTRACT {
223            @Override
224            void blend(int[] src, int[] dst, int[] result) {
225                result[0] = Math.max(0, src[0] + dst[0] - 256);
226                result[1] = Math.max(0, src[1] + dst[1] - 256);
227                result[2] = Math.max(0, src[2] + dst[2] - 256);
228                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
229            }
230        },
231
232        /**
233         * The {@code Lighten} blending mode compares the color information for each pixel of the
234         * base and the blend color and applies the lighter color as the result. Any pixels in the
235         * base image that are darker than the blend color are replaced, and pixels that are lighter
236         * are left unchanged. No part of the image will become darker.
237         */
238        LIGHTEN {
239            @Override
240            void blend(int[] src, int[] dst, int[] result) {
241                result[0] = Math.max(src[0], dst[0]);
242                result[1] = Math.max(src[1], dst[1]);
243                result[2] = Math.max(src[2], dst[2]);
244                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
245            }
246        },
247
248        /**
249         * The {@code Screen} blending mode is the opposite of the {@link #MULTIPLY Multiply} mode
250         * in that it multiples the inverse of the base color with the blend color. What this means
251         * is that your image will get lighter overall. In areas where the blend color is black, the
252         * base image will be unchanged, and in areas where the blend or base color is white, the
253         * result will be no change. Dark areas in the base image will become significantly lighter,
254         * and bright areas will become only slightly lighter.
255         */
256        SCREEN {
257            @Override
258            void blend(int[] src, int[] dst, int[] result) {
259                result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
260                result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
261                result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
262                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
263            }
264        },
265
266        /**
267         * The {@code Color Dodge} blending mode is essentially the opposite of {@link #COLOR_BURN
268         * Color Burn}. The {@code Color Dodge} blending mode decreases the contrast to brighten the
269         * base color while reflecting the blend color. The lighter the blend color, the more
270         * significant the color dodge effect will be making the result brighter, with less
271         * contrast, and tinted toward the blend color. Black as the blend color produces no change.
272         */
273        COLOR_DODGE {
274            @Override
275            void blend(int[] src, int[] dst, int[] result) {
276                result[0] = src[0] == 255 ? 255 : Math.min((dst[0] << 8) / (255 - src[0]), 255);
277                result[1] = src[1] == 255 ? 255 : Math.min((dst[1] << 8) / (255 - src[1]), 255);
278                result[2] = src[2] == 255 ? 255 : Math.min((dst[2] << 8) / (255 - src[2]), 255);
279                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
280            }
281        },
282
283        /**
284         * {@code Inverse Color Dodge} is the same as {@link #COLOR_DODGE Color Dodge} with the
285         * source and destination swapped.
286         */
287        INVERSE_COLOR_DODGE {
288            @Override
289            void blend(int[] src, int[] dst, int[] result) {
290                result[0] = dst[0] == 255 ? 255 : Math.min((src[0] << 8) / (255 - dst[0]), 255);
291                result[1] = dst[1] == 255 ? 255 : Math.min((src[1] << 8) / (255 - dst[1]), 255);
292                result[2] = dst[2] == 255 ? 255 : Math.min((src[2] << 8) / (255 - dst[2]), 255);
293                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
294            }
295        },
296
297        SOFT_DODGE {
298            @Override
299            void blend(int[] src, int[] dst, int[] result) {
300                result[0] = dst[0] + src[0] < 256
301                        ? (src[0] == 255 ? 255 : Math.min(255, (dst[0] << 7) / (255 - src[0])))
302                        : Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0]));
303                result[1] = dst[1] + src[1] < 256 
304                        ? (src[1] == 255 ? 255 : Math.min(255, (dst[1] << 7) / (255 - src[1])))
305                        : Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1]));
306                result[2] = dst[2] + src[2] < 256 
307                        ? (src[2] == 255 ? 255 : Math.min(255, (dst[2] << 7) / (255 - src[2]))) 
308                        : Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2]));
309                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
310            }
311        },
312        
313        /**
314         * {@code Add} is the opposite of {@link #SUBTRACT Subtract}. It increases brightness to
315         * lighten the base color and reflect the blend color. It is also similar to the
316         * {@link #SCREEN Screen} blend mode, but produces a more intense result. Black as the blend
317         * color produces no change.
318         * <p>
319         * This mode is also known as {@code Linear Dodge}.
320         */
321        ADD {
322            @Override
323            void blend(int[] src, int[] dst, int[] result) {
324                result[0] = Math.min(255, src[0] + dst[0]);
325                result[1] = Math.min(255, src[1] + dst[1]);
326                result[2] = Math.min(255, src[2] + dst[2]);
327                result[3] = Math.min(255, src[3] + dst[3]);
328            }
329        },
330        
331        /**
332         * The {@code Overlay} blending mode preserves the highlights and shadows of the base color
333         * while mixing the base color and the blend color. It is a combination of the
334         * {@link #MULTIPLY Multiply} and {@link #SCREEN Screen} blending modes--multiplying the
335         * dark areas, and screening the light areas. A blend color of 50% gray has no effect on the
336         * base image.
337         */
338        OVERLAY {
339            @Override
340            void blend(int[] src, int[] dst, int[] result) {
341                result[0] = dst[0] < 128 ? dst[0] * src[0] >> 7
342                        : 255 - ((255 - dst[0]) * (255 - src[0]) >> 7);
343                result[1] = dst[1] < 128 ? dst[1] * src[1] >> 7
344                        : 255 - ((255 - dst[1]) * (255 - src[1]) >> 7);
345                result[2] = dst[2] < 128 ? dst[2] * src[2] >> 7
346                        : 255 - ((255 - dst[2]) * (255 - src[2]) >> 7);
347                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
348            }
349        },
350
351        /**
352         * The {@code Soft Light} blend mode creates a subtle lighter or darker result depending on
353         * the brightness of the blend color. Blend colors that are more than 50% brightness will
354         * lighten the base image and colors that are less than 50% brightness will darken the base
355         * image. Pure black will create a slightly darker result; pure white will create a slightly
356         * lighter result, and 50% gray will have no effect on the base image.
357         */
358        SOFT_LIGHT {
359            @Override
360            void blend(int[] src, int[] dst, int[] result) {
361                int mRed = src[0] * dst[0] / 255;
362                int mGreen = src[1] * dst[1] / 255;
363                int mBlue = src[2] * dst[2] / 255;
364                result[0] = mRed + dst[0] * (255 - ((255 - dst[0]) * (255 - src[0]) / 255) - mRed) / 255;
365                result[1] = mGreen + dst[1] * (255 - ((255 - dst[1]) * (255 - src[1]) / 255) - mGreen) / 255;
366                result[2] = mBlue + dst[2] * (255 - ((255 - dst[2]) * (255 - src[2]) / 255) - mBlue) / 255;
367                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
368            }
369        },
370
371        /**
372         * {@code Hard Light} drastically lightens or darkens the base image depending on the
373         * brightness of the blend color. The effect is more intense than {@link #SOFT_LIGHT Soft
374         * Light} because the contrast is also increased. Blend colors that are more than 50%
375         * brightness will lighten the base image in the same way as the screen blending mode.
376         * Colors that are less than 50% brightness will darken the base image in the same way as
377         * the multiply blending mode. Pure black will result in black; pure white will create a
378         * white result, and 50% gray will have no effect on the base image.
379         */
380        HARD_LIGHT {
381            @Override
382            void blend(int[] src, int[] dst, int[] result) {
383                result[0] = src[0] < 128 ? dst[0] * src[0] >> 7
384                        : 255 - ((255 - src[0]) * (255 - dst[0]) >> 7);
385                result[1] = src[1] < 128 ? dst[1] * src[1] >> 7
386                        : 255 - ((255 - src[1]) * (255 - dst[1]) >> 7);
387                result[2] = src[2] < 128 ? dst[2] * src[2] >> 7 
388                        : 255 - ((255 - src[2]) * (255 - dst[2]) >> 7);
389                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
390            }
391        },
392
393        /**
394         * Burns or dodges the colors by increasing or decreasing the contrast, depending on the
395         * blend color. If the blend color is lighter than 50% grey, the image is lightened by
396         * decreasing the contrast. If the blend color is darker than 50% grey, the image is
397         * darkened by increasing the contrast.
398         */
399        VIVID_LIGHT {
400            @Override
401            void blend(int[] src, int[] dst, int[] result) {
402                result[0] = src[0] < 128
403                        ? src[0] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[0]) << 7) / src[0])
404                        : src[0] == 255 ? 255 : Math.min(255, (dst[0] << 7) / (255 - src[0]));
405                result[1] = src[1] < 128
406                        ? src[1] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[1]) << 7) / src[1])
407                        : src[1] == 255 ? 255 : Math.min(255, (dst[1] << 7) / (255 - src[1]));
408                result[2] = src[2] < 128
409                        ? src[2] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[2]) << 7) / src[2])
410                        : src[2] == 255 ? 255 : Math.min(255, (dst[2] << 7) / (255 - src[2]));
411                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
412            }
413        },
414        
415        LINEAR_LIGHT {
416            @Override
417            void blend(int[] src, int[] dst, int[] result) {
418                result[0] = src[0] < 128 ? Math.max(0, dst[0] + (src[0] << 1) - 255)
419                        : Math.min(255, dst[0] + (src[0] - 128 << 1));
420                result[1] = src[1] < 128 ? Math.max(0, dst[1] + (src[1] << 1) - 255)
421                        : Math.min(255, dst[1] + (src[1] - 128 << 1));
422                result[2] = src[2] < 128 ? Math.max(0, dst[2] + (src[2] << 1) - 255)
423                        : Math.min(255, dst[2] + (src[2] - 128 << 1));
424                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
425            }
426        },
427        
428        PIN_LIGHT {
429            @Override
430            void blend(int[] src, int[] dst, int[] result) {
431                result[0] = src[0] < 128 ? Math.min(dst[0], src[0] << 1)
432                        : Math.max(dst[0], (src[0] - 128) << 1);
433                result[1] = src[1] < 128 ? Math.min(dst[1], src[1] << 1)
434                        : Math.max(dst[1], (src[1] - 128) << 1);
435                result[2] = src[2] < 128 ? Math.min(dst[2], src[2] << 1)
436                        : Math.max(dst[2], (src[2] - 128) << 1);
437                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
438            }
439        },
440        
441        HARD_MIX {
442            @Override
443            void blend(int[] src, int[] dst, int[] result) {
444                result[0] = src[0] < 256 - dst[0] ? 0 : 255;
445                result[1] = src[1] < 256 - dst[1] ? 0 : 255;
446                result[2] = src[2] < 256 - dst[2] ? 0 : 255;
447                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
448            }
449        },
450        
451        REFLECT {
452            @Override
453            void blend(int[] src, int[] dst, int[] result) {
454                result[0] = src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0]));
455                result[1] = src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1]));
456                result[2] = src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2]));
457                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
458            }
459        },
460        
461        GLOW {
462            @Override
463            void blend(int[] src, int[] dst, int[] result) {
464                result[0] = dst[0] == 255 ? 255 : Math.min(255, src[0] * src[0] / (255 - dst[0]));
465                result[1] = dst[1] == 255 ? 255 : Math.min(255, src[1] * src[1] / (255 - dst[1]));
466                result[2] = dst[2] == 255 ? 255 : Math.min(255, src[2] * src[2] / (255 - dst[2]));
467                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
468            }
469        },
470        
471         FREEZE {
472            @Override
473            void blend(int[] src, int[] dst, int[] result) {
474                result[0] = src[0] == 0 ? 0 : Math.max(0, 255 - (255 - dst[0]) * (255 - dst[0])
475                        / src[0]);
476                result[1] = src[1] == 0 ? 0 : Math.max(0, 255 - (255 - dst[1]) * (255 - dst[1])
477                        / src[1]);
478                result[2] = src[2] == 0 ? 0 : Math.max(0, 255 - (255 - dst[2]) * (255 - dst[2])
479                        / src[2]);
480                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
481            }
482        },
483        
484        HEAT {
485            @Override
486            void blend(int[] src, int[] dst, int[] result) {
487                result[0] = dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0])
488                        / dst[0]);
489                result[1] = dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1])
490                        / dst[1]);
491                result[2] = dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2])
492                        / dst[2]);
493                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
494            }
495        },
496
497        /**
498         * The {@code Difference} blending mode highlights the differences between the blend layer
499         * and the base layer. The more technical explanation is that the blend color is subtracted
500         * from the base color--or vice-versa, depending on the brightness--and the result is the
501         * difference between them. When white is the blend color, the base image is inverted. When
502         * black is the blend color, there is no change.
503         */
504        DIFFERENCE {
505            @Override
506            void blend(int[] src, int[] dst, int[] result) {
507                result[0] = Math.abs(dst[0] - src[0]);
508                result[1] = Math.abs(dst[1] - src[1]);
509                result[2] = Math.abs(dst[2] - src[2]);
510                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
511            }
512        },
513
514        /**
515         * The {@code Exclusion} blending mode works very much like {@link #DIFFERENCE Difference}
516         * but the contrast is lower. When white is the blend color, the base image is inverted.
517         * When black is the blend color, there is no change.
518         */
519        EXCLUSION {
520            @Override
521            void blend(int[] src, int[] dst, int[] result) {
522                result[0] = dst[0] + src[0] - (dst[0] * src[0] >> 7);
523                result[1] = dst[1] + src[1] - (dst[1] * src[1] >> 7);
524                result[2] = dst[2] + src[2] - (dst[2] * src[2] >> 7);
525                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
526            }
527        },
528
529        /**
530         * The {@code Hue} blend mode applies the hue of the blend color to the base image while retaining
531         * the luminance and saturation of the base image. It gives the base image a tinted effect
532         * where the tinting is darkest in areas of high saturation. Where the blend color is a
533         * shade of gray (0% saturation), the base image is desaturated and where the base image is
534         * gray, the Hue blending mode has no effect.
535         */
536        HUE {
537            @Override
538            void blend(int[] src, int[] dst, int[] result) {
539                float[] srcHSL = new float[3];
540                ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
541                float[] dstHSL = new float[3];
542                ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
543
544                ColorUtilities.HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result);
545                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
546            }
547        },
548
549        /**
550         * The {@code Saturation} blending mode applies the saturation of the blend color to the
551         * base image while retaining the hue and luminance of the base image. Neutral tones (black,
552         * white, and gray) in the blend will desaturate the base image. Neutral areas in the base
553         * image will not be changed by the saturation blending mode.
554         */
555        SATURATION {
556            @Override
557            void blend(int[] src, int[] dst, int[] result) {
558                float[] srcHSL = new float[3];
559                ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
560                float[] dstHSL = new float[3];
561                ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
562
563                ColorUtilities.HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result);
564                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
565            }
566        },
567
568        /**
569         * The {@code Color} blending mode applies the hue and saturation of the blend color to the
570         * base image while retaining the luminance of the base image. Simply put, it colors the
571         * base image. Neutral blend colors will desaturate the base image.
572         */
573        COLOR {
574            @Override
575            void blend(int[] src, int[] dst, int[] result) {
576                float[] srcHSL = new float[3];
577                ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
578                float[] dstHSL = new float[3];
579                ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
580
581                ColorUtilities.HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result);
582                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
583            }
584        },
585
586        /**
587         * The {@code Luminosity} blending mode applies the luminosity (brightness) of the blend
588         * colors to the base image while retaining the hue and saturation of the base image.
589         * {@code Luminosity} is the opposite of the {@link #COLOR Color} blending mode.
590         */
591        LUMINOSITY {
592            @Override
593            void blend(int[] src, int[] dst, int[] result) {
594                float[] srcHSL = new float[3];
595                ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
596                float[] dstHSL = new float[3];
597                ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
598
599                ColorUtilities.HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result);
600                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
601            }
602        },
603
604        /**
605         * This one is the "opposite" of difference mode. Note that it is NOT difference mode
606         * inverted, because black and white return the same result, but colors between become
607         * brighter instead of darker. This mode can be used to invert parts of the base image, but
608         * NOT to compare two images.
609         */
610        NEGATION {
611            @Override
612            void blend(int[] src, int[] dst, int[] result) {
613                result[0] = 255 - Math.abs(255 - dst[0] - src[0]);
614                result[1] = 255 - Math.abs(255 - dst[1] - src[1]);
615                result[2] = 255 - Math.abs(255 - dst[2] - src[2]);
616                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
617            }
618        },
619
620        /**
621         * Keeps the red channel from the blend image and the green and blue channels from the base
622         * image.
623         */
624        RED {
625            @Override
626            void blend(int[] src, int[] dst, int[] result) {
627                result[0] = src[0];
628                result[1] = dst[1];
629                result[2] = dst[2];
630                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
631            }
632        },
633        
634        /**
635         * Keeps the green channel from the blend image and the red and blue channels from the base
636         * image.
637         */
638        GREEN {
639            @Override
640            void blend(int[] src, int[] dst, int[] result) {
641                result[0] = dst[0];
642                result[1] = src[1];
643                result[2] = dst[2];
644                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
645            }
646        },
647        
648        /**
649         * Keeps the blue channel from the blend image and the red and green channels from the base
650         * image.
651         */
652        BLUE {
653            @Override
654            void blend(int[] src, int[] dst, int[] result) {
655                result[0] = dst[0];
656                result[1] = dst[1];
657                result[2] = src[2];
658                result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
659            }
660        },
661        ;
662
663        /**
664         * Blends the input colors into the result.
665         * 
666         * @param src
667         *            the source RGBA
668         * @param dst
669         *            the destination RGBA
670         * @param result
671         *            the result RGBA
672         * @throws NullPointerException
673         *             if any argument is {@code null}
674         */
675        abstract void blend(int[] src, int[] dst, int[] result);
676    }
677
678    public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE);
679    public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
680    public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
681    public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
682    public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
683    public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
684    public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT);
685    public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT);
686    public static final BlendComposite VividLight = new BlendComposite(BlendingMode.VIVID_LIGHT);
687    public static final BlendComposite LinearLight = new BlendComposite(BlendingMode.LINEAR_LIGHT);
688    public static final BlendComposite PinLight = new BlendComposite(BlendingMode.PIN_LIGHT);
689    public static final BlendComposite HardMix = new BlendComposite(BlendingMode.HARD_MIX);
690    public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE);
691    public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION);
692    public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION);
693    public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE);
694    public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE);
695    public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE);
696    public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN);
697    public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN);
698    public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN);
699    public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT);
700    public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW);
701    public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE);
702    public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT);
703    public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
704    public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT);
705    public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP);
706    public static final BlendComposite Red = new BlendComposite(BlendingMode.RED);
707    public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN);
708    public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE);
709    public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE);
710    public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION);
711    public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR);
712    public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY);
713
714    private final float alpha;
715    private final BlendingMode mode;
716
717    private BlendComposite(BlendingMode mode) {
718        this(mode, 1.0f);
719    }
720
721    private BlendComposite(BlendingMode mode, float alpha) {
722        this.mode = mode;
723
724        if (alpha < 0.0f || alpha > 1.0f) {
725            throw new IllegalArgumentException(
726                    "alpha must be comprised between 0.0f and 1.0f");
727        }
728        this.alpha = alpha;
729    }
730
731    /**
732     * <p>Creates a new composite based on the blending mode passed
733     * as a parameter. A default opacity of 1.0 is applied.</p>
734     *
735     * @param mode the blending mode defining the compositing rule
736     * @return a new <code>BlendComposite</code> based on the selected blending
737     *   mode, with an opacity of 1.0
738     */
739    public static BlendComposite getInstance(BlendingMode mode) {
740        return new BlendComposite(mode);
741    }
742
743    /**
744     * <p>Creates a new composite based on the blending mode and opacity passed
745     * as parameters. The opacity must be a value between 0.0 and 1.0.</p>
746     *
747     * @param mode the blending mode defining the compositing rule
748     * @param alpha the constant alpha to be multiplied with the alpha of the
749     *   source. <code>alpha</code> must be a floating point between 0.0 and 1.0.
750     * @throws IllegalArgumentException if the opacity is less than 0.0 or
751     *   greater than 1.0
752     * @return a new <code>BlendComposite</code> based on the selected blending
753     *   mode and opacity
754     */
755    public static BlendComposite getInstance(BlendingMode mode, float alpha) {
756        return new BlendComposite(mode, alpha);
757    }
758
759    /**
760     * <p>Returns a <code>BlendComposite</code> object that uses the specified
761     * blending mode and this object's alpha value. If the newly specified
762     * blending mode is the same as this object's, this object is returned.</p>
763     *
764     * @param mode the blending mode defining the compositing rule
765     * @return a <code>BlendComposite</code> object derived from this object,
766     *   that uses the specified blending mode
767     */
768    public BlendComposite derive(BlendingMode mode) {
769        return this.mode == mode ? this : new BlendComposite(mode, getAlpha());
770    }
771
772    /**
773     * <p>Returns a <code>BlendComposite</code> object that uses the specified
774     * opacity, or alpha, and this object's blending mode. If the newly specified
775     * opacity is the same as this object's, this object is returned.</p>
776     *
777     * @param alpha the constant alpha to be multiplied with the alpha of the
778     *   source. <code>alpha</code> must be a floating point between 0.0 and 1.0.
779     * @throws IllegalArgumentException if the opacity is less than 0.0 or
780     *   greater than 1.0
781     * @return a <code>BlendComposite</code> object derived from this object,
782     *   that uses the specified blending mode
783     */
784    public BlendComposite derive(float alpha) {
785        return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha);
786    }
787
788    /**
789     * <p>Returns the opacity of this composite. If no opacity has been defined,
790     * 1.0 is returned.</p>
791     *
792     * @return the alpha value, or opacity, of this object
793     */
794    public float getAlpha() {
795        return alpha;
796    }
797
798    /**
799     * <p>Returns the blending mode of this composite.</p>
800     *
801     * @return the blending mode used by this object
802     */
803    public BlendingMode getMode() {
804        return mode;
805    }
806
807    /**
808     * {@inheritDoc}
809     */
810    @Override
811    public int hashCode() {
812        return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
813    }
814
815    /**
816     * {@inheritDoc}
817     */
818    @Override
819    public boolean equals(Object obj) {
820        if (!(obj instanceof BlendComposite)) {
821            return false;
822        }
823
824        BlendComposite bc = (BlendComposite) obj;
825        return mode == bc.mode && alpha == bc.alpha;
826    }
827
828    private static boolean isRgbColorModel(ColorModel cm) {
829        if (cm instanceof DirectColorModel &&
830                cm.getTransferType() == DataBuffer.TYPE_INT) {
831            DirectColorModel directCM = (DirectColorModel) cm;
832
833            return directCM.getRedMask() == 0x00FF0000 &&
834                   directCM.getGreenMask() == 0x0000FF00 &&
835                   directCM.getBlueMask() == 0x000000FF &&
836                   (directCM.getNumComponents() == 3 ||
837                    directCM.getAlphaMask() == 0xFF000000);
838        }
839
840        return false;
841    }
842
843    private static boolean isBgrColorModel(ColorModel cm) {
844        if (cm instanceof DirectColorModel &&
845                cm.getTransferType() == DataBuffer.TYPE_INT) {
846            DirectColorModel directCM = (DirectColorModel) cm;
847
848            return directCM.getRedMask() == 0x000000FF &&
849                   directCM.getGreenMask() == 0x0000FF00 &&
850                   directCM.getBlueMask() == 0x00FF0000 &&
851                   (directCM.getNumComponents() == 3 ||
852                    directCM.getAlphaMask() == 0xFF000000);
853        }
854
855        return false;
856    }
857
858    /**
859     * {@inheritDoc}
860     */
861    @Override
862    public CompositeContext createContext(ColorModel srcColorModel,
863                                          ColorModel dstColorModel,
864                                          RenderingHints hints) {
865        if (isRgbColorModel(srcColorModel) && isRgbColorModel(dstColorModel)) {
866            return new BlendingRgbContext(this);
867        } else if (isBgrColorModel(srcColorModel) && isBgrColorModel(dstColorModel)) {
868            return new BlendingBgrContext(this);
869        }
870
871        throw new RasterFormatException("Incompatible color models:\n  " + srcColorModel + "\n  " + dstColorModel);
872    }
873
874    private static abstract class BlendingContext implements CompositeContext {
875        protected final BlendComposite composite;
876
877        private BlendingContext(BlendComposite composite) {
878            this.composite = composite;
879        }
880
881        @Override
882        public void dispose() {
883        }
884    }
885
886    private static class BlendingRgbContext extends BlendingContext {
887        private BlendingRgbContext(BlendComposite composite) {
888            super(composite);
889        }
890
891        @Override
892        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
893            int width = Math.min(src.getWidth(), dstIn.getWidth());
894            int height = Math.min(src.getHeight(), dstIn.getHeight());
895
896            float alpha = composite.getAlpha();
897
898            int[] result = new int[4];
899            int[] srcPixel = new int[4];
900            int[] dstPixel = new int[4];
901            int[] srcPixels = new int[width];
902            int[] dstPixels = new int[width];
903
904            for (int y = 0; y < height; y++) {
905                src.getDataElements(0, y, width, 1, srcPixels);
906                dstIn.getDataElements(0, y, width, 1, dstPixels);
907                for (int x = 0; x < width; x++) {
908                    // pixels are stored as INT_ARGB
909                    // our arrays are [R, G, B, A]
910                    int pixel = srcPixels[x];
911                    srcPixel[0] = (pixel >> 16) & 0xFF;
912                    srcPixel[1] = (pixel >>  8) & 0xFF;
913                    srcPixel[2] = (pixel      ) & 0xFF;
914                    srcPixel[3] = (pixel >> 24) & 0xFF;
915
916                    pixel = dstPixels[x];
917                    dstPixel[0] = (pixel >> 16) & 0xFF;
918                    dstPixel[1] = (pixel >>  8) & 0xFF;
919                    dstPixel[2] = (pixel      ) & 0xFF;
920                    dstPixel[3] = (pixel >> 24) & 0xFF;
921
922                    composite.getMode().blend(srcPixel, dstPixel, result);
923
924                    // mixes the result with the opacity
925                    dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
926                                   ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
927                                   ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
928                                    (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
929                }
930                dstOut.setDataElements(0, y, width, 1, dstPixels);
931            }
932        }
933    }
934
935    private static class BlendingBgrContext extends BlendingContext {
936        private BlendingBgrContext(BlendComposite composite) {
937            super(composite);
938        }
939
940        @Override
941        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
942            int width = Math.min(src.getWidth(), dstIn.getWidth());
943            int height = Math.min(src.getHeight(), dstIn.getHeight());
944
945            float alpha = composite.getAlpha();
946
947            int[] result = new int[4];
948            int[] srcPixel = new int[4];
949            int[] dstPixel = new int[4];
950            int[] srcPixels = new int[width];
951            int[] dstPixels = new int[width];
952
953            for (int y = 0; y < height; y++) {
954                src.getDataElements(0, y, width, 1, srcPixels);
955                dstIn.getDataElements(0, y, width, 1, dstPixels);
956                for (int x = 0; x < width; x++) {
957                    // pixels are stored as INT_ABGR
958                    // our arrays are [R, G, B, A]
959                    int pixel = srcPixels[x];
960                    srcPixel[0] = (pixel      ) & 0xFF;
961                    srcPixel[1] = (pixel >>  8) & 0xFF;
962                    srcPixel[2] = (pixel >> 16) & 0xFF;
963                    srcPixel[3] = (pixel >> 24) & 0xFF;
964
965                    pixel = dstPixels[x];
966                    dstPixel[0] = (pixel      ) & 0xFF;
967                    dstPixel[1] = (pixel >>  8) & 0xFF;
968                    dstPixel[2] = (pixel >> 16) & 0xFF;
969                    dstPixel[3] = (pixel >> 24) & 0xFF;
970
971                    composite.getMode().blend(srcPixel, dstPixel, result);
972
973                    // mixes the result with the opacity
974                    dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
975                                   ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF)       |
976                                   ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
977                                   ((int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF) << 16;
978                }
979                dstOut.setDataElements(0, y, width, 1, dstPixels);
980            }
981        }
982    }
983}