Bug #223909 --> Land the aviary 1.0 changes for supporting copy and paste of windows clipboard images into HTML mail

compose onto the 1.8 branch. Changes were re-reviewed for 1.8 by various folks including module owners: stuart, biesi, glazman

a=asa


git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@179004 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
scott%scott-macgregor.org
2005-08-25 22:35:49 +00:00
parent 964be8bea4
commit 2ee2592948
14 changed files with 935 additions and 23 deletions

View File

@@ -59,9 +59,11 @@ REQUIRES = xpcom \
necko \
pref \
gfx \
imglib2 \
widget \
view \
webshell \
exthandler \
$(NULL)
# Building the full blown HTML Editor so add its source files and objects:

View File

@@ -130,6 +130,10 @@
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsIContentFilter.h"
#include "imgIEncoder.h"
#include "nsIExternalHelperAppService.h"
#include "nsCExternalHandlerService.h"
const PRUnichar nbsp = 160;
@@ -1143,7 +1147,11 @@ NS_IMETHODIMP nsHTMLEditor::PrepareHTMLTransferable(nsITransferable **aTransfera
}
(*aTransferable)->AddDataFlavor(kHTMLMime);
(*aTransferable)->AddDataFlavor(kFileMime);
//(*aTransferable)->AddDataFlavor(kJPEGImageMime);
#ifdef XP_WIN32
// we only support copy and paste of clipboard images on Windows
(*aTransferable)->AddDataFlavor(kJPEGImageMime);
(*aTransferable)->AddDataFlavor(kNativeImageMime);
#endif
}
(*aTransferable)->AddDataFlavor(kUnicodeMime);
}
@@ -1407,14 +1415,58 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable
}
}
}
else if (flavor.Equals(NS_LITERAL_STRING(kNativeImageMime)))
{
nsXPIDLString htmlstr;
nsAutoEditBatch beginBatching(this);
rv = InsertHTMLWithContext(htmlstr, EmptyString(), EmptyString(), flavor, aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
}
else if (0 == nsCRT::strcmp(bestFlavor, kJPEGImageMime))
{
// need to provide a hook from here
// Insert Image code here
printf("Don't know how to insert an image yet!\n");
//nsIImage* image = (nsIImage *)data;
//NS_RELEASE(image);
rv = NS_ERROR_NOT_IMPLEMENTED; // for now give error code
// Insert Image code
nsCOMPtr<nsIClipboardImage> clipboardImage (do_QueryInterface(genericDataObj));
if (clipboardImage)
{
// invoke image encoder
nsCOMPtr<imgIEncoder> imgEncoder (do_CreateInstance("@mozilla.org/image/encoder;2?type=image/jpeg"));
if (imgEncoder)
{
nsCOMPtr<nsIFile> clipboardImageFile;
rv = imgEncoder->EncodeClipboardImage(clipboardImage, getter_AddRefs(clipboardImageFile));
if (NS_FAILED(rv) || !clipboardImageFile)
return rv;
nsCOMPtr<nsIURI> uri;
rv = NS_NewFileURI(getter_AddRefs(uri), clipboardImageFile);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURL> fileURL(do_QueryInterface(uri));
if (fileURL)
{
nsCAutoString urltext;
rv = fileURL->GetSpec(urltext);
if (NS_SUCCEEDED(rv) && !urltext.IsEmpty())
{
stuffToPaste.AssignLiteral("<IMG src=\"");
AppendUTF8toUTF16(urltext, stuffToPaste);
stuffToPaste.AppendLiteral("\" alt=\"\" >");
nsAutoEditBatch beginBatching(this);
rv = InsertHTMLWithContext(stuffToPaste,
nsString(), nsString(), NS_LITERAL_STRING(kFileMime),
aSourceDoc,
aDestinationNode, aDestOffset,
aDoDeleteSelection);
}
}
nsCOMPtr<nsPIExternalAppLauncher> tempFileManager (do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
if (tempFileManager)
tempFileManager->DeleteTemporaryFileOnExit(clipboardImageFile);
}
else
rv = NS_ERROR_NOT_IMPLEMENTED; // for now give error code, we don't know how to handle the image format from the clipboard
}
}
}
nsCRT::free(bestFlavor);
@@ -1927,7 +1979,7 @@ NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste)
// the flavors that we can deal with
const char* const textEditorFlavors[] = { kUnicodeMime, nsnull };
const char* const htmlEditorFlavors[] = { kHTMLMime, kJPEGImageMime, nsnull };
const char* const htmlEditorFlavors[] = { kHTMLMime, kJPEGImageMime, kNativeImageMime, nsnull };
nsCOMPtr<nsISupportsArray> flavorsList =
do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);

