| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* $Id$ */ |
| |
| package org.apache.fop.util.bitmap; |
| |
| import java.awt.Color; |
| |
| /** |
| * Utility methods for dithering. |
| */ |
| public class DitherUtil { |
| |
| /** Selects a 2x2 Bayer dither matrix (5 grayscales) */ |
| public static final int DITHER_MATRIX_2X2 = 2; |
| /** Selects a 4x4 Bayer dither matrix (17 grayscales) */ |
| public static final int DITHER_MATRIX_4X4 = 4; |
| /** Selects a 8x8 Bayer dither matrix (65 grayscales) */ |
| public static final int DITHER_MATRIX_8X8 = 8; |
| |
| //Bayer dither matrices (4x4 and 8x8 are derived from the 2x2 matrix) |
| private static final int[] BAYER_D2 = new int[] {0, 2, 3, 1}; |
| private static final int[] BAYER_D4; |
| private static final int[] BAYER_D8; |
| |
| static { |
| BAYER_D4 = deriveBayerMatrix(BAYER_D2); |
| BAYER_D8 = deriveBayerMatrix(BAYER_D4); |
| } |
| |
| private static int[] deriveBayerMatrix(int[] d) { |
| int[] dn = new int[d.length * 4]; |
| int half = (int)Math.sqrt(d.length); |
| for (int part = 0; part < 4; part++) { |
| for (int i = 0, c = d.length; i < c; i++) { |
| setValueInMatrix(dn, half, part, i, d[i] * 4 + BAYER_D2[part]); |
| } |
| } |
| return dn; |
| } |
| |
| private static void setValueInMatrix(int[] dn, int half, int part, int idx, int value) { |
| int xoff = (part & 1) * half; |
| int yoff = (part & 2) * half * half; |
| int matrixIndex = yoff + ((idx / half) * half * 2) + (idx % half) + xoff; |
| dn[matrixIndex] = value; |
| } |
| |
| /** |
| * Returns the Bayer dither base pattern for a particular matrix size. |
| * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4} |
| * or {@link #DITHER_MATRIX_8X8}) |
| * @return the base pattern for the given size |
| */ |
| public static int[] getBayerBasePattern(int matrix) { |
| int[] result = new int[matrix * matrix]; |
| switch (matrix) { |
| case DITHER_MATRIX_2X2: |
| System.arraycopy(BAYER_D2, 0, result, 0, BAYER_D2.length); |
| break; |
| case DITHER_MATRIX_4X4: |
| System.arraycopy(BAYER_D4, 0, result, 0, BAYER_D4.length); |
| break; |
| case DITHER_MATRIX_8X8: |
| System.arraycopy(BAYER_D8, 0, result, 0, BAYER_D8.length); |
| break; |
| default: |
| throw new IllegalArgumentException("Unsupported dither matrix: " + matrix); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a byte array containing the dither pattern for the given 8-bit gray value. |
| * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4} |
| * or {@link #DITHER_MATRIX_8X8}) |
| * @param gray255 the gray value (0-255) |
| * @param doubleMatrix true if the 4x4 matrix shall be doubled to a 8x8 |
| * @return the dither pattern |
| */ |
| public static byte[] getBayerDither(int matrix, int gray255, boolean doubleMatrix) { |
| int ditherIndex; |
| byte[] dither; |
| int[] bayer; |
| switch (matrix) { |
| case DITHER_MATRIX_4X4: |
| ditherIndex = gray255 * 17 / 255; |
| bayer = BAYER_D4; |
| break; |
| case DITHER_MATRIX_8X8: |
| ditherIndex = gray255 * 65 / 255; |
| bayer = BAYER_D8; |
| break; |
| default: |
| throw new IllegalArgumentException("Unsupported dither matrix: " + matrix); |
| } |
| if (doubleMatrix) { |
| if (doubleMatrix && (matrix != DITHER_MATRIX_4X4)) { |
| throw new IllegalArgumentException("doubleMatrix=true is only allowed for 4x4"); |
| } |
| dither = new byte[bayer.length / 8 * 4]; |
| for (int i = 0, c = bayer.length; i < c; i++) { |
| boolean dot = !(bayer[i] < ditherIndex - 1); |
| if (dot) { |
| int byteIdx = i / 4; |
| dither[byteIdx] |= 1 << (i % 4); |
| dither[byteIdx] |= 1 << ((i % 4) + 4); |
| dither[byteIdx + 4] |= 1 << (i % 4); |
| dither[byteIdx + 4] |= 1 << ((i % 4) + 4); |
| } |
| } |
| } else { |
| dither = new byte[bayer.length / 8]; |
| for (int i = 0, c = bayer.length; i < c; i++) { |
| boolean dot = !(bayer[i] < ditherIndex - 1); |
| if (dot) { |
| int byteIdx = i / 8; |
| dither[byteIdx] |= 1 << (i % 8); |
| } |
| } |
| } |
| return dither; |
| } |
| |
| /** |
| * Returns a byte array containing the dither pattern for the given 8-bit gray value. |
| * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4} |
| * or {@link #DITHER_MATRIX_8X8}) |
| * @param col the color |
| * @param doubleMatrix true if the 4x4 matrix shall be doubled to a 8x8 |
| * @return the dither pattern |
| */ |
| public static byte[] getBayerDither(int matrix, Color col, boolean doubleMatrix) { |
| float black = BitmapImageUtil.convertToGray(col.getRGB()) / 256f; |
| return getBayerDither(matrix, Math.round(black * 256), doubleMatrix); |
| } |
| |
| } |