From 39641d346a86f200452730dd60721759f84b8962 Mon Sep 17 00:00:00 2001 From: "cbarrett@mozilla.com" Date: Mon, 17 Sep 2007 11:02:50 -0700 Subject: [PATCH] Bug 393646 - Support reading image data off the clipboard on Mac r=josh,bienvenu sr=roc a=mconnor/drivers --- editor/libeditor/html/nsHTMLDataTransfer.cpp | 4 +- widget/src/cocoa/nsClipboard.mm | 104 ++++++++++++++++--- widget/src/windows/nsImageClipboard.cpp | 5 +- 3 files changed, 91 insertions(+), 22 deletions(-) diff --git a/editor/libeditor/html/nsHTMLDataTransfer.cpp b/editor/libeditor/html/nsHTMLDataTransfer.cpp index 656c4130b495..d46c0afb3c8e 100644 --- a/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -1118,10 +1118,8 @@ NS_IMETHODIMP nsHTMLEditor::PrepareHTMLTransferable(nsITransferable **aTransfera } (*aTransferable)->AddDataFlavor(kHTMLMime); (*aTransferable)->AddDataFlavor(kFileMime); -#ifdef XP_WIN32 - // image pasting from the clipboard is only implemented on Windows right now. + // image pasting from the clipboard is only implemented on Windows & Mac right now. (*aTransferable)->AddDataFlavor(kJPEGImageMime); -#endif } (*aTransferable)->AddDataFlavor(kUnicodeMime); } diff --git a/widget/src/cocoa/nsClipboard.mm b/widget/src/cocoa/nsClipboard.mm index b8b31eac01ea..912d1f664af1 100644 --- a/widget/src/cocoa/nsClipboard.mm +++ b/widget/src/cocoa/nsClipboard.mm @@ -45,6 +45,10 @@ #include "nsMemory.h" #include "nsIImage.h" #include "nsILocalFile.h" +#include "nsStringStream.h" + +// Screenshots use the (undocumented) png pasteboard type. +#define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil #ifdef MOZ_LOGGING #define FORCE_PR_LOG @@ -196,14 +200,72 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, PRInt32 aWhi free(clipboardDataPtr); break; } + else if (flavorStr.EqualsLiteral(kJPEGImageMime) || + flavorStr.EqualsLiteral(kPNGImageMime) || + flavorStr.EqualsLiteral(kGIFImageMime)) { + // Figure out if there's data on the pasteboard we can grab (sanity check) + NSString *type = [cocoaPasteboard availableTypeFromArray:[NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; + if (!type) + continue; - /* - if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || - flavorStr.EqualsLiteral(kGIFImageMime)) { - // We have never supported this on Mac OS X, we could someday but nobody does this. - break; + // Read data off the clipboard, make sure to catch any exceptions (timeouts) + // XXX should convert to @try/@catch someday? + NSData *pasteboardData = nil; + NS_DURING + pasteboardData = [cocoaPasteboard dataForType:type]; + NS_HANDLER + NS_ASSERTION(0, "Exception raised while getting data from the pasteboard."); + NS_ENDHANDLER + if (!pasteboardData) + continue; + + // Figure out what type we're converting to + CFStringRef outputType = NULL; + if (flavorStr.EqualsLiteral(kJPEGImageMime)) + outputType = CFSTR("public.jpeg"); + else if (flavorStr.EqualsLiteral(kPNGImageMime)) + outputType = CFSTR("public.png"); + else if (flavorStr.EqualsLiteral(kGIFImageMime)) + outputType = CFSTR("com.compuserve.gif"); + else + continue; + + // Use ImageIO to interpret the data on the clipboard and transcode. + // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely + // and safely in most cases (like ObjC). A notable exception is CFRelease. + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + (NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat, + (type == NSTIFFPboardType ? @"public.tiff" : @"public.png"), + kCGImageSourceTypeIdentifierHint, nil]; + + CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, + (CFDictionaryRef)options); + NSMutableData *encodedData = [NSMutableData data]; + CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData, + outputType, + 1, NULL); + CGImageDestinationAddImageFromSource(dest, source, 0, NULL); + PRBool successfullyConverted = CGImageDestinationFinalize(dest); + + if (successfullyConverted) { + // Put the converted data in a form Gecko can understand + nsCOMPtr byteStream; + NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)[encodedData bytes], + [encodedData length], NS_ASSIGNMENT_COPY); + + aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); + } + + if (dest) + CFRelease(dest); + if (source) + CFRelease(source); + + if (successfullyConverted) + break; + else + continue; } - */ } return NS_OK; @@ -271,6 +333,15 @@ nsClipboard::HasDataMatchingFlavors(nsISupportsArray* aFlavorList, PRInt32 aWhic *outResult = PR_TRUE; break; } + } else if (flavorStr.EqualsLiteral(kJPEGImageMime) || + flavorStr.EqualsLiteral(kPNGImageMime) || + flavorStr.EqualsLiteral(kGIFImageMime)) { + NSString* availableType = [generalPBoard availableTypeFromArray: + [NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; + if (availableType) { + *outResult = PR_TRUE; + break; + } } } } @@ -369,28 +440,31 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) NULL, 0, kCGRenderingIntentDefault); + CGColorSpaceRelease(colorSpace); CGDataProviderRelease(dataProvider); // Convert the CGImageRef to TIFF data. CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0); CGImageDestinationRef destRef = CGImageDestinationCreateWithData(tiffData, - (CFStringRef)@"public.tiff", + CFSTR("public.tiff"), 1, - nil); - CGImageDestinationAddImage(destRef, imageRef, nil); - CGImageDestinationFinalize(destRef); + NULL); + CGImageDestinationAddImage(destRef, imageRef, NULL); + PRBool successfullyConverted = CGImageDestinationFinalize(destRef); - CGColorSpaceRelease(colorSpace); CGImageRelease(imageRef); - CFRelease(destRef); + if (destRef) + CFRelease(destRef); - if (NS_FAILED(image->UnlockImagePixels(PR_FALSE))) { - CFRelease(tiffData); + if (NS_FAILED(image->UnlockImagePixels(PR_FALSE)) || !successfullyConverted) { + if (tiffData) + CFRelease(tiffData); continue; } [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType]; - CFRelease(tiffData); + if (tiffData) + CFRelease(tiffData); } else if (flavorStr.EqualsLiteral(kFilePromiseMime)) { [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:NSFilesPromisePboardType]; diff --git a/widget/src/windows/nsImageClipboard.cpp b/widget/src/windows/nsImageClipboard.cpp index c473d66c1d50..0c0f9d3faf19 100644 --- a/widget/src/windows/nsImageClipboard.cpp +++ b/widget/src/windows/nsImageClipboard.cpp @@ -43,9 +43,6 @@ #include "nsMemory.h" #include "prmem.h" #include "imgIEncoder.h" -#ifdef MOZILLA_1_8_BRANCH -#define imgIEncoder imgIEncoder_MOZILLA_1_8_BRANCH -#endif #include "nsLiteralString.h" /* Things To Do 11/8/00 @@ -233,7 +230,7 @@ nsImageFromClipboard ::GetEncodedImageStream (unsigned char * aClipboardData, ns nsCOMPtr encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/jpeg", &rv); if (NS_SUCCEEDED(rv)){ rv = encoder->InitFromData(rgbData, 0, width, height, 3 * width /* RGB * # pixels in a row */, - imgIEncoder::INPUT_FORMAT_RGB, NS_LITERAL_STRING("transparency=none")); + imgIEncoder::INPUT_FORMAT_RGB, EmptyString()); if (NS_SUCCEEDED(rv)) encoder->QueryInterface(NS_GET_IID(nsIInputStream), (void **) aInputStream); }