1/*
2 * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "dither.h"
27
28sgn_ordered_dither_array std_img_oda_red;
29sgn_ordered_dither_array std_img_oda_green;
30sgn_ordered_dither_array std_img_oda_blue;
31int std_odas_computed = 0;
32
33void initInverseGrayLut(int* prgb, int rgbsize, ColorData *cData) {
34    int *inverse;
35    int lastindex, lastgray, missing, i;
36
37    if (!cData) {
38        return;
39    }
40
41    inverse = calloc(256, sizeof(int));
42    if (!inverse) {
43        return;
44    }
45    cData->pGrayInverseLutData = inverse;
46
47    for (i = 0; i < 256; i++) {
48        inverse[i] = -1;
49    }
50
51    /* First, fill the gray values */
52    for (i = 0; i < rgbsize; i++) {
53        int r, g, b, rgb = prgb[i];
54        if (rgb == 0x0) {
55            /* ignore transparent black */
56            continue;
57        }
58        r = (rgb >> 16) & 0xff;
59        g = (rgb >> 8 ) & 0xff;
60        b = rgb & 0xff;
61        if (b == r && b == g) {
62            inverse[b] = i;
63        }
64    }
65
66    /* fill the missing gaps by taking the valid values
67     * on either side and filling them halfway into the gap
68     */
69    lastindex = -1;
70    lastgray = -1;
71    missing = 0;
72    for (i = 0; i < 256; i++) {
73        if (inverse[i] < 0) {
74            inverse[i] = lastgray;
75            missing = 1;
76        } else {
77            lastgray = inverse[i];
78            if (missing) {
79                lastindex = lastindex < 0 ? 0 : (i+lastindex)/2;
80                while (lastindex < i) {
81                    inverse[lastindex++] = lastgray;
82                }
83            }
84            lastindex = i;
85            missing = 0;
86        }
87    }
88}
89
90void freeICMColorData(ColorData *pData) {
91    if (CANFREE(pData)) {
92        if (pData->img_clr_tbl) {
93            free(pData->img_clr_tbl);
94        }
95        if (pData->pGrayInverseLutData) {
96            free(pData->pGrayInverseLutData);
97        }
98        free(pData);
99    }
100}
101
102/* REMIND: does not deal well with bifurcation which happens when two
103 * palette entries map to the same cube vertex
104 */
105
106static int
107recurseLevel(CubeStateInfo *priorState) {
108    int i;
109    CubeStateInfo currentState;
110    memcpy(&currentState, priorState, sizeof(CubeStateInfo));
111
112
113    currentState.rgb = (unsigned short *)malloc(6
114                                                * sizeof(unsigned short)
115                                                * priorState->activeEntries);
116    if (currentState.rgb == NULL) {
117        return 0;
118    }
119
120    currentState.indices = (unsigned char *)malloc(6
121                                                * sizeof(unsigned char)
122                                                * priorState->activeEntries);
123
124    if (currentState.indices == NULL) {
125        free(currentState.rgb);
126        return 0;
127    }
128
129    currentState.depth++;
130    if (currentState.depth > priorState->maxDepth) {
131        priorState->maxDepth = currentState.depth;
132    }
133    currentState.activeEntries = 0;
134    for (i=priorState->activeEntries - 1; i >= 0; i--) {
135        unsigned short rgb = priorState->rgb[i];
136        unsigned char  index = priorState->indices[i];
137        ACTIVATE(rgb, 0x7c00, 0x0400, currentState, index);
138        ACTIVATE(rgb, 0x03e0, 0x0020, currentState, index);
139        ACTIVATE(rgb, 0x001f, 0x0001, currentState, index);
140    }
141    if (currentState.activeEntries) {
142        if (!recurseLevel(&currentState)) {
143            free(currentState.rgb);
144            free(currentState.indices);
145            return 0;
146        }
147    }
148    if (currentState.maxDepth > priorState->maxDepth) {
149        priorState->maxDepth = currentState.maxDepth;
150    }
151
152    free(currentState.rgb);
153    free(currentState.indices);
154    return  1;
155}
156
157/*
158 * REMIND: take core inversedLUT calculation to the shared tree and
159 * recode the functions (Win32)awt_Image:initCubemap(),
160 * (Win32)awt_Image:make_cubemap(), (Win32)AwtToolkit::GenerateInverseLUT(),
161 * (Solaris)color:initCubemap() to call the shared codes.
162 */
163unsigned char*
164initCubemap(int* cmap,
165            int  cmap_len,
166            int  cube_dim) {
167    int i;
168    CubeStateInfo currentState;
169    int cubesize = cube_dim * cube_dim * cube_dim;
170    unsigned char *useFlags;
171    unsigned char *newILut = (unsigned char*)malloc(cubesize);
172    int cmap_mid = (cmap_len >> 1) + (cmap_len & 0x1);
173    if (newILut) {
174
175      useFlags = (unsigned char *)calloc(cubesize, 1);
176
177      if (useFlags == 0) {
178          free(newILut);
179#ifdef DEBUG
180        fprintf(stderr, "Out of memory in color:initCubemap()1\n");
181#endif
182          return NULL;
183      }
184
185        currentState.depth          = 0;
186        currentState.maxDepth       = 0;
187        currentState.usedFlags      = useFlags;
188        currentState.activeEntries  = 0;
189        currentState.iLUT           = newILut;
190
191        currentState.rgb = (unsigned short *)
192                                malloc(cmap_len * sizeof(unsigned short));
193        if (currentState.rgb == NULL) {
194            free(newILut);
195            free(useFlags);
196#ifdef DEBUG
197        fprintf(stderr, "Out of memory in color:initCubemap()2\n");
198#endif
199            return NULL;
200        }
201
202        currentState.indices = (unsigned char *)
203                                malloc(cmap_len * sizeof(unsigned char));
204        if (currentState.indices == NULL) {
205            free(currentState.rgb);
206            free(newILut);
207            free(useFlags);
208#ifdef DEBUG
209        fprintf(stderr, "Out of memory in color:initCubemap()3\n");
210#endif
211            return NULL;
212        }
213
214        for (i = 0; i < cmap_mid; i++) {
215            unsigned short rgb;
216            int pixel = cmap[i];
217            rgb = (pixel & 0x00f80000) >> 9;
218            rgb |= (pixel & 0x0000f800) >> 6;
219            rgb |=  (pixel & 0xf8) >> 3;
220            INSERTNEW(currentState, rgb, i);
221            pixel = cmap[cmap_len - i - 1];
222            rgb = (pixel & 0x00f80000) >> 9;
223            rgb |= (pixel & 0x0000f800) >> 6;
224            rgb |=  (pixel & 0xf8) >> 3;
225            INSERTNEW(currentState, rgb, cmap_len - i - 1);
226        }
227
228        if (!recurseLevel(&currentState)) {
229            free(newILut);
230            free(useFlags);
231            free(currentState.rgb);
232            free(currentState.indices);
233#ifdef DEBUG
234        fprintf(stderr, "Out of memory in color:initCubemap()4\n");
235#endif
236            return NULL;
237        }
238
239        free(useFlags);
240        free(currentState.rgb);
241        free(currentState.indices);
242
243        return newILut;
244    }
245
246#ifdef DEBUG
247        fprintf(stderr, "Out of memory in color:initCubemap()5\n");
248#endif
249    return NULL;
250}
251
252void
253initDitherTables(ColorData* cData) {
254
255
256    if(std_odas_computed) {
257        cData->img_oda_red   = &(std_img_oda_red[0][0]);
258        cData->img_oda_green = &(std_img_oda_green[0][0]);
259        cData->img_oda_blue  = &(std_img_oda_blue[0][0]);
260    } else {
261        cData->img_oda_red   = &(std_img_oda_red[0][0]);
262        cData->img_oda_green = &(std_img_oda_green[0][0]);
263        cData->img_oda_blue  = &(std_img_oda_blue[0][0]);
264        make_dither_arrays(256, cData);
265        std_odas_computed = 1;
266    }
267
268}
269
270void make_dither_arrays(int cmapsize, ColorData *cData) {
271    int i, j, k;
272
273    /*
274     * Initialize the per-component ordered dithering arrays
275     * Choose a size based on how far between elements in the
276     * virtual cube.  Assume the cube has cuberoot(cmapsize)
277     * elements per axis and those elements are distributed
278     * over 256 colors.
279     * The calculation should really divide by (#comp/axis - 1)
280     * since the first and last elements are at the extremes of
281     * the 256 levels, but in a practical sense this formula
282     * produces a smaller error array which results in smoother
283     * images that have slightly less color fidelity but much
284     * less dithering noise, especially for grayscale images.
285     */
286    i = (int) (256 / pow(cmapsize, 1.0/3.0));
287    make_sgn_ordered_dither_array(cData->img_oda_red, -i / 2, i / 2);
288    make_sgn_ordered_dither_array(cData->img_oda_green, -i / 2, i / 2);
289    make_sgn_ordered_dither_array(cData->img_oda_blue, -i / 2, i / 2);
290
291    /*
292     * Flip green horizontally and blue vertically so that
293     * the errors don't line up in the 3 primary components.
294     */
295    for (i = 0; i < 8; i++) {
296        for (j = 0; j < 4; j++) {
297            k = cData->img_oda_green[(i<<3)+j];
298            cData->img_oda_green[(i<<3)+j] = cData->img_oda_green[(i<<3)+7 - j];
299            cData->img_oda_green[(i<<3) + 7 - j] = k;
300            k = cData->img_oda_blue[(j<<3)+i];
301            cData->img_oda_blue[(j<<3)+i] = cData->img_oda_blue[((7 - j)<<3)+i];
302            cData->img_oda_blue[((7 - j)<<3) + i] = k;
303        }
304    }
305}
306