View File

@@ -74,6 +74,10 @@
#include "nsPNGDecoder.h"
#endif
#if defined(XP_WIN32) && defined(IMG_BUILD_jpeg)
#include "nsJPEGEncoder.h"
#endif
#ifdef IMG_BUILD_jpeg
// jpeg
#include "nsJPEGDecoder.h"
@@ -102,6 +106,10 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsGIFDecoder2)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsJPEGDecoder)
#endif
#if defined(XP_WIN32) && defined(IMG_BUILD_jpeg)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsJPEGEncoder)
#endif
#ifdef IMG_BUILD_bmp
// bmp
NS_GENERIC_FACTORY_CONSTRUCTOR(nsICODecoder)
@@ -227,6 +235,13 @@ static const nsModuleComponentInfo components[] =
nsJPEGDecoderConstructor, },
#endif
#if defined(XP_WIN32) && defined(IMG_BUILD_jpeg)
{ "JPEG encoder",
NS_JPEGENCODER_CID,
"@mozilla.org/image/encoder;2?type=image/jpeg",
nsJPEGEncoderConstructor, },
#endif
#ifdef IMG_BUILD_bmp
// bmp
{ "ICO Decoder",

View File

@@ -35,11 +35,17 @@ LIBXUL_LIBRARY = 1
REQUIRES = xpcom \
string \
gfx \
widget \
imglib2 \
necko \
$(JPEG_REQUIRES) \
$(NULL)
CPPSRCS = nsJPEGDecoder.cpp
CPPSRCS = nsJPEGDecoder.cpp \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
CPPSRCS += nsJPEGEncoder.cpp
endif
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,567 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the JPEG image encoder.
*
* The Initial Developer of the Original Code is
* Scott MacGregor <mscott@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifdef MOZ_LOGGING
// sorry, this has to be before the pre-compiled header
#define FORCE_PR_LOG /* Allow logging in the release build */
#endif
#include <stdio.h>
#include "nsJPEGEncoder.h"
#include "nsIClipboard.h"
#include "nsIComponentManager.h"
#include "nspr.h"
#include "nsCRT.h"
#include "ImageLogging.h"
#include "jerror.h"
#include "nsILocalFile.h"
#include "nsIOutputStream.h"
#include "nsNetUtil.h"
#include "nsDirectoryServiceDefs.h"
PRLogModuleInfo *gJPEGEncoderLog = PR_NewLogModule("JPEGEncoder");
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
NS_IMPL_ISUPPORTS1(nsJPEGEncoder, imgIEncoder)
METHODDEF(boolean) empty_output_buffer (j_compress_ptr jd);
METHODDEF(void) init_destination (j_compress_ptr jd);
METHODDEF(void) term_destination (j_compress_ptr cinfo);
METHODDEF(void) my_error_exit (j_common_ptr cinfo);
// helper declarations for converting a bitmap to 4 byte RGB data
nsresult ConvertColorBitMap(unsigned char * aBitmapBuffer, PBITMAPINFO pBitMapInfo, unsigned char * aOutputBuffer);nsJPEGEncoder * getEncoderForCompressionInfoStruct(jpeg_compress_struct * cInfo);
struct bitFields {
PRUint32 red;
PRUint32 green;
PRUint32 blue;
PRUint8 redLeftShift;
PRUint8 redRightShift;
PRUint8 greenLeftShift;
PRUint8 greenRightShift;
PRUint8 blueLeftShift;
PRUint8 blueRightShift;
};
void CalcBitShift(bitFields * aColorMask);
/*
* Implementation of a JPEG destination manager object
*/
typedef struct {
/* public fields; must be first in this struct! */
struct jpeg_destination_mgr pub;
nsJPEGEncoder *encoder; // weak reference
} encoder_destination_mgr;
nsJPEGEncoder::nsJPEGEncoder()
{
mBuffer = nsnull;
mBufferLen = mBufferSize = 0;
memset(&mInfo, 0, sizeof(jpeg_compress_struct));
}
nsJPEGEncoder::~nsJPEGEncoder()
{
}
nsresult nsJPEGEncoder::initCompressionInfo()
{
/* We set up the normal JPEG error routines, then override error_exit. */
mInfo.err = jpeg_std_error(&mErr.pub);
mErr.pub.error_exit = my_error_exit;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(mErr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
return NS_ERROR_FAILURE;
}
jpeg_create_compress(&mInfo);
encoder_destination_mgr * destinationManager;
// first, setup the destination manager
if (mInfo.dest == NULL)
{
destinationManager = PR_NEWZAP(encoder_destination_mgr);
if (!destinationManager)
return NS_ERROR_OUT_OF_MEMORY;
mInfo.dest = NS_REINTERPRET_CAST(struct jpeg_destination_mgr *, destinationManager);
}
/* Setup callback functions. */
destinationManager->pub.init_destination = init_destination ;
destinationManager->pub.empty_output_buffer = empty_output_buffer;
destinationManager->pub.term_destination = term_destination;
destinationManager->encoder = this; // note the lack of a reference here. We own the lifetime of the destinationManager object
return NS_OK;
}
nsJPEGEncoder * getEncoderForCompressionInfoStruct(jpeg_compress_struct * cInfo)
{
if (cInfo && cInfo->dest)
{
encoder_destination_mgr *destinationManager = NS_REINTERPRET_CAST(encoder_destination_mgr *, cInfo->dest);
return destinationManager->encoder;
}
return NULL;
}
/** imgIEncoder methods **/
NS_IMETHODIMP nsJPEGEncoder::EncodeClipboardImage(nsIClipboardImage * aClipboardImage, nsIFile ** aImageFile)
{
// this is windows only....
STGMEDIUM stm;
nsresult rv = aClipboardImage->GetNativeImage( (void *) &stm);
NS_ENSURE_SUCCESS(rv, rv);
// test...try writing the bitmap to a file
nsCOMPtr<nsIFile> fileToUse;
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(fileToUse));
fileToUse->Append(NS_LITERAL_STRING("moz-screenshot.jpg"));
nsCOMPtr<nsILocalFile> path = do_QueryInterface(fileToUse);
path->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), path);
NS_ENSURE_SUCCESS(rv, rv);
// It is critical that we do not return early in this routine from here on out.
// We must release our lock and release stm before exiting
HGLOBAL hGlobal = stm.hGlobal;
BYTE * pGlobal = (BYTE *)::GlobalLock (hGlobal);
BITMAPINFO * bitMapInfo = (BITMAPINFO *) pGlobal;
rv = initCompressionInfo();
if (NS_SUCCEEDED(rv))
{
mInfo.in_color_space = JCS_RGB;
mInfo.input_components = 3;
mInfo.data_precision = 8;
mInfo.image_width = (JDIMENSION) bitMapInfo->bmiHeader.biWidth;
mInfo.image_height = (JDIMENSION) bitMapInfo->bmiHeader.biHeight;
unsigned char * rgbData = (unsigned char *) malloc (bitMapInfo->bmiHeader.biWidth * bitMapInfo->bmiHeader.biHeight * 3 /* RGB */);
if (rgbData)
{
rv = ConvertColorBitMap((unsigned char *) (pGlobal + bitMapInfo->bmiHeader.biSize), bitMapInfo, rgbData);
if (NS_SUCCEEDED(rv))
{
jpeg_set_defaults(&mInfo);
if ( bitMapInfo->bmiHeader.biXPelsPerMeter > 0 && bitMapInfo->bmiHeader.biYPelsPerMeter > 0)
{
/* Set JFIF density parameters from the BMP data */
mInfo.X_density = (UINT16) ( bitMapInfo->bmiHeader.biXPelsPerMeter/100); /* 100 cm per meter */
mInfo.Y_density = (UINT16) ( bitMapInfo->bmiHeader.biYPelsPerMeter/100);
mInfo.density_unit = 2; /* dots/cm */
}
jpeg_start_compress(&mInfo, TRUE);
PRInt32 row_stride = bitMapInfo->bmiHeader.biWidth * 3;
JSAMPROW row_pointer[1];
while (mInfo.next_scanline < mInfo.image_height)
{
row_pointer[0] = &rgbData[mInfo.next_scanline * row_stride];
jpeg_write_scanlines(&mInfo, row_pointer, 1);
}
jpeg_finish_compress(&mInfo);
}
free(rgbData);
} // if rgbdata
jpeg_destroy_compress(&mInfo);
}
// close the output file stream. we are done with it
mOutputStream->Close();
// return the file URL to the JPG
NS_IF_ADDREF(*aImageFile = fileToUse);
::GlobalUnlock (hGlobal); // release our lock on the bitmap
aClipboardImage->ReleaseNativeImage( (void *) &stm);
return NS_OK;
}
void InvertRows(unsigned char * aInitialBuffer, PRUint32 sizeOfBuffer, PRUint32 numBytesPerRow)
{
if (!numBytesPerRow)
return;
PRUint32 numRows = sizeOfBuffer / numBytesPerRow;
void * temporaryRowHolder = (void *) nsMemory::Alloc(numBytesPerRow);
PRUint32 currentRow = 0;
PRUint32 lastRow = (numRows - 1) * numBytesPerRow;
while (currentRow < lastRow)
{
// store the current row into a temporary buffer
memcpy(temporaryRowHolder, (void *) &aInitialBuffer[currentRow], numBytesPerRow);
memcpy((void *) &aInitialBuffer[currentRow], (void *)&aInitialBuffer[lastRow], numBytesPerRow);
memcpy((void *) &aInitialBuffer[lastRow], temporaryRowHolder, numBytesPerRow);
lastRow -= numBytesPerRow;
currentRow += numBytesPerRow;
}
nsMemory::Free(temporaryRowHolder);
}
nsresult ConvertColorBitMap(unsigned char * buffer, PBITMAPINFO pBitMapInfo, unsigned char * outBuffer)
{
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap"));
PRUint8 bitCount = pBitMapInfo->bmiHeader.biBitCount;
PRUint32 imageSize = pBitMapInfo->bmiHeader.biSizeImage; // may be zero for BI_RGB bitmaps which means we need to calculate by hand
PRUint32 bytesPerPixel = bitCount / 8;
if (bitCount <= 4)
bytesPerPixel = 1;
// rows are DWORD aligned. Calculate how many real bytes are in each row in the bitmap. This number won't
// correspond to biWidth.
PRUint32 rowSize = (bitCount * pBitMapInfo->bmiHeader.biWidth + 7) / 8; // +7 to round up
if (rowSize % 4)
rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
// if our buffer includes a color map, skip over it
if (bitCount <= 8)
{
PRInt32 bytesToSkip = (pBitMapInfo->bmiHeader.biClrUsed ? pBitMapInfo->bmiHeader.biClrUsed : (1 << bitCount) ) * sizeof(RGBQUAD);
buffer += bytesToSkip;
}
bitFields colorMasks; // only used if biCompression == BI_BITFIELDS
if (pBitMapInfo->bmiHeader.biCompression == BI_BITFIELDS)
{
// color table consists of 3 DWORDS containing the color masks...
colorMasks.red = (*((PRUint32*)&(pBitMapInfo->bmiColors[0])));
colorMasks.green = (*((PRUint32*)&(pBitMapInfo->bmiColors[1])));
colorMasks.blue = (*((PRUint32*)&(pBitMapInfo->bmiColors[2])));
CalcBitShift(&colorMasks);
buffer += 3 * sizeof(DWORD);
}
else if (pBitMapInfo->bmiHeader.biCompression == BI_RGB && !imageSize) // BI_RGB can have a size of zero which means we figure it out
{
// XXX: note use rowSize here and not biWidth. rowSize accounts for the DWORD padding for each row
imageSize = rowSize * pBitMapInfo->bmiHeader.biHeight;
}
// dump out some log information about the bit map info struct
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap biBitCount: %u", bitCount));
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap rowSize: %u", rowSize));
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap biCompression: %u", pBitMapInfo->bmiHeader.biCompression));
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap biWidth: %u", pBitMapInfo->bmiHeader.biWidth));
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap biHeight: %u", pBitMapInfo->bmiHeader.biHeight));
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap biClrUsed: %u", pBitMapInfo->bmiHeader.biClrUsed));
InvertRows(buffer, imageSize, rowSize);
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap bytesPerPixel: %u", bytesPerPixel));
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap calculated imageSize: %u", imageSize));
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("nsJPEGEncoder::ConvertColorBitMap biSizeImage: %u", pBitMapInfo->bmiHeader.biSizeImage));
if (!pBitMapInfo->bmiHeader.biCompression || pBitMapInfo->bmiHeader.biCompression == BI_BITFIELDS)
{
PRUint32 index = 0;
PRUint32 writeIndex = 0;
unsigned char redValue, greenValue, blueValue;
PRUint8 colorTableEntry = 0;
PRInt8 bit; // used for grayscale bitmaps where each bit is a pixel
PRUint32 numPixelsLeftInRow = pBitMapInfo->bmiHeader.biWidth; // how many more pixels do we still need to read for the current row
PRUint32 pos = 0;
while (index < imageSize)
{
switch (bitCount)
{
case 1:
for (bit = 7; bit >= 0 && numPixelsLeftInRow; bit--)
{
colorTableEntry = (buffer[index] >> bit) & 1;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbRed;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbGreen;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbBlue;
numPixelsLeftInRow--;
}
pos += 1;
break;
case 4:
{
// each buffer[index] entry contains data for two pixels.
// read the first pixel
colorTableEntry = buffer[index] >> 4;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbRed;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbGreen;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbBlue;
numPixelsLeftInRow--;
if (numPixelsLeftInRow) // now read the second pixel
{
colorTableEntry = buffer[index] & 0xF;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbRed;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbGreen;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbBlue;
numPixelsLeftInRow--;
}
pos += 1;
}
break;
case 8:
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[buffer[index]].rgbRed;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[buffer[index]].rgbGreen;
outBuffer[writeIndex++] = pBitMapInfo->bmiColors[buffer[index]].rgbBlue;
numPixelsLeftInRow--;
pos += 1;
break;
case 16:
{
PRUint16 num = 0;
num = (PRUint8) buffer[index+1];
num <<= 8;
num |= (PRUint8) buffer[index];
redValue = ((PRUint32) (((float)(num & 0xf800) / 0xf800) * 0xFF0000) & 0xFF0000)>> 16;
greenValue = ((PRUint32)(((float)(num & 0x07E0) / 0x07E0) * 0x00FF00) & 0x00FF00)>> 8;
blueValue = ((PRUint32)(((float)(num & 0x001F) / 0x001F) * 0x0000FF) & 0x0000FF);
// now we have the right RGB values...
outBuffer[writeIndex++] = redValue;
outBuffer[writeIndex++] = greenValue;
outBuffer[writeIndex++] = blueValue;
numPixelsLeftInRow--;
pos += 2;
}
break;
case 32:
case 24:
if (pBitMapInfo->bmiHeader.biCompression == BI_BITFIELDS)
{
PRUint32 val = *((PRUint32*) (buffer + index) );
outBuffer[writeIndex++] = (val & colorMasks.red) >> colorMasks.redRightShift << colorMasks.redLeftShift;
outBuffer[writeIndex++] = (val & colorMasks.green) >> colorMasks.greenRightShift << colorMasks.greenLeftShift;
outBuffer[writeIndex++] = (val & colorMasks.blue) >> colorMasks.blueRightShift << colorMasks.blueLeftShift;
numPixelsLeftInRow--;
pos += 4; // we read in 4 bytes of data in order to process this pixel
}
else
{
outBuffer[writeIndex++] = buffer[index+2];
outBuffer[writeIndex++] = buffer[index+1];
outBuffer[writeIndex++] = buffer[index];
numPixelsLeftInRow--;
pos += bytesPerPixel; // 3 bytes for 24 bit data, 4 bytes for 32 bit data (we skip over the 4th byte)...
}
break;
default:
// This is probably the wrong place to check this...
return NS_ERROR_FAILURE;
}
index += bytesPerPixel; // increment our loop counter
if (!numPixelsLeftInRow)
{
if (rowSize != pos)
{
// advance index to skip over remaining padding bytes
index += (rowSize - pos);
}
numPixelsLeftInRow = pBitMapInfo->bmiHeader.biWidth;
pos = 0;
}
} // while we still have bytes to process
}
PR_LOG(gJPEGEncoderLog, PR_LOG_ALWAYS, ("exiting nsJPEGEncoder::ConvertColorBitMap"));
return NS_OK;
}
static void calcBitmask(PRUint32 aMask, PRUint8& aBegin, PRUint8& aLength)
{
// find the rightmost 1
PRUint8 pos;
PRBool started = PR_FALSE;
aBegin = aLength = 0;
for (pos = 0; pos <= 31; pos++)
{
if (!started && (aMask & (1 << pos)))
{
aBegin = pos;
started = PR_TRUE;
}
else if (started && !(aMask & (1 << pos)))
{
aLength = pos - aBegin;
break;
}
}
}
void CalcBitShift(bitFields * aColorMask)
{
PRUint8 begin, length;
// red
calcBitmask(aColorMask->red, begin, length);
aColorMask->redRightShift = begin;
aColorMask->redLeftShift = 8 - length;
// green
calcBitmask(aColorMask->green, begin, length);
aColorMask->greenRightShift = begin;
aColorMask->greenLeftShift = 8 - length;
// blue
calcBitmask(aColorMask->blue, begin, length);
aColorMask->blueRightShift = begin;
aColorMask->blueLeftShift = 8 - length;
}
/******************************************************************************/
/* data destination manager method
Initialize the destination buffer the JPEG library should write compressed bits into
*/
METHODDEF(void)
init_destination (j_compress_ptr jd)
{
nsJPEGEncoder * encoder = getEncoderForCompressionInfoStruct(jd);
if (!encoder)
return;
if (!encoder->mBuffer)
{
encoder->mBuffer = (JOCTET *)PR_Malloc(OUTPUT_BUF_SIZE);
encoder->mBufferSize = OUTPUT_BUF_SIZE;
}
jd->dest->next_output_byte = encoder->mBuffer;
jd->dest->free_in_buffer = OUTPUT_BUF_SIZE;
}
// called by the JPEG library when our working output buffer is full. We need to take the bits and write
// them to our destination stream. return true if buffer was dumped successfully otherwise return
METHODDEF(boolean) empty_output_buffer (j_compress_ptr cinfo)
{
nsJPEGEncoder * encoder = getEncoderForCompressionInfoStruct(cinfo);
if (!encoder)
return PR_TRUE;
PRUint32 written;
encoder->mOutputStream->Write((const char*) encoder->mBuffer, encoder->mBufferSize, &written);
// XXX: what do we do if we did write the # of bytes we thought we should write?
// now reset the jpeg pointers to the start of the buffer
cinfo->dest->next_output_byte = encoder->mBuffer;
cinfo->dest->free_in_buffer = OUTPUT_BUF_SIZE;
return PR_TRUE;
}
/* Terminate destination --- called by jpeg_finish_compress()() after all
* data has been written to clean up JPEG destination manager. NOT called by
* jpeg_abort() or jpeg_destroy().
*/
METHODDEF(void) term_destination (j_compress_ptr cinfo)
{
nsJPEGEncoder * encoder = getEncoderForCompressionInfoStruct(cinfo);
if (!encoder)
return;
// flush whatever is left in the buffer
PRUint32 written;
encoder->mOutputStream->Write((const char*) encoder->mBuffer, encoder->mBufferSize - cinfo->dest->free_in_buffer , &written);
// if we were an asynch process making calls back to an observer, we would
// make the notification saying we were all done in this method. Since we are blocking,
// do nothing.
}
/* Override the standard error method in the IJG JPEG decoder code.
*/
METHODDEF(void) my_error_exit (j_common_ptr cinfo)
{
nsresult error_code;
encoder_error_mgr *err = (encoder_error_mgr *) cinfo->err;
/* Convert error to a browser error code */
switch (cinfo->err->msg_code) {
case JERR_OUT_OF_MEMORY:
error_code = NS_ERROR_OUT_OF_MEMORY;
default:
error_code = NS_ERROR_FAILURE;
}
#ifdef DEBUG
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
(*cinfo->err->format_message) (cinfo, buffer);
fprintf(stderr, "JPEG decoding error:\n%s\n", buffer);
#endif
/* Return control to the setjmp point. */
longjmp(err->setjmp_buffer, error_code);
}

