1/*
2 * Copyright (c) 2011, 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#import "jni_util.h"
26
27#import <Cocoa/Cocoa.h>
28#import <JavaNativeFoundation/JavaNativeFoundation.h>
29
30#import "GeomUtilities.h"
31#import "ThreadUtilities.h"
32
33#import "sun_lwawt_macosx_CImage.h"
34
35
36static void CImage_CopyArrayIntoNSImageRep
37(jint *srcPixels, jint *dstPixels, int width, int height)
38{
39    int x, y;
40    // TODO: test this on big endian systems (not sure if its correct)...
41    for (y = 0; y < height; y++) {
42        for (x = 0; x < width; x++) {
43            jint pix = srcPixels[x];
44            jint a = (pix >> 24) & 0xff;
45            jint r = (pix >> 16) & 0xff;
46            jint g = (pix >>  8) & 0xff;
47            jint b = (pix      ) & 0xff;
48            dstPixels[x] = (b << 24) | (g << 16) | (r << 8) | a;
49        }
50        srcPixels += width; // TODO: use explicit scanStride
51        dstPixels += width;
52    }
53}
54
55static void CImage_CopyNSImageIntoArray
56(NSImage *srcImage, jint *dstPixels, NSRect fromRect, NSRect toRect)
57{
58    int width = toRect.size.width;
59    int height = toRect.size.height;
60    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
61    CGContextRef cgRef = CGBitmapContextCreate(dstPixels, width, height,
62                                8, width * 4, colorspace,
63                                kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
64    CGColorSpaceRelease(colorspace);
65    NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:cgRef flipped:NO];
66    CGContextRelease(cgRef);
67    NSGraphicsContext *oldContext = [[NSGraphicsContext currentContext] retain];
68    [NSGraphicsContext setCurrentContext:context];
69    [srcImage drawInRect:toRect
70                fromRect:fromRect
71               operation:NSCompositeSourceOver
72                fraction:1.0];
73    [NSGraphicsContext setCurrentContext:oldContext];
74    [oldContext release];
75}
76
77static NSBitmapImageRep* CImage_CreateImageRep(JNIEnv *env, jintArray buffer, jint width, jint height)
78{
79    NSBitmapImageRep* imageRep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
80                                                                          pixelsWide:width
81                                                                          pixelsHigh:height
82                                                                       bitsPerSample:8
83                                                                     samplesPerPixel:4
84                                                                            hasAlpha:YES
85                                                                            isPlanar:NO
86                                                                      colorSpaceName:NSDeviceRGBColorSpace
87                                                                        bitmapFormat:NSAlphaFirstBitmapFormat
88                                                                         bytesPerRow:width*4 // TODO: use explicit scanStride
89                                                                        bitsPerPixel:32] autorelease];
90
91    jint *imgData = (jint *)[imageRep bitmapData];
92    if (imgData == NULL) return 0L;
93
94    jint *src = (*env)->GetPrimitiveArrayCritical(env, buffer, NULL);
95    if (src == NULL) return 0L;
96
97    CImage_CopyArrayIntoNSImageRep(src, imgData, width, height);
98
99    (*env)->ReleasePrimitiveArrayCritical(env, buffer, src, JNI_ABORT);
100
101    return imageRep;
102}
103
104/*
105 * Class:     sun_lwawt_macosx_CImage
106 * Method:    nativeCreateNSImageFromArray
107 * Signature: ([III)J
108 */
109JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromArray
110(JNIEnv *env, jclass klass, jintArray buffer, jint width, jint height)
111{
112    jlong result = 0L;
113
114JNF_COCOA_ENTER(env);
115    
116    NSBitmapImageRep* imageRep = CImage_CreateImageRep(env, buffer, width, height);
117    if (imageRep) {
118        NSImage *nsImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
119        [nsImage addRepresentation:imageRep];
120        result = ptr_to_jlong(nsImage);
121    }
122
123JNF_COCOA_EXIT(env);
124
125    return result;
126}
127
128/*
129 * Class:     sun_lwawt_macosx_CImage
130 * Method:    nativeCreateNSImageFromArrays
131 * Signature: ([[I[I[I)J
132 */
133JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromArrays
134(JNIEnv *env, jclass klass, jobjectArray buffers, jintArray widths, jintArray heights)
135{
136    jlong result = 0L;
137
138JNF_COCOA_ENTER(env);
139
140    jsize num = (*env)->GetArrayLength(env, buffers);
141    NSMutableArray * reps = [NSMutableArray arrayWithCapacity: num];
142
143    jint * ws = (*env)->GetIntArrayElements(env, widths, NULL);
144    if (ws != NULL) {
145        jint * hs = (*env)->GetIntArrayElements(env, heights, NULL);
146        if (hs != NULL) {
147            jsize i;
148            for (i = 0; i < num; i++) {
149                jintArray buffer = (*env)->GetObjectArrayElement(env, buffers, i);
150
151                NSBitmapImageRep* imageRep = CImage_CreateImageRep(env, buffer, ws[i], hs[i]);
152                if (imageRep) {
153                    [reps addObject: imageRep];
154                }
155            }
156
157            (*env)->ReleaseIntArrayElements(env, heights, hs, JNI_ABORT);
158        }
159        (*env)->ReleaseIntArrayElements(env, widths, ws, JNI_ABORT);
160    }
161    if ([reps count]) {
162        NSImage *nsImage = [[NSImage alloc] initWithSize:NSMakeSize(0, 0)];
163        [nsImage addRepresentations: reps];
164        result = ptr_to_jlong(nsImage);
165    }
166
167JNF_COCOA_EXIT(env);
168
169    return result;
170}
171
172/*
173 * Class:     sun_lwawt_macosx_CImage
174 * Method:    nativeCreateNSImageFromIconSelector
175 * Signature: (I)J
176 */
177JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromIconSelector
178(JNIEnv *env, jclass klass, jint selector)
179{
180    NSImage *image = nil;
181
182JNF_COCOA_ENTER(env);
183
184    IconRef iconRef;
185    if (noErr == GetIconRef(kOnSystemDisk, kSystemIconsCreator, selector, &iconRef)) {
186        image = [[NSImage alloc] initWithIconRef:iconRef];
187        ReleaseIconRef(iconRef);
188    }
189
190JNF_COCOA_EXIT(env);
191
192    return ptr_to_jlong(image);
193}
194
195/*
196 * Class:     sun_lwawt_macosx_CImage
197 * Method:    nativeCreateNSImageFromFileContents
198 * Signature: (Ljava/lang/String;)J
199 */
200JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromFileContents
201(JNIEnv *env, jclass klass, jstring file)
202{
203    NSImage *image = nil;
204
205JNF_COCOA_ENTER(env);
206
207    NSString *path = JNFNormalizedNSStringForPath(env, file);
208    image = [[NSImage alloc] initByReferencingFile:path];
209
210JNF_COCOA_EXIT(env);
211
212    return ptr_to_jlong(image);
213}
214
215/*
216 * Class:     sun_lwawt_macosx_CImage
217 * Method:    nativeCreateNSImageOfFileFromLaunchServices
218 * Signature: (Ljava/lang/String;)J
219 */
220JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageOfFileFromLaunchServices
221(JNIEnv *env, jclass klass, jstring file)
222{
223    __block NSImage *image = nil;
224
225JNF_COCOA_ENTER(env);
226
227    NSString *path = JNFNormalizedNSStringForPath(env, file);
228    [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
229        image = [[[NSWorkspace sharedWorkspace] iconForFile:path] retain];
230        [image setScalesWhenResized:TRUE];
231    }];
232
233JNF_COCOA_EXIT(env);
234
235    return ptr_to_jlong(image);
236}
237
238/*
239 * Class:     sun_lwawt_macosx_CImage
240 * Method:    nativeCreateNSImageFromImageName
241 * Signature: (Ljava/lang/String;)J
242 */
243JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromImageName
244(JNIEnv *env, jclass klass, jstring name)
245{
246    NSImage *image = nil;
247
248JNF_COCOA_ENTER(env);
249
250    image = [[NSImage imageNamed:JNFJavaToNSString(env, name)] retain];
251
252JNF_COCOA_EXIT(env);
253
254    return ptr_to_jlong(image);
255}
256
257/*
258 * Class:     sun_lwawt_macosx_CImage
259 * Method:    nativeCopyNSImageIntoArray
260 * Signature: (J[IIIII)V
261 */
262JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CImage_nativeCopyNSImageIntoArray
263(JNIEnv *env, jclass klass, jlong nsImgPtr, jintArray buffer, jint sw, jint sh,
264                 jint dw, jint dh)
265{
266JNF_COCOA_ENTER(env);
267
268    NSImage *img = (NSImage *)jlong_to_ptr(nsImgPtr);
269    jint *dst = (*env)->GetPrimitiveArrayCritical(env, buffer, NULL);
270    if (dst) {
271        NSRect fromRect = NSMakeRect(0, 0, sw, sh);
272        NSRect toRect = NSMakeRect(0, 0, dw, dh);
273        CImage_CopyNSImageIntoArray(img, dst, fromRect, toRect);
274        (*env)->ReleasePrimitiveArrayCritical(env, buffer, dst, JNI_ABORT);
275    }
276
277JNF_COCOA_EXIT(env);
278}
279
280/*
281 * Class:     sun_lwawt_macosx_CImage
282 * Method:    nativeGetNSImageSize
283 * Signature: (J)Ljava/awt/geom/Dimension2D;
284 */
285JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CImage_nativeGetNSImageSize
286(JNIEnv *env, jclass klass, jlong nsImgPtr)
287{
288    jobject size = NULL;
289
290JNF_COCOA_ENTER(env);
291
292    size = NSToJavaSize(env, [(NSImage *)jlong_to_ptr(nsImgPtr) size]);
293
294JNF_COCOA_EXIT(env);
295
296    return size;
297}
298
299/*
300 * Class:     sun_lwawt_macosx_CImage
301 * Method:    nativeSetNSImageSize
302 * Signature: (JDD)V
303 */
304JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CImage_nativeSetNSImageSize
305(JNIEnv *env, jclass clazz, jlong image, jdouble w, jdouble h)
306{
307    if (!image) return;
308    NSImage *i = (NSImage *)jlong_to_ptr(image);
309
310JNF_COCOA_ENTER(env);
311
312    [i setScalesWhenResized:TRUE];
313    [i setSize:NSMakeSize(w, h)];
314
315JNF_COCOA_EXIT(env);
316}
317
318/*
319 * Class:     sun_lwawt_macosx_CImage
320 * Method:    nativeResizeNSImageRepresentations
321 * Signature: (JDD)V
322 */
323JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CImage_nativeResizeNSImageRepresentations
324(JNIEnv *env, jclass clazz, jlong image, jdouble w, jdouble h)
325{
326    if (!image) return;
327    NSImage *i = (NSImage *)jlong_to_ptr(image);
328    
329JNF_COCOA_ENTER(env);
330    
331    NSImageRep *imageRep = nil;
332    NSArray *imageRepresentations = [i representations];
333    NSEnumerator *imageEnumerator = [imageRepresentations objectEnumerator];
334    while ((imageRep = [imageEnumerator nextObject]) != nil) {
335        [imageRep setSize:NSMakeSize(w, h)];
336    }
337    
338JNF_COCOA_EXIT(env);
339}
340
341NSComparisonResult getOrder(BOOL order){
342    return (NSComparisonResult) (order ? NSOrderedAscending : NSOrderedDescending);
343}
344
345/*
346 * Class:     sun_lwawt_macosx_CImage
347 * Method:    nativeGetNSImageRepresentationsCount
348 * Signature: (JDD)[Ljava/awt/geom/Dimension2D;
349 */
350JNIEXPORT jobjectArray JNICALL
351                  Java_sun_lwawt_macosx_CImage_nativeGetNSImageRepresentationSizes
352(JNIEnv *env, jclass clazz, jlong image, jdouble w, jdouble h)
353{
354    if (!image) return NULL;
355    jobjectArray jreturnArray = NULL;
356    NSImage *img = (NSImage *)jlong_to_ptr(image);
357
358JNF_COCOA_ENTER(env);
359        
360    NSArray *imageRepresentations = [img representations];
361    if([imageRepresentations count] == 0){
362        return NULL;
363    }
364    
365    NSArray *sortedImageRepresentations = [imageRepresentations
366                    sortedArrayUsingComparator: ^(id obj1, id obj2) {
367        
368        NSImageRep *imageRep1 = (NSImageRep *) obj1;
369        NSImageRep *imageRep2 = (NSImageRep *) obj2;
370        NSSize size1 = [imageRep1 size];
371        NSSize size2 = [imageRep2 size];
372        
373        if (NSEqualSizes(size1, size2)) {
374            return getOrder([imageRep1 pixelsWide] <= [imageRep2 pixelsWide] &&
375                            [imageRep1 pixelsHigh] <= [imageRep2 pixelsHigh]);
376        }
377
378        return getOrder(size1.width <= size2.width && size1.height <= size2.height);
379    }];
380
381    NSMutableArray *sortedPixelSizes = [[[NSMutableArray alloc] init] autorelease];
382    NSSize lastSize = [[sortedImageRepresentations lastObject] size];
383    
384    NSUInteger i = [sortedImageRepresentations indexOfObjectPassingTest:
385               ^BOOL(id obj, NSUInteger idx, BOOL *stop) {
386        NSSize imageRepSize = [obj size];
387        return (w <= imageRepSize.width && h <= imageRepSize.height)
388                   || NSEqualSizes(imageRepSize, lastSize);
389    }];
390
391    NSUInteger count = [sortedImageRepresentations count];
392    i = (i == NSNotFound) ? count - 1 : i;
393    NSSize bestFitSize = [[sortedImageRepresentations objectAtIndex: i] size];
394
395    for(; i < count; i++){
396        NSImageRep *imageRep = [sortedImageRepresentations objectAtIndex: i];
397
398        if (!NSEqualSizes([imageRep size], bestFitSize)) {
399            break;
400        }
401
402        NSSize pixelSize = NSMakeSize(
403                                [imageRep pixelsWide], [imageRep pixelsHigh]);
404        [sortedPixelSizes addObject: [NSValue valueWithSize: pixelSize]];
405    }
406
407    count = [sortedPixelSizes count];
408    static JNF_CLASS_CACHE(jc_Dimension, "java/awt/Dimension");
409    jreturnArray = JNFNewObjectArray(env, &jc_Dimension, count);
410    CHECK_NULL_RETURN(jreturnArray, NULL);
411
412    for(i = 0; i < count; i++){
413        NSSize pixelSize = [[sortedPixelSizes objectAtIndex: i] sizeValue];
414
415        (*env)->SetObjectArrayElement(env, jreturnArray, i,
416                                      NSToJavaSize(env, pixelSize));
417        JNU_CHECK_EXCEPTION_RETURN(env, NULL);
418    }
419
420JNF_COCOA_EXIT(env);
421
422    return jreturnArray;
423}
424
425/*
426 * Class:     sun_lwawt_macosx_CImage
427 * Method:    nativeGetPlatformImageBytes
428 * Signature: ([III)[B
429 */
430JNIEXPORT jbyteArray JNICALL Java_sun_lwawt_macosx_CImage_nativeGetPlatformImageBytes
431(JNIEnv *env, jclass klass, jintArray buffer, jint width, jint height)
432{
433    jbyteArray result = 0L;
434
435    JNF_COCOA_ENTER(env);
436
437    NSBitmapImageRep* imageRep = CImage_CreateImageRep(env, buffer, width, height);
438    if (imageRep) {
439        NSData *tiffImage = [imageRep TIFFRepresentation];
440        jsize tiffSize = (jsize)[tiffImage length];
441        result = (*env)->NewByteArray(env, tiffSize);
442        CHECK_NULL_RETURN(result, nil);
443        jbyte *tiffData = (jbyte *)(*env)->GetPrimitiveArrayCritical(env, result, 0);
444        CHECK_NULL_RETURN(tiffData, nil);
445        [tiffImage getBytes:tiffData];
446        (*env)->ReleasePrimitiveArrayCritical(env, result, tiffData, 0);
447    }
448
449    JNF_COCOA_EXIT(env);
450
451    return result;
452}
453
454/*
455 * Class:     sun_lwawt_macosx_CImage
456 * Method:    nativeCreateNSImageFromBytes
457 * Signature: ([B)J
458 */
459JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromBytes
460(JNIEnv *env, jclass klass, jbyteArray sourceData)
461{
462    jlong result = 0L;
463    CHECK_NULL_RETURN(sourceData, 0L);
464
465    JNF_COCOA_ENTER(env);
466
467    jsize sourceSize = (*env)->GetArrayLength(env, sourceData);
468    if (sourceSize == 0) return 0L;
469
470    jbyte *sourceBytes = (*env)->GetPrimitiveArrayCritical(env, sourceData, NULL);
471    CHECK_NULL_RETURN(sourceBytes, 0L);
472    NSData *rawData = [NSData dataWithBytes:sourceBytes length:sourceSize];
473    NSImage *newImage = [[NSImage alloc] initWithData:rawData];
474
475    (*env)->ReleasePrimitiveArrayCritical(env, sourceData, sourceBytes, JNI_ABORT);
476    CHECK_NULL_RETURN(newImage, 0L);
477
478    result = ptr_to_jlong(newImage);
479    JNF_COCOA_EXIT(env);
480
481    return result;
482}
483