1/*
2 * Copyright (c) 1997, 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 <string.h>
27
28#include "jni.h"
29#include "jni_util.h"
30#include "awt_parseImage.h"
31#include "imageInitIDs.h"
32#include "sun_awt_image_ImageRepresentation.h"
33
34static int compareLUTs(unsigned int *lut1, int numLut1, int transIdx,
35                       unsigned int *lut2, int numLut2, unsigned char *cvtLut,
36                       int *retNumLut1, int *retTransIdx, int *jniFlagP);
37
38static int findIdx(unsigned int rgb, unsigned int *lut, int numLut1);
39
40#define ALPHA_MASK    0xff000000
41#ifndef FALSE
42#  define FALSE 0
43#endif
44#ifndef TRUE
45#  define TRUE 1
46#endif
47
48#define CHECK_STRIDE(yy, hh, ss)                            \
49    if ((ss) != 0) {                                        \
50        int limit = 0x7fffffff / ((ss) > 0 ? (ss) : -(ss)); \
51        if (limit < (yy) || limit < ((yy) + (hh) - 1)) {    \
52            /* integer oveflow */                           \
53            return JNI_FALSE;                               \
54        }                                                   \
55    }                                                       \
56
57#define CHECK_SRC()                                      \
58    do {                                                 \
59        int pixeloffset;                                 \
60        if (off < 0 || off >= srcDataLength) {           \
61            return JNI_FALSE;                            \
62        }                                                \
63        CHECK_STRIDE(0, h, scansize);                    \
64                                                         \
65        /* check scansize */                             \
66        pixeloffset = scansize * (h - 1);                \
67        if ((w - 1) > (0x7fffffff - pixeloffset)) {      \
68            return JNI_FALSE;                            \
69        }                                                \
70        pixeloffset += (w - 1);                          \
71                                                         \
72        if (off > (0x7fffffff - pixeloffset)) {          \
73            return JNI_FALSE;                            \
74        }                                                \
75    } while (0)                                          \
76
77#define CHECK_DST(xx, yy)                                \
78    do {                                                 \
79        int soffset = (yy) * sStride;                    \
80        int poffset = (xx) * pixelStride;                \
81        if (poffset > (0x7fffffff - soffset)) {          \
82            return JNI_FALSE;                            \
83        }                                                \
84        poffset += soffset;                              \
85        if (dstDataOff > (0x7fffffff - poffset)) {       \
86            return JNI_FALSE;                            \
87        }                                                \
88        poffset += dstDataOff;                           \
89                                                         \
90        if (poffset < 0 || poffset >= dstDataLength) {   \
91            return JNI_FALSE;                            \
92        }                                                \
93    } while (0)                                          \
94
95static jfieldID s_JnumSrcLUTID;
96static jfieldID s_JsrcLUTtransIndexID;
97
98JNIEXPORT void JNICALL
99Java_sun_awt_image_ImageRepresentation_initIDs(JNIEnv *env, jclass cls) {
100    CHECK_NULL(s_JnumSrcLUTID = (*env)->GetFieldID(env, cls, "numSrcLUT", "I"));
101    CHECK_NULL(s_JsrcLUTtransIndexID = (*env)->GetFieldID(env, cls,
102                                                          "srcLUTtransIndex", "I"));
103}
104
105/*
106 * This routine is used to draw ICM pixels into a default color model
107 */
108JNIEXPORT jboolean JNICALL
109Java_sun_awt_image_ImageRepresentation_setICMpixels(JNIEnv *env, jclass cls,
110                                                    jint x, jint y, jint w,
111                                                    jint h, jintArray jlut,
112                                                    jbyteArray jpix, jint off,
113                                                    jint scansize,
114                                                    jobject jict)
115{
116    unsigned char *srcData = NULL;
117    jint srcDataLength;
118    int *dstData;
119    jint dstDataLength;
120    jint dstDataOff;
121    int *dstP, *dstyP;
122    unsigned char *srcyP, *srcP;
123    int *srcLUT = NULL;
124    int yIdx, xIdx;
125    int sStride;
126    int *cOffs;
127    int pixelStride;
128    jobject joffs = NULL;
129    jobject jdata = NULL;
130
131    if (JNU_IsNull(env, jlut)) {
132        JNU_ThrowNullPointerException(env, "NullPointerException");
133        return JNI_FALSE;
134    }
135
136    if (JNU_IsNull(env, jpix)) {
137        JNU_ThrowNullPointerException(env, "NullPointerException");
138        return JNI_FALSE;
139    }
140
141    if (x < 0 || w < 1 || (0x7fffffff - x) < w) {
142        return JNI_FALSE;
143    }
144
145    if (y < 0 || h < 1 || (0x7fffffff - y) < h) {
146        return JNI_FALSE;
147    }
148
149    sStride = (*env)->GetIntField(env, jict, g_ICRscanstrID);
150    pixelStride = (*env)->GetIntField(env, jict, g_ICRpixstrID);
151    joffs = (*env)->GetObjectField(env, jict, g_ICRdataOffsetsID);
152    jdata = (*env)->GetObjectField(env, jict, g_ICRdataID);
153
154    if (JNU_IsNull(env, jdata)) {
155        /* no destination buffer */
156        return JNI_FALSE;
157    }
158
159    if (JNU_IsNull(env, joffs) || (*env)->GetArrayLength(env, joffs) < 1) {
160        /* invalid data offstes in raster */
161        return JNI_FALSE;
162    }
163
164    srcDataLength = (*env)->GetArrayLength(env, jpix);
165    dstDataLength = (*env)->GetArrayLength(env, jdata);
166
167    cOffs = (int *) (*env)->GetPrimitiveArrayCritical(env, joffs, NULL);
168    if (cOffs == NULL) {
169        (*env)->ExceptionClear(env);
170        JNU_ThrowNullPointerException(env, "Null channel offset array");
171        return JNI_FALSE;
172    }
173
174    dstDataOff = cOffs[0];
175
176    /* the offset array is not needed anymore and can be released */
177    (*env)->ReleasePrimitiveArrayCritical(env, joffs, cOffs, JNI_ABORT);
178    joffs = NULL;
179    cOffs = NULL;
180
181    /* do basic validation: make sure that offsets for
182    * first pixel and for last pixel are safe to calculate and use */
183    CHECK_STRIDE(y, h, sStride);
184    CHECK_STRIDE(x, w, pixelStride);
185
186    CHECK_DST(x, y);
187    CHECK_DST(x + w -1, y + h - 1);
188
189    /* check source array */
190    CHECK_SRC();
191
192    srcLUT = (int *) (*env)->GetPrimitiveArrayCritical(env, jlut, NULL);
193    if (srcLUT == NULL) {
194        (*env)->ExceptionClear(env);
195        JNU_ThrowNullPointerException(env, "Null IndexColorModel LUT");
196        return JNI_FALSE;
197    }
198
199    srcData = (unsigned char *) (*env)->GetPrimitiveArrayCritical(env, jpix,
200                                                                  NULL);
201    if (srcData == NULL) {
202        (*env)->ReleasePrimitiveArrayCritical(env, jlut, srcLUT, JNI_ABORT);
203        (*env)->ExceptionClear(env);
204        JNU_ThrowNullPointerException(env, "Null data array");
205        return JNI_FALSE;
206    }
207
208    dstData = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata, NULL);
209    if (dstData == NULL) {
210        (*env)->ReleasePrimitiveArrayCritical(env, jlut, srcLUT, JNI_ABORT);
211        (*env)->ReleasePrimitiveArrayCritical(env, jpix, srcData, JNI_ABORT);
212        (*env)->ExceptionClear(env);
213        JNU_ThrowNullPointerException(env, "Null tile data array");
214        return JNI_FALSE;
215    }
216
217    dstyP = dstData + dstDataOff + y*sStride + x*pixelStride;
218    srcyP = srcData + off;
219    for (yIdx = 0; yIdx < h; yIdx++, srcyP += scansize, dstyP+=sStride) {
220        srcP = srcyP;
221        dstP = dstyP;
222        for (xIdx = 0; xIdx < w; xIdx++, dstP+=pixelStride) {
223            *dstP = srcLUT[*srcP++];
224        }
225    }
226
227    /* Release the locked arrays */
228    (*env)->ReleasePrimitiveArrayCritical(env, jlut, srcLUT,  JNI_ABORT);
229    (*env)->ReleasePrimitiveArrayCritical(env, jpix, srcData, JNI_ABORT);
230    (*env)->ReleasePrimitiveArrayCritical(env, jdata, dstData, JNI_ABORT);
231
232    return JNI_TRUE;
233}
234
235JNIEXPORT jboolean JNICALL
236Java_sun_awt_image_ImageRepresentation_setDiffICM(JNIEnv *env, jclass cls,
237                                                  jint x, jint y, jint w,
238                                                  jint h, jintArray jlut,
239                                                  jint transIdx, jint numLut,
240                                                  jobject jicm,
241                                                  jbyteArray jpix, jint off,
242                                                  jint scansize,
243                                                  jobject jbct, jint dstDataOff)
244{
245    unsigned int *srcLUT = NULL;
246    unsigned int *newLUT = NULL;
247    int sStride;
248    int pixelStride;
249    int mapSize;
250    jobject jdata = NULL;
251    jobject jnewlut = NULL;
252    jint srcDataLength;
253    jint dstDataLength;
254    unsigned char *srcData;
255    unsigned char *dstData;
256    unsigned char *dataP;
257    unsigned char *pixP;
258    int i;
259    int j;
260    int newNumLut;
261    int newTransIdx;
262    int jniFlag = JNI_ABORT;
263    unsigned char *ydataP;
264    unsigned char *ypixP;
265    unsigned char cvtLut[256];
266
267    if (JNU_IsNull(env, jlut)) {
268        JNU_ThrowNullPointerException(env, "NullPointerException");
269        return JNI_FALSE;
270    }
271
272    if (JNU_IsNull(env, jpix)) {
273        JNU_ThrowNullPointerException(env, "NullPointerException");
274        return JNI_FALSE;
275    }
276
277    if (x < 0 || w < 1 || (0x7fffffff - x) < w) {
278        return JNI_FALSE;
279    }
280
281    if (y < 0 || h < 1 || (0x7fffffff - y) < h) {
282        return JNI_FALSE;
283    }
284
285
286    sStride = (*env)->GetIntField(env, jbct, g_BCRscanstrID);
287    pixelStride =(*env)->GetIntField(env, jbct, g_BCRpixstrID);
288    jdata = (*env)->GetObjectField(env, jbct, g_BCRdataID);
289    jnewlut = (*env)->GetObjectField(env, jicm, g_ICMrgbID);
290    mapSize = (*env)->GetIntField(env, jicm, g_ICMmapSizeID);
291
292    if (numLut < 0 || numLut > 256 || mapSize < 0 || mapSize > 256) {
293        /* Ether old or new ICM has a palette that exceeds capacity
294           of byte data type, so we have to convert the image data
295           to default representation.
296        */
297        return JNI_FALSE;
298    }
299
300    if (JNU_IsNull(env, jdata)) {
301        /* no destination buffer */
302        return JNI_FALSE;
303    }
304
305    srcDataLength = (*env)->GetArrayLength(env, jpix);
306    dstDataLength = (*env)->GetArrayLength(env, jdata);
307
308    CHECK_STRIDE(y, h, sStride);
309    CHECK_STRIDE(x, w, pixelStride);
310
311    CHECK_DST(x, y);
312    CHECK_DST(x + w -1, y + h - 1);
313
314    /* check source array */
315    CHECK_SRC();
316
317    srcLUT = (unsigned int *) (*env)->GetPrimitiveArrayCritical(env, jlut,
318                                                                NULL);
319    if (srcLUT == NULL) {
320        /* out of memory error already thrown */
321        return JNI_FALSE;
322    }
323
324    newLUT = (unsigned int *) (*env)->GetPrimitiveArrayCritical(env, jnewlut,
325                                                                NULL);
326    if (newLUT == NULL) {
327        (*env)->ReleasePrimitiveArrayCritical(env, jlut, srcLUT,
328                                              JNI_ABORT);
329        /* out of memory error already thrown */
330        return JNI_FALSE;
331    }
332
333    newNumLut = numLut;
334    newTransIdx = transIdx;
335    if (compareLUTs(srcLUT, numLut, transIdx, newLUT, mapSize,
336                    cvtLut, &newNumLut, &newTransIdx, &jniFlag) == FALSE) {
337        /* Need to convert to ICR */
338        (*env)->ReleasePrimitiveArrayCritical(env, jlut, srcLUT,
339                                              JNI_ABORT);
340        (*env)->ReleasePrimitiveArrayCritical(env, jnewlut, newLUT, JNI_ABORT);
341        return JNI_FALSE;
342    }
343
344    /* Don't need these any more */
345    (*env)->ReleasePrimitiveArrayCritical(env, jlut, srcLUT, jniFlag);
346    (*env)->ReleasePrimitiveArrayCritical(env, jnewlut, newLUT, JNI_ABORT);
347
348    if (newNumLut != numLut) {
349        /* Need to write back new number of entries in lut */
350        (*env)->SetIntField(env, cls, s_JnumSrcLUTID, newNumLut);
351    }
352
353    if (newTransIdx != transIdx) {
354        (*env)->SetIntField(env, cls, s_JsrcLUTtransIndexID, newTransIdx);
355    }
356
357    srcData = (unsigned char *) (*env)->GetPrimitiveArrayCritical(env, jpix,
358                                                                  NULL);
359    if (srcData == NULL) {
360        /* out of memory error already thrown */
361        return JNI_FALSE;
362    }
363
364    dstData = (unsigned char *) (*env)->GetPrimitiveArrayCritical(env, jdata,
365                                                                  NULL);
366    if (dstData == NULL) {
367        (*env)->ReleasePrimitiveArrayCritical(env, jpix, srcData, JNI_ABORT);
368        /* out of memory error already thrown */
369        return JNI_FALSE;
370    }
371
372    ydataP = dstData + dstDataOff + y*sStride + x*pixelStride;
373    ypixP  = srcData + off;
374
375    for (i=0; i < h; i++) {
376        dataP = ydataP;
377        pixP = ypixP;
378        for (j=0; j < w; j++) {
379            *dataP = cvtLut[*pixP];
380            dataP += pixelStride;
381            pixP++;
382        }
383        ydataP += sStride;
384        ypixP  += scansize;
385    }
386
387    (*env)->ReleasePrimitiveArrayCritical(env, jpix, srcData, JNI_ABORT);
388    (*env)->ReleasePrimitiveArrayCritical(env, jdata, dstData, JNI_ABORT);
389
390    return JNI_TRUE;
391}
392
393static int compareLUTs(unsigned int *lut1, int numLut1, int transIdx,
394                       unsigned int *lut2, int numLut2, unsigned char *cvtLut,
395                       int *retNumLut1, int *retTransIdx, int *jniFlagP)
396{
397    int i;
398    int idx;
399    int newTransIdx = -1;
400    unsigned int rgb;
401    int changed = FALSE;
402    int maxSize = (numLut1 > numLut2 ? numLut1 : numLut2);
403
404    *jniFlagP = JNI_ABORT;
405
406    for (i=0; i < maxSize; i++) {
407        cvtLut[i] = i;
408    }
409
410    for (i=0; i < numLut2; i++) {
411        /* If this slot in new palette is different from the
412         * same slot in current palette, then we try to find
413         * this color in other slots. On failure, add this color
414         * to current palette.
415         */
416        if ((i >= numLut1) ||
417            (lut1[i] != lut2[i]))
418        {
419            rgb = lut2[i];
420            /* Transparent */
421            if ((rgb & ALPHA_MASK) == 0) {
422                if (transIdx == -1) {
423                    if (numLut1 < 256) {
424                        cvtLut[i] = numLut1;
425                        newTransIdx = i;
426                        transIdx = i;
427                        numLut1++;
428                        changed = TRUE;
429                    }
430                    else {
431                        return FALSE;
432                    }
433                }
434                cvtLut[i] = transIdx;
435            }
436            else {
437                if ((idx = findIdx(rgb, lut1, numLut1)) == -1) {
438                    if (numLut1 < 256) {
439                        lut1[numLut1] = rgb;
440                        cvtLut[i] = numLut1;
441                        numLut1++;
442                        changed = TRUE;
443                    }
444                    else {
445                        /* Bad news...  need to convert image */
446                        return FALSE;
447                    }
448                } else {
449                    cvtLut[i] = idx;
450                }
451            }
452        }
453    }
454
455    if (changed) {
456        *jniFlagP = 0;
457        *retNumLut1 = numLut1;
458        if (newTransIdx != -1) {
459            *retTransIdx = newTransIdx;
460        }
461    }
462    return TRUE;
463}
464
465static int findIdx(unsigned int rgb, unsigned int *lut, int numLut) {
466    int i;
467
468    if ((rgb&0xff000000)==0) {
469        for (i=0; i < numLut; i++) {
470            if ((lut[i]&0xff000000)==0) return i;
471        }
472    }
473    else {
474        for (i=0; i < numLut; i++) {
475            if (lut[i] == rgb) return i;
476        }
477    }
478    return -1;
479}
480