View File

@@ -0,0 +1,91 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the JPEG image encoder.
*
* The Initial Developer of the Original Code is
* Scott MacGregor <mscott@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsJPEGEncoder_h_
#define nsJPEGEncoder_h_
#include "imgIEncoder.h"
#include "nsIOutputStream.h"
#include <ole2.h> // stgmedium
#include "nsCOMPtr.h"
extern "C" {
#include "jpeglib.h"
}
#include <setjmp.h>
#define NS_JPEGENCODER_CID \
{ /* 13784154-1C73-4635-B75F-F1F21755E1C1 */ \
0x13784154, \
0x1c73, \
0x4635, \
{0xb7, 0x5f, 0xf1, 0xf2, 0x17, 0x55, 0xe1, 0xc1} \
}
// helper structs for interacting with the jpeg library
typedef struct {
struct jpeg_error_mgr pub; /* "public" fields for IJG library*/
jmp_buf setjmp_buffer; /* For handling catastropic errors */
} encoder_error_mgr;
class nsJPEGEncoder : public imgIEncoder
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGIENCODER
nsJPEGEncoder();
virtual ~nsJPEGEncoder();
public:
encoder_error_mgr mErr;
struct jpeg_compress_struct mInfo;
JOCTET *mBuffer; // temporary buffer the jpeg library writes the compressed data to before we write it out to disk
PRUint32 mBufferLen; // amount of data currently in mBuffer
PRUint32 mBufferSize; // size in bytes what mBuffer was created with
nsCOMPtr<nsIOutputStream> mOutputStream;
protected:
nsresult initCompressionInfo();
};
#endif // nsJPEGEncoder_h_

