View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.palette;
18  
19  import java.awt.image.BufferedImage;
20  
21  import org.apache.commons.imaging.ImagingException;
22  
23  /**
24   * Dithering algorithms to use when quantizing an image to palette form.
25   */
26  public final class Dithering {
27      private static int adjustPixel(final int argb, final int errA, final int errR, final int errG, final int errB, final int mul) {
28          int a = argb >> 24 & 0xff;
29          int r = argb >> 16 & 0xff;
30          int g = argb >> 8 & 0xff;
31          int b = argb & 0xff;
32  
33          a += errA * mul / 16;
34          r += errR * mul / 16;
35          g += errG * mul / 16;
36          b += errB * mul / 16;
37  
38          if (a < 0) {
39              a = 0;
40          } else if (a > 0xff) {
41              a = 0xff;
42          }
43          if (r < 0) {
44              r = 0;
45          } else if (r > 0xff) {
46              r = 0xff;
47          }
48          if (g < 0) {
49              g = 0;
50          } else if (g > 0xff) {
51              g = 0xff;
52          }
53          if (b < 0) {
54              b = 0;
55          } else if (b > 0xff) {
56              b = 0xff;
57          }
58  
59          return a << 24 | r << 16 | g << 8 | b;
60      }
61  
62      /**
63       * Changes the given image to only use colors from the given palette, applying Floyd-Steinberg dithering in the process. Ensure that your alpha values in
64       * the image and in the palette are consistent.
65       *
66       * @param image   the image to change
67       * @param palette the palette to use
68       * @throws ImagingException if it fails to read the palette index
69       */
70      public static void applyFloydSteinbergDithering(final BufferedImage image, final Palette palette) throws ImagingException {
71          for (int y = 0; y < image.getHeight(); y++) {
72              for (int x = 0; x < image.getWidth(); x++) {
73                  final int argb = image.getRGB(x, y);
74                  final int index = palette.getPaletteIndex(argb);
75                  final int nextArgb = palette.getEntry(index);
76                  image.setRGB(x, y, nextArgb);
77  
78                  final int a = argb >> 24 & 0xff;
79                  final int r = argb >> 16 & 0xff;
80                  final int g = argb >> 8 & 0xff;
81                  final int b = argb & 0xff;
82  
83                  final int na = nextArgb >> 24 & 0xff;
84                  final int nr = nextArgb >> 16 & 0xff;
85                  final int ng = nextArgb >> 8 & 0xff;
86                  final int nb = nextArgb & 0xff;
87  
88                  final int errA = a - na;
89                  final int errR = r - nr;
90                  final int errG = g - ng;
91                  final int errB = b - nb;
92  
93                  if (x + 1 < image.getWidth()) {
94                      int update = adjustPixel(image.getRGB(x + 1, y), errA, errR, errG, errB, 7);
95                      image.setRGB(x + 1, y, update);
96                      if (y + 1 < image.getHeight()) {
97                          update = adjustPixel(image.getRGB(x + 1, y + 1), errA, errR, errG, errB, 1);
98                          image.setRGB(x + 1, y + 1, update);
99                      }
100                 }
101                 if (y + 1 < image.getHeight()) {
102                     int update = adjustPixel(image.getRGB(x, y + 1), errA, errR, errG, errB, 5);
103                     image.setRGB(x, y + 1, update);
104                     if (x - 1 >= 0) {
105                         update = adjustPixel(image.getRGB(x - 1, y + 1), errA, errR, errG, errB, 3);
106                         image.setRGB(x - 1, y + 1, update);
107                     }
108 
109                 }
110             }
111         }
112     }
113 
114     private Dithering() {
115         // no instances.
116     }
117 }