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 
  36 static 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 
  55 static 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 
  77 static 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];
  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  */
 109 JNIEXPORT 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 
 114 JNF_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         [imageRep release];
 121 
 122         if (nsImage != nil) {
 123             CFRetain(nsImage); // GC
 124         }
 125 
 126         result = ptr_to_jlong(nsImage);
 127     }
 128 
 129 JNF_COCOA_EXIT(env);
 130 
 131     return result;
 132 }
 133 
 134 /*
 135  * Class:     sun_lwawt_macosx_CImage
 136  * Method:    nativeCreateNSImageFromArrays
 137  * Signature: ([[I[I[I)J
 138  */
 139 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromArrays
 140 (JNIEnv *env, jclass klass, jobjectArray buffers, jintArray widths, jintArray heights)
 141 {
 142     jlong result = 0L;
 143 
 144 JNF_COCOA_ENTER(env);
 145 
 146     jsize num = (*env)->GetArrayLength(env, buffers);
 147     NSMutableArray * reps = [NSMutableArray arrayWithCapacity: num];
 148 
 149     jint * ws = (*env)->GetIntArrayElements(env, widths, NULL);
 150     if (ws != NULL) {
 151         jint * hs = (*env)->GetIntArrayElements(env, heights, NULL);
 152         if (hs != NULL) {
 153             jsize i;
 154             for (i = 0; i < num; i++) {
 155                 jintArray buffer = (*env)->GetObjectArrayElement(env, buffers, i);
 156 
 157                 NSBitmapImageRep* imageRep = CImage_CreateImageRep(env, buffer, ws[i], hs[i]);
 158                 if (imageRep) {
 159                     [reps addObject: imageRep];
 160                 }
 161             }
 162 
 163             (*env)->ReleaseIntArrayElements(env, heights, hs, JNI_ABORT);
 164         }
 165         (*env)->ReleaseIntArrayElements(env, widths, ws, JNI_ABORT);
 166     }
 167     if ([reps count]) {
 168         NSImage *nsImage = [[NSImage alloc] initWithSize:NSMakeSize(0, 0)];
 169         [nsImage addRepresentations: reps];
 170 
 171         if (nsImage != nil) {
 172             CFRetain(nsImage); // GC
 173         }
 174 
 175         result = ptr_to_jlong(nsImage);
 176     }
 177 
 178 JNF_COCOA_EXIT(env);
 179 
 180     return result;
 181 }
 182 
 183 /*
 184  * Class:     sun_lwawt_macosx_CImage
 185  * Method:    nativeCreateNSImageFromIconSelector
 186  * Signature: (I)J
 187  */
 188 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromIconSelector
 189 (JNIEnv *env, jclass klass, jint selector)
 190 {
 191     NSImage *image = nil;
 192 
 193 JNF_COCOA_ENTER(env);
 194 
 195     IconRef iconRef;
 196     if (noErr == GetIconRef(kOnSystemDisk, kSystemIconsCreator, selector, &iconRef)) {
 197         image = [[NSImage alloc] initWithIconRef:iconRef];
 198         if (image) CFRetain(image); // GC
 199         ReleaseIconRef(iconRef);
 200     }
 201 
 202 JNF_COCOA_EXIT(env);
 203 
 204     return ptr_to_jlong(image);
 205 }
 206 
 207 /*
 208  * Class:     sun_lwawt_macosx_CImage
 209  * Method:    nativeCreateNSImageFromFileContents
 210  * Signature: (Ljava/lang/String;)J
 211  */
 212 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromFileContents
 213 (JNIEnv *env, jclass klass, jstring file)
 214 {
 215     NSImage *image = nil;
 216 
 217 JNF_COCOA_ENTER(env);
 218 
 219     NSString *path = JNFNormalizedNSStringForPath(env, file);
 220     image = [[NSImage alloc] initByReferencingFile:path];
 221     if (image) CFRetain(image); // GC
 222 
 223 JNF_COCOA_EXIT(env);
 224 
 225     return ptr_to_jlong(image);
 226 }
 227 
 228 /*
 229  * Class:     sun_lwawt_macosx_CImage
 230  * Method:    nativeCreateNSImageOfFileFromLaunchServices
 231  * Signature: (Ljava/lang/String;)J
 232  */
 233 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageOfFileFromLaunchServices
 234 (JNIEnv *env, jclass klass, jstring file)
 235 {
 236     __block NSImage *image = nil;
 237 
 238 JNF_COCOA_ENTER(env);
 239 
 240     NSString *path = JNFNormalizedNSStringForPath(env, file);
 241     [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 242         image = [[NSWorkspace sharedWorkspace] iconForFile:path];
 243         [image setScalesWhenResized:TRUE];
 244         if (image) CFRetain(image); // GC
 245     }];
 246 
 247 JNF_COCOA_EXIT(env);
 248 
 249     return ptr_to_jlong(image);
 250 }
 251 
 252 /*
 253  * Class:     sun_lwawt_macosx_CImage
 254  * Method:    nativeCreateNSImageFromImageName
 255  * Signature: (Ljava/lang/String;)J
 256  */
 257 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromImageName
 258 (JNIEnv *env, jclass klass, jstring name)
 259 {
 260     NSImage *image = nil;
 261 
 262 JNF_COCOA_ENTER(env);
 263 
 264     image = [NSImage imageNamed:JNFJavaToNSString(env, name)];
 265     if (image) CFRetain(image); // GC
 266 
 267 JNF_COCOA_EXIT(env);
 268 
 269     return ptr_to_jlong(image);
 270 }
 271 
 272 /*
 273  * Class:     sun_lwawt_macosx_CImage
 274  * Method:    nativeCopyNSImageIntoArray
 275  * Signature: (J[IIIII)V
 276  */
 277 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CImage_nativeCopyNSImageIntoArray
 278 (JNIEnv *env, jclass klass, jlong nsImgPtr, jintArray buffer, jint sw, jint sh,
 279                  jint dw, jint dh)
 280 {
 281 JNF_COCOA_ENTER(env);
 282 
 283     NSImage *img = (NSImage *)jlong_to_ptr(nsImgPtr);
 284     jint *dst = (*env)->GetPrimitiveArrayCritical(env, buffer, NULL);
 285     if (dst) {
 286         NSRect fromRect = NSMakeRect(0, 0, sw, sh);
 287         NSRect toRect = NSMakeRect(0, 0, dw, dh);
 288         CImage_CopyNSImageIntoArray(img, dst, fromRect, toRect);
 289         (*env)->ReleasePrimitiveArrayCritical(env, buffer, dst, JNI_ABORT);
 290     }
 291 
 292 JNF_COCOA_EXIT(env);
 293 }
 294 
 295 /*
 296  * Class:     sun_lwawt_macosx_CImage
 297  * Method:    nativeGetNSImageSize
 298  * Signature: (J)Ljava/awt/geom/Dimension2D;
 299  */
 300 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CImage_nativeGetNSImageSize
 301 (JNIEnv *env, jclass klass, jlong nsImgPtr)
 302 {
 303     jobject size = NULL;
 304 
 305 JNF_COCOA_ENTER(env);
 306 
 307     size = NSToJavaSize(env, [(NSImage *)jlong_to_ptr(nsImgPtr) size]);
 308 
 309 JNF_COCOA_EXIT(env);
 310 
 311     return size;
 312 }
 313 
 314 /*
 315  * Class:     sun_lwawt_macosx_CImage
 316  * Method:    nativeSetNSImageSize
 317  * Signature: (JDD)V
 318  */
 319 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CImage_nativeSetNSImageSize
 320 (JNIEnv *env, jclass clazz, jlong image, jdouble w, jdouble h)
 321 {
 322     if (!image) return;
 323     NSImage *i = (NSImage *)jlong_to_ptr(image);
 324 
 325 JNF_COCOA_ENTER(env);
 326 
 327     [i setScalesWhenResized:TRUE];
 328     [i setSize:NSMakeSize(w, h)];
 329 
 330 JNF_COCOA_EXIT(env);
 331 }
 332 
 333 /*
 334  * Class:     sun_lwawt_macosx_CImage
 335  * Method:    nativeResizeNSImageRepresentations
 336  * Signature: (JDD)V
 337  */
 338 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CImage_nativeResizeNSImageRepresentations
 339 (JNIEnv *env, jclass clazz, jlong image, jdouble w, jdouble h)
 340 {
 341     if (!image) return;
 342     NSImage *i = (NSImage *)jlong_to_ptr(image);
 343     
 344 JNF_COCOA_ENTER(env);
 345     
 346     NSImageRep *imageRep = nil;
 347     NSArray *imageRepresentations = [i representations];
 348     NSEnumerator *imageEnumerator = [imageRepresentations objectEnumerator];
 349     while ((imageRep = [imageEnumerator nextObject]) != nil) {
 350         [imageRep setSize:NSMakeSize(w, h)];
 351     }
 352     
 353 JNF_COCOA_EXIT(env);
 354 }
 355 
 356 NSComparisonResult getOrder(BOOL order){
 357     return (NSComparisonResult) (order ? NSOrderedAscending : NSOrderedDescending);
 358 }
 359 
 360 /*
 361  * Class:     sun_lwawt_macosx_CImage
 362  * Method:    nativeGetNSImageRepresentationsCount
 363  * Signature: (JDD)[Ljava/awt/geom/Dimension2D;
 364  */
 365 JNIEXPORT jobjectArray JNICALL
 366                   Java_sun_lwawt_macosx_CImage_nativeGetNSImageRepresentationSizes
 367 (JNIEnv *env, jclass clazz, jlong image, jdouble w, jdouble h)
 368 {
 369     if (!image) return NULL;
 370     jobjectArray jreturnArray = NULL;
 371     NSImage *img = (NSImage *)jlong_to_ptr(image);
 372 
 373 JNF_COCOA_ENTER(env);
 374         
 375     NSArray *imageRepresentations = [img representations];
 376     if([imageRepresentations count] == 0){
 377         return NULL;
 378     }
 379     
 380     NSArray *sortedImageRepresentations = [imageRepresentations
 381                     sortedArrayUsingComparator: ^(id obj1, id obj2) {
 382         
 383         NSImageRep *imageRep1 = (NSImageRep *) obj1;
 384         NSImageRep *imageRep2 = (NSImageRep *) obj2;
 385         NSSize size1 = [imageRep1 size];
 386         NSSize size2 = [imageRep2 size];
 387         
 388         if (NSEqualSizes(size1, size2)) {
 389             return getOrder([imageRep1 pixelsWide] <= [imageRep2 pixelsWide] &&
 390                             [imageRep1 pixelsHigh] <= [imageRep2 pixelsHigh]);
 391         }
 392 
 393         return getOrder(size1.width <= size2.width && size1.height <= size2.height);
 394     }];
 395 
 396     NSMutableArray *sortedPixelSizes = [[NSMutableArray alloc] init];
 397     NSSize lastSize = [[sortedImageRepresentations lastObject] size];
 398     
 399     NSUInteger i = [sortedImageRepresentations indexOfObjectPassingTest:
 400                ^BOOL(id obj, NSUInteger idx, BOOL *stop) {
 401         NSSize imageRepSize = [obj size];
 402         return (w <= imageRepSize.width && h <= imageRepSize.height)
 403                    || NSEqualSizes(imageRepSize, lastSize);
 404     }];
 405 
 406     NSUInteger count = [sortedImageRepresentations count];
 407     i = (i == NSNotFound) ? count - 1 : i;
 408     NSSize bestFitSize = [[sortedImageRepresentations objectAtIndex: i] size];
 409 
 410     for(; i < count; i++){
 411         NSImageRep *imageRep = [sortedImageRepresentations objectAtIndex: i];
 412 
 413         if (!NSEqualSizes([imageRep size], bestFitSize)) {
 414             break;
 415         }
 416 
 417         NSSize pixelSize = NSMakeSize(
 418                                 [imageRep pixelsWide], [imageRep pixelsHigh]);
 419         [sortedPixelSizes addObject: [NSValue valueWithSize: pixelSize]];
 420     }
 421 
 422     count = [sortedPixelSizes count];
 423     static JNF_CLASS_CACHE(jc_Dimension, "java/awt/Dimension");
 424     jreturnArray = JNFNewObjectArray(env, &jc_Dimension, count);
 425     CHECK_NULL_RETURN(jreturnArray, NULL);
 426 
 427     for(i = 0; i < count; i++){
 428         NSSize pixelSize = [[sortedPixelSizes objectAtIndex: i] sizeValue];
 429 
 430         (*env)->SetObjectArrayElement(env, jreturnArray, i,
 431                                       NSToJavaSize(env, pixelSize));
 432         JNU_CHECK_EXCEPTION_RETURN(env, NULL);
 433     }
 434 
 435 JNF_COCOA_EXIT(env);
 436 
 437     return jreturnArray;
 438 }
 439 
 440 /*
 441  * Class:     sun_lwawt_macosx_CImage
 442  * Method:    nativeGetPlatformImageBytes
 443  * Signature: ([III)[B
 444  */
 445 JNIEXPORT jbyteArray JNICALL Java_sun_lwawt_macosx_CImage_nativeGetPlatformImageBytes
 446 (JNIEnv *env, jclass klass, jintArray buffer, jint width, jint height)
 447 {
 448     jbyteArray result = 0L;
 449     
 450     JNF_COCOA_ENTER(env);
 451     
 452     NSBitmapImageRep* imageRep = CImage_CreateImageRep(env, buffer, width, height);
 453     if (imageRep) {
 454         NSData *tiffImage = [imageRep TIFFRepresentation];
 455         jsize tiffSize = (jsize)[tiffImage length];
 456         result = (*env)->NewByteArray(env, tiffSize);
 457         CHECK_NULL_RETURN(result, nil);
 458         jbyte *tiffData = (jbyte *)(*env)->GetPrimitiveArrayCritical(env, result, 0);
 459         CHECK_NULL_RETURN(tiffData, nil);
 460         [tiffImage getBytes:tiffData];
 461         (*env)->ReleasePrimitiveArrayCritical(env, result, tiffData, 0);
 462         [imageRep release];
 463     }
 464     
 465     JNF_COCOA_EXIT(env);
 466     
 467     return result;
 468 }
 469 
 470 /*
 471  * Class:     sun_lwawt_macosx_CImage
 472  * Method:    nativeCreateNSImageFromBytes
 473  * Signature: ([B)J
 474  */
 475 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromBytes
 476 (JNIEnv *env, jclass klass, jbyteArray sourceData)
 477 {
 478     jlong result = 0L;
 479     CHECK_NULL_RETURN(sourceData, 0L);
 480     
 481     JNF_COCOA_ENTER(env);
 482     
 483     jsize sourceSize = (*env)->GetArrayLength(env, sourceData);
 484     if (sourceSize == 0) return 0L;
 485     
 486     jbyte *sourceBytes = (*env)->GetPrimitiveArrayCritical(env, sourceData, NULL);
 487     CHECK_NULL_RETURN(sourceBytes, 0L);
 488     NSData *rawData = [NSData dataWithBytes:sourceBytes length:sourceSize];
 489     
 490     NSImage *newImage = [[NSImage alloc] initWithData:rawData];
 491     if (newImage) CFRetain(newImage); // GC
 492     [newImage release];
 493     
 494     (*env)->ReleasePrimitiveArrayCritical(env, sourceData, sourceBytes, JNI_ABORT);
 495     CHECK_NULL_RETURN(newImage, 0L);
 496     
 497     result = ptr_to_jlong(newImage);
 498     JNF_COCOA_EXIT(env);
 499     
 500     return result;
 501 }
 502