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