View File

@@ -56,6 +56,7 @@ XPIDLSRCS = \
imgILoad.idl \
imgILoader.idl \
imgIRequest.idl \
imgIEncoder.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is image encoder interface.
*
* The Initial Developer of the Original Code is Scott MacGregor <mscott@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIClipboardImage;
interface nsIFile;
/**
* imgIEncoder interface
* Currently this is a very specific encoder designed to encode a native clipboard image as a JPEG out to disk.
* It is not intended to be a generic image encoder.
*
*/
[scriptable, uuid(CCC5B3AD-3E67-4e3d-97E1-B06B2E96FEF8)]
interface imgIEncoder : nsISupports
{
/**
Encode a native clipboard image
**/
void encodeClipboardImage(in nsIClipboardImage aClipboardImage, out nsIFile aImageFile);
};

View File

@@ -105,6 +105,22 @@ interface nsIClipboard : nsISupports
boolean supportsSelectionClipboard ( ) ;
};
// Interface to a native image format such as the format stored on the clipboard
[scriptable, uuid(DB21EB6C-AEBB-4d16-94EC-BCD8BBF513AE)]
interface nsIClipboardImage : nsISupports
{
// the idl compiler complains if these two methods are scriptable. need to figure out why
[noscript] void setNativeImage(in voidPtr aNativeImageData);
// caller must later call releaseNativeImage to release their reference to the image. nsIClipboardImage
// will keep its own reference until it is destroyed.
[noscript] void getNativeImage(in voidPtr aNativeImageData);
// releaseNativeImage should only be called if you used getNativeImageData. You should pass in the same
// object to both methods. (this routine exists so callers do not have to link in urlmon.lib just to release
// the stgmedium object on windows).
[noscript] void releaseNativeImage(in voidPtr aNativeImageData);
};
%{ C++

View File

@@ -154,6 +154,10 @@
#define NS_CLIPBOARD_CID \
{ 0x8b5314ba, 0xdb01, 0x11d2, { 0x96, 0xce, 0x0, 0x60, 0xb0, 0xfb, 0x99, 0x56 } }
// {B655157E-D264-4d24-B9C9-1E213276AB5A}
#define NS_CLIPBOARDIMAGE_CID \
{ 0xb655157e, 0xd264, 0x4d24, { 0xb9, 0xc9, 0x1e, 0x21, 0x32, 0x76, 0xab, 0x5a } }
// {77221D5A-1DD2-11B2-8C69-C710F15D2ED5}
#define NS_CLIPBOARDHELPER_CID \
{ 0x77221d5a, 0x1dd2, 0x11b2, { 0x8c, 0x69, 0xc7, 0x10, 0xf1, 0x5d, 0x2e, 0xd5 } }

View File

@@ -78,6 +78,10 @@ ifneq ($(OS_ARCH), WINCE)
OS_LIBS += $(call EXPAND_LIBNAME, comctl32 comdlg32 shell32 gdi32 imm32 )
endif
ifndef GNU_CC
OS_LIBS += $(call EXPAND_LIBNAME, urlmon)
endif
SHARED_LIBRARY_LIBS = \
$(DIST)/lib/$(LIB_PREFIX)widget_windows.$(LIB_SUFFIX) \
$(DIST)/lib/$(LIB_PREFIX)xpwidgets_s.$(LIB_SUFFIX) \

View File

@@ -70,6 +70,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
#ifndef WINCE
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardImage)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard)
#endif
@@ -109,6 +110,10 @@ static const nsModuleComponentInfo components[] =
// "@mozilla.org/widget/sound/win;1"
"@mozilla.org/sound;1",
nsSoundConstructor },
{ "nsClipboardImage",
NS_CLIPBOARDIMAGE_CID,
"@mozilla.org/widget/clipboardimage;1",
nsClipboardImageConstructor },
{ "Drag Service",
NS_DRAGSERVICE_CID,
// "@mozilla.org/widget/dragservice/win;1",

View File

@@ -60,8 +60,9 @@
#include "nsPrimitiveHelpers.h"
#include "nsImageClipboard.h"
#include "nsIWidget.h"
#include "nsIComponentManager.h"
#include "nsComponentManagerUtils.h"
#include "nsWidgetsCID.h"
#include "nsGfxCIID.h"
#include "nsCRT.h"
#include "nsNetUtil.h"
@@ -112,7 +113,8 @@ UINT nsClipboard::GetFormat(const char* aMimeStr)
else if (strcmp(aMimeStr, kUnicodeMime) == 0)
format = CF_UNICODETEXT;
#ifndef WINCE
else if (strcmp(aMimeStr, kJPEGImageMime) == 0)
else if (strcmp(aMimeStr, kJPEGImageMime) == 0 ||
strcmp(aMimeStr, kNativeImageMime) == 0)
format = CF_DIB;
else if (strcmp(aMimeStr, kFileMime) == 0 ||
strcmp(aMimeStr, kFilePromiseMime) == 0)
@@ -478,20 +480,16 @@ nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT
#ifndef WINCE
case CF_DIB :
{
HGLOBAL hGlobal = stm.hGlobal;
BYTE * pGlobal = (BYTE *) GlobalLock (hGlobal) ;
BITMAPV4HEADER * header = (BITMAPV4HEADER *)pGlobal;
nsIClipboardImage * nativeImageWrapper = nsnull; // don't use a nsCOMPtr here
result = CallCreateInstance("@mozilla.org/widget/clipboardimage;1", &nativeImageWrapper);
nsImageFromClipboard converter ( header );
nsIImage* image;
converter.GetImage ( &image ); // addrefs for us, don't release
if ( image ) {
*aData = image;
*aLen = sizeof(nsIImage*);
if (nativeImageWrapper)
{
nativeImageWrapper->SetNativeImage((void *) &stm);
*aData = nativeImageWrapper; // note that we never release our ref to nativeImageWrapper. We pass it on to the owner of *aData
*aLen = sizeof(nsIClipboardImage *);
result = NS_OK;
}
GlobalUnlock (hGlobal) ;
} break;
case CF_HDROP :
@@ -651,6 +649,11 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
else
continue; // something wrong with this flavor, keep looking for other data
}
else if ( strcmp(flavorStr, kJPEGImageMime) == 0 || strcmp(flavorStr, kNativeImageMime) == 0) {
// we have image data. We don't want to attempt any conversions here
nsIClipboardImage * image = NS_REINTERPRET_CAST(nsIClipboardImage*, data);
genericDataWrapper = do_QueryInterface(image);
}
else {
// we probably have some form of text. The DOM only wants LF, so convert from Win32 line
// endings to DOM line endings.
@@ -664,6 +667,8 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
NS_ASSERTION ( genericDataWrapper, "About to put null data into the transferable" );
aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLen);
// don't free data if it is an image object
if (strcmp(flavorStr, kJPEGImageMime) && strcmp(flavorStr, kNativeImageMime))
nsMemory::Free ( NS_REINTERPRET_CAST(char*, data) );
res = NS_OK;
@@ -909,3 +914,75 @@ NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(nsISupportsArray *aFlavorList,
return NS_OK;
}
nsClipboardImage::nsClipboardImage()
{
memset(&mStgMedium, 0, sizeof(STGMEDIUM));
}
nsClipboardImage :: ~nsClipboardImage()
{
if(mStgMedium.hGlobal)
ReleaseStgMedium(&mStgMedium);
}
NS_IMPL_ISUPPORTS1(nsClipboardImage, nsIClipboardImage)
static HGLOBAL CopyGlobalMemory(HGLOBAL hSource)
{
if (hSource == NULL)
return NULL;
DWORD nSize = (DWORD)::GlobalSize(hSource);
HGLOBAL hDest = ::GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, nSize);
if (hDest == NULL)
return NULL;
// copy the bits
LPVOID lpSource = ::GlobalLock(hSource);
LPVOID lpDest = ::GlobalLock(hDest);
memcpy(lpDest, lpSource, nSize);
::GlobalUnlock(hDest);
::GlobalUnlock(hSource);
return hDest;
}
NS_IMETHODIMP nsClipboardImage::SetNativeImage(void * aNativeImageData)
{
STGMEDIUM * stg = (STGMEDIUM *)aNativeImageData;
HGLOBAL hDest = CopyGlobalMemory(stg->hGlobal);
if (!hDest)
return NS_ERROR_OUT_OF_MEMORY;
if(mStgMedium.hGlobal)
ReleaseStgMedium(&mStgMedium);
mStgMedium = *stg;
mStgMedium.hGlobal = hDest;
return NS_OK;
}
NS_IMETHODIMP nsClipboardImage::GetNativeImage(void * aNativeImageData)
{
// the caller should be passing in a STGMEDIUM object which we can copy into
HGLOBAL hDest = CopyGlobalMemory(mStgMedium.hGlobal);
if (!hDest)
return NS_ERROR_OUT_OF_MEMORY;
STGMEDIUM * stg = (STGMEDIUM *)aNativeImageData;
*stg = mStgMedium;
stg->hGlobal = hDest;
return NS_OK;
}
NS_IMETHODIMP nsClipboardImage::ReleaseNativeImage(void * aNativeImageData)
{
STGMEDIUM * stg = (STGMEDIUM *) aNativeImageData;
if (stg)
ReleaseStgMedium(stg);
return NS_OK;
}

View File

@@ -42,6 +42,7 @@
#include "nsIObserver.h"
#include "nsIURI.h"
#include <windows.h>
#include <ole2.h>
class nsITransferable;
class nsIClipboardOwner;
@@ -100,6 +101,19 @@ protected:
};
class nsClipboardImage : public nsIClipboardImage {
public:
nsClipboardImage();
~nsClipboardImage();
NS_DECL_ISUPPORTS
NS_DECL_NSICLIPBOARDIMAGE
protected:
STGMEDIUM mStgMedium;
};
#define SET_FORMATETC(fe, cf, td, asp, li, med) \
{\
(fe).cfFormat=cf;\