1/*
2 * Copyright (c) 1999, 2014, 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 "BufImgSurfaceData.h"
27#include <stdlib.h>
28
29#include "sun_awt_image_BufImgSurfaceData.h"
30
31#include "img_util_md.h"
32#include "jni_util.h"
33/* Define uintptr_t */
34#include "gdefs.h"
35
36/**
37 * This include file contains support code for loops using the
38 * SurfaceData interface to talk to an X11 drawable from native
39 * code.
40 */
41
42static LockFunc                 BufImg_Lock;
43static GetRasInfoFunc           BufImg_GetRasInfo;
44static ReleaseFunc              BufImg_Release;
45static DisposeFunc              BufImg_Dispose;
46
47static ColorData *BufImg_SetupICM(JNIEnv *env, BufImgSDOps *bisdo);
48
49static jfieldID         rgbID;
50static jfieldID         mapSizeID;
51static jfieldID         colorDataID;
52static jfieldID         pDataID;
53static jfieldID         allGrayID;
54
55static jclass           clsICMCD;
56static jmethodID        initICMCDmID;
57/*
58 * Class:     sun_awt_image_BufImgSurfaceData
59 * Method:    initIDs
60 * Signature: ()V
61 */
62JNIEXPORT void JNICALL
63Java_sun_awt_image_BufImgSurfaceData_initIDs
64(JNIEnv *env, jclass bisd, jclass icm, jclass cd)
65{
66    if (sizeof(BufImgRIPrivate) > SD_RASINFO_PRIVATE_SIZE) {
67        JNU_ThrowInternalError(env, "Private RasInfo structure too large!");
68        return;
69    }
70
71    clsICMCD = (*env)->NewWeakGlobalRef(env, cd);
72    JNU_CHECK_EXCEPTION(env);
73    CHECK_NULL(initICMCDmID = (*env)->GetMethodID(env, cd, "<init>", "(J)V"));
74    CHECK_NULL(pDataID = (*env)->GetFieldID(env, cd, "pData", "J"));
75    CHECK_NULL(rgbID = (*env)->GetFieldID(env, icm, "rgb", "[I"));
76    CHECK_NULL(allGrayID = (*env)->GetFieldID(env, icm, "allgrayopaque", "Z"));
77    CHECK_NULL(mapSizeID = (*env)->GetFieldID(env, icm, "map_size", "I"));
78    CHECK_NULL(colorDataID = (*env)->GetFieldID(env, icm, "colorData",
79                                           "Lsun/awt/image/BufImgSurfaceData$ICMColorData;"));
80}
81
82/*
83 * Class:     sun_java2d_SurfaceData
84 * Method:    freeNativeICMData
85 * Signature: (Ljava/awt/image/IndexColorModel;)V
86 */
87JNIEXPORT void JNICALL
88Java_sun_awt_image_BufImgSurfaceData_freeNativeICMData
89    (JNIEnv *env, jclass sd, jlong pData)
90{
91    ColorData *cdata = (ColorData*)jlong_to_ptr(pData);
92    freeICMColorData(cdata);
93}
94
95/*
96 * Class:     sun_awt_image_BufImgSurfaceData
97 * Method:    initOps
98 * Signature: (Ljava/lang/Object;IIIII)V
99 */
100JNIEXPORT void JNICALL
101Java_sun_awt_image_BufImgSurfaceData_initRaster(JNIEnv *env, jobject bisd,
102                                                jobject array,
103                                                jint offset, jint bitoffset,
104                                                jint width, jint height,
105                                                jint pixStr, jint scanStr,
106                                                jobject icm)
107{
108    BufImgSDOps *bisdo =
109        (BufImgSDOps*)SurfaceData_InitOps(env, bisd, sizeof(BufImgSDOps));
110    if (bisdo == NULL) {
111        JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
112        return;
113    }
114    bisdo->sdOps.Lock = BufImg_Lock;
115    bisdo->sdOps.GetRasInfo = BufImg_GetRasInfo;
116    bisdo->sdOps.Release = BufImg_Release;
117    bisdo->sdOps.Unlock = NULL;
118    bisdo->sdOps.Dispose = BufImg_Dispose;
119    bisdo->array = (*env)->NewWeakGlobalRef(env, array);
120    JNU_CHECK_EXCEPTION(env);
121    bisdo->offset = offset;
122    bisdo->bitoffset = bitoffset;
123    bisdo->scanStr = scanStr;
124    bisdo->pixStr = pixStr;
125    if (JNU_IsNull(env, icm)) {
126        bisdo->lutarray = NULL;
127        bisdo->lutsize = 0;
128        bisdo->icm = NULL;
129    } else {
130        jobject lutarray = (*env)->GetObjectField(env, icm, rgbID);
131        bisdo->lutarray = (*env)->NewWeakGlobalRef(env, lutarray);
132        JNU_CHECK_EXCEPTION(env);
133        bisdo->lutsize = (*env)->GetIntField(env, icm, mapSizeID);
134        bisdo->icm = (*env)->NewWeakGlobalRef(env, icm);
135    }
136    bisdo->rasbounds.x1 = 0;
137    bisdo->rasbounds.y1 = 0;
138    bisdo->rasbounds.x2 = width;
139    bisdo->rasbounds.y2 = height;
140}
141
142/*
143 * Method for disposing native BufImgSD
144 */
145static void BufImg_Dispose(JNIEnv *env, SurfaceDataOps *ops)
146{
147    /* ops is assumed non-null as it is checked in SurfaceData_DisposeOps */
148    BufImgSDOps *bisdo = (BufImgSDOps *)ops;
149    (*env)->DeleteWeakGlobalRef(env, bisdo->array);
150    if (bisdo->lutarray != NULL) {
151        (*env)->DeleteWeakGlobalRef(env, bisdo->lutarray);
152    }
153    if (bisdo->icm != NULL) {
154        (*env)->DeleteWeakGlobalRef(env, bisdo->icm);
155    }
156}
157
158static jint BufImg_Lock(JNIEnv *env,
159                        SurfaceDataOps *ops,
160                        SurfaceDataRasInfo *pRasInfo,
161                        jint lockflags)
162{
163    BufImgSDOps *bisdo = (BufImgSDOps *)ops;
164    BufImgRIPrivate *bipriv = (BufImgRIPrivate *) &(pRasInfo->priv);
165
166    if ((lockflags & (SD_LOCK_LUT)) != 0 && JNU_IsNull(env, bisdo->lutarray)) {
167        /* REMIND: Should this be an InvalidPipe exception? */
168        JNU_ThrowNullPointerException(env, "Attempt to lock missing colormap");
169        return SD_FAILURE;
170    }
171    if ((lockflags & SD_LOCK_INVCOLOR) != 0 ||
172        (lockflags & SD_LOCK_INVGRAY) != 0)
173    {
174        bipriv->cData = BufImg_SetupICM(env, bisdo);
175        if (bipriv->cData == NULL) {
176            (*env)->ExceptionClear(env);
177            JNU_ThrowNullPointerException(env, "Could not initialize inverse tables");
178            return SD_FAILURE;
179        }
180    } else {
181        bipriv->cData = NULL;
182    }
183
184    bipriv->lockFlags = lockflags;
185    bipriv->base = NULL;
186    bipriv->lutbase = NULL;
187
188    SurfaceData_IntersectBounds(&pRasInfo->bounds, &bisdo->rasbounds);
189
190    return SD_SUCCESS;
191}
192
193static void BufImg_GetRasInfo(JNIEnv *env,
194                              SurfaceDataOps *ops,
195                              SurfaceDataRasInfo *pRasInfo)
196{
197    BufImgSDOps *bisdo = (BufImgSDOps *)ops;
198    BufImgRIPrivate *bipriv = (BufImgRIPrivate *) &(pRasInfo->priv);
199
200    if ((bipriv->lockFlags & (SD_LOCK_RD_WR)) != 0) {
201        bipriv->base =
202            (*env)->GetPrimitiveArrayCritical(env, bisdo->array, NULL);
203        CHECK_NULL(bipriv->base);
204    }
205    if ((bipriv->lockFlags & (SD_LOCK_LUT)) != 0) {
206        bipriv->lutbase =
207            (*env)->GetPrimitiveArrayCritical(env, bisdo->lutarray, NULL);
208    }
209
210    if (bipriv->base == NULL) {
211        pRasInfo->rasBase = NULL;
212        pRasInfo->pixelStride = 0;
213        pRasInfo->pixelBitOffset = 0;
214        pRasInfo->scanStride = 0;
215    } else {
216        pRasInfo->rasBase = (void *)
217            (((uintptr_t) bipriv->base) + bisdo->offset);
218        pRasInfo->pixelStride = bisdo->pixStr;
219        pRasInfo->pixelBitOffset = bisdo->bitoffset;
220        pRasInfo->scanStride = bisdo->scanStr;
221    }
222    if (bipriv->lutbase == NULL) {
223        pRasInfo->lutBase = NULL;
224        pRasInfo->lutSize = 0;
225    } else {
226        pRasInfo->lutBase = bipriv->lutbase;
227        pRasInfo->lutSize = bisdo->lutsize;
228    }
229    if (bipriv->cData == NULL) {
230        pRasInfo->invColorTable = NULL;
231        pRasInfo->redErrTable = NULL;
232        pRasInfo->grnErrTable = NULL;
233        pRasInfo->bluErrTable = NULL;
234        pRasInfo->representsPrimaries = 0;
235    } else {
236        pRasInfo->invColorTable = bipriv->cData->img_clr_tbl;
237        pRasInfo->redErrTable = bipriv->cData->img_oda_red;
238        pRasInfo->grnErrTable = bipriv->cData->img_oda_green;
239        pRasInfo->bluErrTable = bipriv->cData->img_oda_blue;
240        pRasInfo->invGrayTable = bipriv->cData->pGrayInverseLutData;
241        pRasInfo->representsPrimaries = bipriv->cData->representsPrimaries;
242    }
243}
244
245static void BufImg_Release(JNIEnv *env,
246                           SurfaceDataOps *ops,
247                           SurfaceDataRasInfo *pRasInfo)
248{
249    BufImgSDOps *bisdo = (BufImgSDOps *)ops;
250    BufImgRIPrivate *bipriv = (BufImgRIPrivate *) &(pRasInfo->priv);
251
252    if (bipriv->base != NULL) {
253        jint mode = (((bipriv->lockFlags & (SD_LOCK_WRITE)) != 0)
254                     ? 0 : JNI_ABORT);
255        (*env)->ReleasePrimitiveArrayCritical(env, bisdo->array,
256                                              bipriv->base, mode);
257    }
258    if (bipriv->lutbase != NULL) {
259        (*env)->ReleasePrimitiveArrayCritical(env, bisdo->lutarray,
260                                              bipriv->lutbase, JNI_ABORT);
261    }
262}
263
264static int calculatePrimaryColorsApproximation(int* cmap, unsigned char* cube, int cube_size) {
265    int i, j, k;
266    int index, value, color;
267    // values calculated from cmap
268    int r, g, b;
269    // maximum positive/negative variation allowed for r, g, b values for primary colors
270    int delta = 5;
271    // get the primary color cmap indices from corner of inverse color table
272    for (i = 0; i < cube_size; i += (cube_size - 1)) {
273        for (j = 0; j < cube_size; j += (cube_size - 1)) {
274            for (k = 0; k < cube_size; k += (cube_size - 1)) {
275                // calculate inverse color table index
276                index = i + cube_size * (j + cube_size * k);
277                // get value present in corners of inverse color table
278                value = cube[index];
279                // use the corner values as index for cmap
280                color = cmap[value];
281                // extract r,g,b values from cmap value
282                r = ((color) >> 16) & 0xff;
283                g = ((color) >> 8) & 0xff;
284                b = color & 0xff;
285                /*
286                 * If i/j/k value is 0 optimum value of b/g/r should be 0 but we allow
287                 * maximum positive variation of 5. If i/j/k value is 31 optimum value
288                 * of b/g/r should be 255 but we allow maximum negative variation of 5.
289                 */
290                if (i == 0) {
291                    if (b > delta)
292                        return 0;
293                } else {
294                    if (b < (255 - delta))
295                        return 0;
296                }
297                if (j == 0) {
298                    if (g > delta)
299                        return 0;
300                } else {
301                    if (g < (255 - delta))
302                        return 0;
303                }
304                if (k == 0) {
305                    if (r > delta)
306                        return 0;
307                } else {
308                    if (r < (255 - delta))
309                        return 0;
310                }
311            }
312        }
313    }
314    return 1;
315}
316
317static ColorData *BufImg_SetupICM(JNIEnv *env,
318                                  BufImgSDOps *bisdo)
319{
320    ColorData *cData = NULL;
321    jobject colorData;
322
323    if (JNU_IsNull(env, bisdo->icm)) {
324        return (ColorData *) NULL;
325    }
326
327    colorData = (*env)->GetObjectField(env, bisdo->icm, colorDataID);
328
329    if (JNU_IsNull(env, colorData)) {
330        if (JNU_IsNull(env, clsICMCD)) {
331            // we are unable to create a wrapper object
332            return (ColorData*)NULL;
333        }
334    } else {
335        cData = (ColorData*)JNU_GetLongFieldAsPtr(env, colorData, pDataID);
336    }
337
338    if (cData != NULL) {
339        return cData;
340    }
341
342    cData = (ColorData*)calloc(1, sizeof(ColorData));
343
344    if (cData != NULL) {
345        jboolean allGray
346            = (*env)->GetBooleanField(env, bisdo->icm, allGrayID);
347        int *pRgb = (int *)
348            ((*env)->GetPrimitiveArrayCritical(env, bisdo->lutarray, NULL));
349
350        if (pRgb == NULL) {
351            free(cData);
352            return (ColorData*)NULL;
353        }
354
355        cData->img_clr_tbl = initCubemap(pRgb, bisdo->lutsize, 32);
356        cData->representsPrimaries = calculatePrimaryColorsApproximation(pRgb, cData->img_clr_tbl, 32);
357        if (allGray == JNI_TRUE) {
358            initInverseGrayLut(pRgb, bisdo->lutsize, cData);
359        }
360        (*env)->ReleasePrimitiveArrayCritical(env, bisdo->lutarray, pRgb,
361                                              JNI_ABORT);
362
363        initDitherTables(cData);
364
365        if (JNU_IsNull(env, colorData)) {
366            jlong pData = ptr_to_jlong(cData);
367            colorData = (*env)->NewObjectA(env, clsICMCD, initICMCDmID, (jvalue *)&pData);
368
369            if ((*env)->ExceptionCheck(env))
370            {
371                free(cData);
372                return (ColorData*)NULL;
373            }
374
375            (*env)->SetObjectField(env, bisdo->icm, colorDataID, colorData);
376        }
377    }
378
379    return cData;
380}
381