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] 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  */
 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         result = ptr_to_jlong(nsImage);
 121     }
 122 
 123 JNF_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  */
 133 JNIEXPORT 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 
 138 JNF_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 
 167 JNF_COCOA_EXIT(env);
 168 
 169     return result;
 170 }
 171 
 172 /*
 173  * Class:     sun_lwawt_macosx_CImage
 174  * Method:    nativeCreateNSImageFromIconSelector
 175  * Signature: (I)J
 176  */
 177 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromIconSelector
 178 (JNIEnv *env, jclass klass, jint selector)
 179 {
 180     NSImage *image = nil;
 181 
 182 JNF_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 
 190 JNF_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  */
 200 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromFileContents
 201 (JNIEnv *env, jclass klass, jstring file)
 202 {
 203     NSImage *image = nil;
 204 
 205 JNF_COCOA_ENTER(env);
 206 
 207     NSString *path = JNFNormalizedNSStringForPath(env, file);
 208     image = [[NSImage alloc] initByReferencingFile:path];
 209 
 210 JNF_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  */
 220 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageOfFileFromLaunchServices
 221 (JNIEnv *env, jclass klass, jstring file)
 222 {
 223     __block NSImage *image = nil;
 224 
 225 JNF_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 
 233 JNF_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  */
 243 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromImageName
 244 (JNIEnv *env, jclass klass, jstring name)
 245 {
 246     NSImage *image = nil;
 247 
 248 JNF_COCOA_ENTER(env);
 249 
 250     image = [[NSImage imageNamed:JNFJavaToNSString(env, name)] retain];
 251 
 252 JNF_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  */
 262 JNIEXPORT 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 {
 266 JNF_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 
 277 JNF_COCOA_EXIT(env);
 278 }
 279 
 280 /*
 281  * Class:     sun_lwawt_macosx_CImage
 282  * Method:    nativeGetNSImageSize
 283  * Signature: (J)Ljava/awt/geom/Dimension2D;
 284  */
 285 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CImage_nativeGetNSImageSize
 286 (JNIEnv *env, jclass klass, jlong nsImgPtr)
 287 {
 288     jobject size = NULL;
 289 
 290 JNF_COCOA_ENTER(env);
 291 
 292     size = NSToJavaSize(env, [(NSImage *)jlong_to_ptr(nsImgPtr) size]);
 293 
 294 JNF_COCOA_EXIT(env);
 295 
 296     return size;
 297 }
 298 
 299 /*
 300  * Class:     sun_lwawt_macosx_CImage
 301  * Method:    nativeSetNSImageSize
 302  * Signature: (JDD)V
 303  */
 304 JNIEXPORT 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 
 310 JNF_COCOA_ENTER(env);
 311 
 312     [i setScalesWhenResized:TRUE];
 313     [i setSize:NSMakeSize(w, h)];
 314 
 315 JNF_COCOA_EXIT(env);
 316 }
 317 
 318 /*
 319  * Class:     sun_lwawt_macosx_CImage
 320  * Method:    nativeResizeNSImageRepresentations
 321  * Signature: (JDD)V
 322  */
 323 JNIEXPORT 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     
 329 JNF_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     
 338 JNF_COCOA_EXIT(env);
 339 }
 340 
 341 NSComparisonResult 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  */
 350 JNIEXPORT 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 
 358 JNF_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 
 420 JNF_COCOA_EXIT(env);
 421 
 422     return jreturnArray;
 423 }
 424 
 425 /*
 426  * Class:     sun_lwawt_macosx_CImage
 427  * Method:    nativeGetPlatformImageBytes
 428  * Signature: ([III)[B
 429  */
 430 JNIEXPORT 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  */
 459 JNIEXPORT 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 }