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