Use existing image decoders to handle clipboard BMP data.

This gets rid of the old nsImageClipboard widget code in favor of using
the nsBMPDecoder in imglib.
This commit is contained in:
wolfbeast
2019-02-07 22:06:24 +01:00
committed by Roy Tam
parent 84cc1f3239
commit c15f982d7a
11 changed files with 150 additions and 652 deletions
+7
View File
@@ -58,6 +58,10 @@ DecoderFactory::GetDecoderType(const char* aMimeType)
} else if (!strcmp(aMimeType, IMAGE_BMP_MS)) {
type = DecoderType::BMP;
// BMP_CLIPBOARD
} else if (!strcmp(aMimeType, IMAGE_BMP_MS_CLIPBOARD)) {
type = DecoderType::BMP_CLIPBOARD;
// ICO
} else if (!strcmp(aMimeType, IMAGE_ICO)) {
type = DecoderType::ICO;
@@ -100,6 +104,9 @@ DecoderFactory::GetDecoder(DecoderType aType,
case DecoderType::BMP:
decoder = new nsBMPDecoder(aImage);
break;
case DecoderType::BMP_CLIPBOARD:
decoder = new nsBMPDecoder(aImage, /* aForClipboard */ true);
break;
case DecoderType::ICO:
decoder = new nsICODecoder(aImage);
break;
+1
View File
@@ -34,6 +34,7 @@ enum class DecoderType
GIF,
JPEG,
BMP,
BMP_CLIPBOARD,
ICO,
ICON,
WEBP,
+14 -3
View File
@@ -185,9 +185,11 @@ nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength)
{
}
// Constructor for normal BMP files.
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
: nsBMPDecoder(aImage, State::FILE_HEADER, FILE_HEADER_LENGTH)
// Constructor for normal BMP files or from the clipboard.
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, bool aForClipboard)
: nsBMPDecoder(aImage,
aForClipboard ? State::CLIPBOARD_HEADER : State::FILE_HEADER,
aForClipboard ? BIHSIZE_FIELD_LENGTH : FILE_HEADER_LENGTH)
{
}
@@ -455,6 +457,7 @@ nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
[=](State aState, const char* aData, size_t aLength) {
switch (aState) {
case State::FILE_HEADER: return ReadFileHeader(aData, aLength);
case State::CLIPBOARD_HEADER: return ReadClipboardHeader(aData, aLength);
case State::INFO_HEADER_SIZE: return ReadInfoHeaderSize(aData, aLength);
case State::INFO_HEADER_REST: return ReadInfoHeaderRest(aData, aLength);
case State::BITFIELDS: return ReadBitfields(aData, aLength);
@@ -488,6 +491,14 @@ nsBMPDecoder::ReadFileHeader(const char* aData, size_t aLength)
return Transition::To(State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH);
}
LexerTransition<nsBMPDecoder::State>
nsBMPDecoder::ReadClipboardHeader(const char* aData, size_t aLength)
{
// With the clipboard, the data offset is the header length.
mH.mDataOffset = LittleEndian::readUint32(aData);
return ReadInfoHeaderSize(aData, aLength);
}
// We read the info header in two steps: (a) read the mBIHSize field to
// determine how long the header is; (b) read the rest of the header.
LexerTransition<nsBMPDecoder::State>
+4 -2
View File
@@ -152,6 +152,7 @@ private:
enum class State {
FILE_HEADER,
CLIPBOARD_HEADER,
INFO_HEADER_SIZE,
INFO_HEADER_REST,
BITFIELDS,
@@ -164,8 +165,8 @@ private:
RLE_ABSOLUTE
};
// This is the constructor used for normal BMP images.
explicit nsBMPDecoder(RasterImage* aImage);
// This is the constructor used for normal and clipboard BMP images.
explicit nsBMPDecoder(RasterImage* aImage, bool aForClipboard = false);
// This is the constructor used for BMP resources in ICO images.
nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset);
@@ -180,6 +181,7 @@ private:
void FinishRow();
LexerTransition<State> ReadFileHeader(const char* aData, size_t aLength);
LexerTransition<State> ReadClipboardHeader(const char* aData, size_t aLength);
LexerTransition<State> ReadInfoHeaderSize(const char* aData, size_t aLength);
LexerTransition<State> ReadInfoHeaderRest(const char* aData, size_t aLength);
LexerTransition<State> ReadBitfields(const char* aData, size_t aLength);
+3
View File
@@ -107,6 +107,9 @@
#define IMAGE_TIFF "image/tiff"
#define IMAGE_BMP "image/bmp"
#define IMAGE_BMP_MS "image/x-ms-bmp"
// This is used internally to represent Windows clipboard BMPs which remove
// part of the header.
#define IMAGE_BMP_MS_CLIPBOARD "image/x-ms-clipboard-bmp"
#define IMAGE_ICO "image/x-icon"
#define IMAGE_ICO_MS "image/vnd.microsoft.icon"
#define IMAGE_ICON_MS "image/icon"
-1
View File
@@ -41,7 +41,6 @@ UNIFIED_SOURCES += [
'nsDataObjCollection.cpp',
'nsDragService.cpp',
'nsIdleServiceWin.cpp',
'nsImageClipboard.cpp',
'nsLookAndFeel.cpp',
'nsNativeDragSource.cpp',
'nsNativeDragTarget.cpp',
+38 -9
View File
@@ -26,7 +26,6 @@
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "nsPrimitiveHelpers.h"
#include "nsImageClipboard.h"
#include "nsIWidget.h"
#include "nsIComponentManager.h"
#include "nsWidgetsCID.h"
@@ -36,6 +35,8 @@
#include "nsIOutputStream.h"
#include "nsEscape.h"
#include "nsIObserverService.h"
#include "nsMimeTypes.h"
#include "imgITools.h"
using mozilla::LogLevel;
@@ -474,17 +475,45 @@ nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT
if (aMIMEImageFormat)
{
uint32_t allocLen = 0;
unsigned char * clipboardData;
const char * clipboardData;
if (NS_SUCCEEDED(GetGlobalData(stm.hGlobal, (void **)&clipboardData, &allocLen)))
{
nsImageFromClipboard converter;
nsIInputStream * inputStream;
converter.GetEncodedImageStream(clipboardData, aMIMEImageFormat, &inputStream); // addrefs for us, don't release
if ( inputStream ) {
*aData = inputStream;
*aLen = sizeof(nsIInputStream*);
result = NS_OK;
nsCOMPtr<imgIContainer> container;
nsCOMPtr<imgITools> imgTools = do_CreateInstance("@mozilla.org/image/tools;1");
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = NS_NewByteInputStream(getter_AddRefs(inputStream),
clipboardData,
allocLen,
NS_ASSIGNMENT_DEPEND);
NS_ENSURE_SUCCESS(rv, rv);
result = imgTools->DecodeImage(inputStream,
NS_LITERAL_CSTRING(IMAGE_BMP_MS_CLIPBOARD),
getter_AddRefs(container));
if (NS_FAILED(result)) {
break;
}
nsAutoCString mimeType;
if (strcmp(aMIMEImageFormat, kJPGImageMime) == 0) {
mimeType.Assign(IMAGE_JPEG);
} else {
mimeType.Assign(aMIMEImageFormat);
}
result = imgTools->EncodeImage(container, mimeType, EmptyString(),
getter_AddRefs(inputStream));
if (NS_FAILED(result)) {
break;
}
if (!inputStream) {
result = NS_ERROR_FAILURE;
break;
}
*aData = inputStream.forget().take();
*aLen = sizeof(nsIInputStream*);
}
} break;
+81 -47
View File
@@ -17,7 +17,6 @@
#include "IEnumFE.h"
#include "nsPrimitiveHelpers.h"
#include "nsXPIDLString.h"
#include "nsImageClipboard.h"
#include "nsCRT.h"
#include "nsPrintfCString.h"
#include "nsIStringBundle.h"
@@ -35,6 +34,8 @@
#include "nsIContentPolicy.h"
#include "nsContentUtils.h"
#include "nsIPrincipal.h"
#include "nsMimeTypes.h"
#include "imgITools.h"
#include "WinUtils.h"
#include "mozilla/LazyIdleThread.h"
@@ -45,6 +46,7 @@
using namespace mozilla;
using namespace mozilla::widget;
#define BFH_LENGTH 14
#define DEFAULT_THREAD_TIMEOUT_MS 30000
NS_IMPL_ISUPPORTS(nsDataObj::CStream, nsIStreamListener)
@@ -917,20 +919,60 @@ nsDataObj::GetDib(const nsACString& inFlavor,
}
if ( image ) {
// use the |nsImageToClipboard| helper class to build up a bitmap. We now own
// the bits, and pass them back to the OS in |aSTG|.
nsImageToClipboard converter(image, aFormat.cfFormat == CF_DIBV5);
HANDLE bits = nullptr;
nsresult rv = converter.GetPicture ( &bits );
if ( NS_SUCCEEDED(rv) && bits ) {
aSTG.hGlobal = bits;
aSTG.tymed = TYMED_HGLOBAL;
result = S_OK;
nsCOMPtr<imgITools> imgTools = do_CreateInstance("@mozilla.org/image/tools;1");
nsAutoString options;
if (aFormat.cfFormat == CF_DIBV5) {
options.AppendLiteral("version=5");
} else {
options.AppendLiteral("version=3");
}
} // if we have an image
else
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = imgTools->EncodeImage(image, NS_LITERAL_CSTRING(IMAGE_BMP),
options, getter_AddRefs(inputStream));
if (NS_FAILED(rv) || !inputStream) {
return E_FAIL;
}
nsCOMPtr<imgIEncoder> encoder = do_QueryInterface(inputStream);
if (!encoder) {
return E_FAIL;
}
uint32_t size = 0;
rv = encoder->GetImageBufferUsed(&size);
if (NS_FAILED(rv) || size <= BFH_LENGTH) {
return E_FAIL;
}
char *src = nullptr;
rv = encoder->GetImageBuffer(&src);
if (NS_FAILED(rv) || !src) {
return E_FAIL;
}
// We don't want the file header.
src += BFH_LENGTH;
size -= BFH_LENGTH;
HGLOBAL glob = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);
if (!glob) {
DWORD err = ::GetLastError();
return E_FAIL;
}
char *dst = (char*) ::GlobalLock(glob);
::CopyMemory(dst, src, size);
::GlobalUnlock(glob);
aSTG.hGlobal = glob;
aSTG.tymed = TYMED_HGLOBAL;
result = S_OK;
} else {
NS_WARNING ( "Definitely not an image on clipboard" );
return result;
}
return result;
}
@@ -1536,18 +1578,29 @@ HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
if (!image)
return E_FAIL;
// Use the clipboard helper class to build up a memory bitmap.
nsImageToClipboard converter(image);
HANDLE bits = nullptr;
rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
if (NS_FAILED(rv) || !bits)
nsCOMPtr<imgITools> imgTools = do_CreateInstance("@mozilla.org/image/tools;1");
nsCOMPtr<nsIInputStream> inputStream;
rv = imgTools->EncodeImage(image, NS_LITERAL_CSTRING(IMAGE_BMP),
NS_LITERAL_STRING("version=3"),
getter_AddRefs(inputStream));
if (NS_FAILED(rv) || !inputStream) {
return E_FAIL;
}
// We now own these bits!
uint32_t bitmapSize = GlobalSize(bits);
if (!bitmapSize) {
GlobalFree(bits);
nsCOMPtr<imgIEncoder> encoder = do_QueryInterface(inputStream);
if (!encoder) {
return E_FAIL;
}
uint32_t size = 0;
rv = encoder->GetImageBufferUsed(&size);
if (NS_FAILED(rv)) {
return E_FAIL;
}
char *src = nullptr;
rv = encoder->GetImageBuffer(&src);
if (NS_FAILED(rv) || !src) {
return E_FAIL;
}
@@ -1555,7 +1608,6 @@ HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
nsCOMPtr<nsIFile> dropFile;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
if (!dropFile) {
GlobalFree(bits);
return E_FAIL;
}
@@ -1569,7 +1621,6 @@ HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
dropFile->AppendNative(filename);
rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
@@ -1582,33 +1633,16 @@ HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
nsCOMPtr<nsIOutputStream> outStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
char * bm = (char *)GlobalLock(bits);
BITMAPFILEHEADER fileHdr;
BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
fileHdr.bfType = ((WORD) ('M' << 8) | 'B');
fileHdr.bfSize = GlobalSize (bits) + sizeof(fileHdr);
fileHdr.bfReserved1 = 0;
fileHdr.bfReserved2 = 0;
fileHdr.bfOffBits = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
uint32_t writeCount = 0;
if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
rv = NS_ERROR_FAILURE;
uint32_t written = 0;
rv = outStream->Write(src, size, &written);
if (NS_FAILED(rv) || written != size) {
return E_FAIL;
}
outStream->Close();
GlobalUnlock(bits);
GlobalFree(bits);
if (NS_FAILED(rv))
return E_FAIL;
}
// Pass the file name back to the drop target so that it can access the file.
-497
View File
@@ -1,497 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsImageClipboard.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/RefPtr.h"
#include "nsITransferable.h"
#include "nsGfxCIID.h"
#include "nsMemory.h"
#include "prmem.h"
#include "imgIEncoder.h"
#include "nsLiteralString.h"
#include "nsComponentManagerUtils.h"
#define BFH_LENGTH 14
using namespace mozilla;
using namespace mozilla::gfx;
/* Things To Do 11/8/00
Check image metrics, can we support them? Do we need to?
Any other render format? HTML?
*/
//
// nsImageToClipboard ctor
//
// Given an imgIContainer, convert it to a DIB that is ready to go on the win32 clipboard
//
nsImageToClipboard::nsImageToClipboard(imgIContainer* aInImage, bool aWantDIBV5)
: mImage(aInImage)
, mWantDIBV5(aWantDIBV5)
{
// nothing to do here
}
//
// nsImageToClipboard dtor
//
// Clean up after ourselves. We know that we have created the bitmap
// successfully if we still have a pointer to the header.
//
nsImageToClipboard::~nsImageToClipboard()
{
}
//
// GetPicture
//
// Call to get the actual bits that go on the clipboard. If an error
// ocurred during conversion, |outBits| will be null.
//
// NOTE: The caller owns the handle and must delete it with ::GlobalRelease()
//
nsresult
nsImageToClipboard :: GetPicture ( HANDLE* outBits )
{
NS_ASSERTION ( outBits, "Bad parameter" );
return CreateFromImage ( mImage, outBits );
} // GetPicture
//
// CalcSize
//
// Computes # of bytes needed by a bitmap with the specified attributes.
//
int32_t
nsImageToClipboard :: CalcSize ( int32_t aHeight, int32_t aColors, WORD aBitsPerPixel, int32_t aSpanBytes )
{
int32_t HeaderMem = sizeof(BITMAPINFOHEADER);
// add size of pallette to header size
if (aBitsPerPixel < 16)
HeaderMem += aColors * sizeof(RGBQUAD);
if (aHeight < 0)
aHeight = -aHeight;
return (HeaderMem + (aHeight * aSpanBytes));
}
//
// CalcSpanLength
//
// Computes the span bytes for determining the overall size of the image
//
int32_t
nsImageToClipboard::CalcSpanLength(uint32_t aWidth, uint32_t aBitCount)
{
int32_t spanBytes = (aWidth * aBitCount) >> 5;
if ((aWidth * aBitCount) & 0x1F)
spanBytes++;
spanBytes <<= 2;
return spanBytes;
}
//
// CreateFromImage
//
// Do the work to setup the bitmap header and copy the bits out of the
// image.
//
nsresult
nsImageToClipboard::CreateFromImage ( imgIContainer* inImage, HANDLE* outBitmap )
{
nsresult rv;
*outBitmap = nullptr;
RefPtr<SourceSurface> surface =
inImage->GetFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE);
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
surface->GetFormat() == SurfaceFormat::B8G8R8X8);
RefPtr<DataSourceSurface> dataSurface;
if (surface->GetFormat() == SurfaceFormat::B8G8R8A8) {
dataSurface = surface->GetDataSurface();
} else {
// XXXjwatt Bug 995923 - get rid of this copy and handle B8G8R8X8
// directly below once bug 995807 is fixed.
dataSurface = gfxUtils::
CopySurfaceToDataSourceSurfaceWithFormat(surface,
SurfaceFormat::B8G8R8A8);
}
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/bmp", &rv);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t format;
nsAutoString options;
if (mWantDIBV5) {
options.AppendLiteral("version=5;bpp=");
} else {
options.AppendLiteral("version=3;bpp=");
}
switch (dataSurface->GetFormat()) {
case SurfaceFormat::B8G8R8A8:
format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
options.AppendInt(32);
break;
#if 0
// XXXjwatt Bug 995923 - fix |format| and reenable once bug 995807 is fixed.
case SurfaceFormat::B8G8R8X8:
format = imgIEncoder::INPUT_FORMAT_RGB;
options.AppendInt(24);
break;
#endif
default:
NS_NOTREACHED("Unexpected surface format");
return NS_ERROR_INVALID_ARG;
}
DataSourceSurface::MappedSurface map;
bool mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
NS_ENSURE_TRUE(mappedOK, NS_ERROR_FAILURE);
rv = encoder->InitFromData(map.mData, 0,
dataSurface->GetSize().width,
dataSurface->GetSize().height,
map.mStride,
format, options);
dataSurface->Unmap();
NS_ENSURE_SUCCESS(rv, rv);
uint32_t size;
encoder->GetImageBufferUsed(&size);
NS_ENSURE_TRUE(size > BFH_LENGTH, NS_ERROR_FAILURE);
HGLOBAL glob = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT,
size - BFH_LENGTH);
if (!glob)
return NS_ERROR_OUT_OF_MEMORY;
char *dst = (char*) ::GlobalLock(glob);
char *src;
rv = encoder->GetImageBuffer(&src);
NS_ENSURE_SUCCESS(rv, rv);
::CopyMemory(dst, src + BFH_LENGTH, size - BFH_LENGTH);
::GlobalUnlock(glob);
*outBitmap = (HANDLE)glob;
return NS_OK;
}
nsImageFromClipboard :: nsImageFromClipboard ()
{
// nothing to do here
}
nsImageFromClipboard :: ~nsImageFromClipboard ( )
{
}
//
// GetEncodedImageStream
//
// Take the raw clipboard image data and convert it to aMIMEFormat in the form of a nsIInputStream
//
nsresult
nsImageFromClipboard ::GetEncodedImageStream (unsigned char * aClipboardData, const char * aMIMEFormat, nsIInputStream** aInputStream )
{
NS_ENSURE_ARG_POINTER (aInputStream);
NS_ENSURE_ARG_POINTER (aMIMEFormat);
nsresult rv;
*aInputStream = nullptr;
// pull the size information out of the BITMAPINFO header and
// initialize the image
BITMAPINFO* header = (BITMAPINFO *) aClipboardData;
int32_t width = header->bmiHeader.biWidth;
int32_t height = header->bmiHeader.biHeight;
// neg. heights mean the Y axis is inverted and we don't handle that case
NS_ENSURE_TRUE(height > 0, NS_ERROR_FAILURE);
unsigned char * rgbData = new unsigned char[width * height * 3 /* RGB */];
if (rgbData) {
BYTE * pGlobal = (BYTE *) aClipboardData;
// Convert the clipboard image into RGB packed pixel data
rv = ConvertColorBitMap((unsigned char *) (pGlobal + header->bmiHeader.biSize), header, rgbData);
// if that succeeded, encode the bitmap as aMIMEFormat data. Don't return early or we risk leaking rgbData
if (NS_SUCCEEDED(rv)) {
nsAutoCString encoderCID(NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type="));
// Map image/jpg to image/jpeg (which is how the encoder is registered).
if (strcmp(aMIMEFormat, kJPGImageMime) == 0)
encoderCID.AppendLiteral("image/jpeg");
else
encoderCID.Append(aMIMEFormat);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get(), &rv);
if (NS_SUCCEEDED(rv)){
rv = encoder->InitFromData(rgbData, 0, width, height, 3 * width /* RGB * # pixels in a row */,
imgIEncoder::INPUT_FORMAT_RGB, EmptyString());
if (NS_SUCCEEDED(rv)) {
encoder.forget(aInputStream);
}
}
}
delete [] rgbData;
}
else
rv = NS_ERROR_OUT_OF_MEMORY;
return rv;
} // GetImage
//
// InvertRows
//
// Take the image data from the clipboard and invert the rows. Modifying aInitialBuffer in place.
//
void
nsImageFromClipboard::InvertRows(unsigned char * aInitialBuffer, uint32_t aSizeOfBuffer, uint32_t aNumBytesPerRow)
{
if (!aNumBytesPerRow)
return;
uint32_t numRows = aSizeOfBuffer / aNumBytesPerRow;
unsigned char * row = new unsigned char[aNumBytesPerRow];
uint32_t currentRow = 0;
uint32_t lastRow = (numRows - 1) * aNumBytesPerRow;
while (currentRow < lastRow)
{
// store the current row into a temporary buffer
memcpy(row, &aInitialBuffer[currentRow], aNumBytesPerRow);
memcpy(&aInitialBuffer[currentRow], &aInitialBuffer[lastRow], aNumBytesPerRow);
memcpy(&aInitialBuffer[lastRow], row, aNumBytesPerRow);
lastRow -= aNumBytesPerRow;
currentRow += aNumBytesPerRow;
}
delete[] row;
}
//
// ConvertColorBitMap
//
// Takes the clipboard bitmap and converts it into a RGB packed pixel values.
//
nsresult
nsImageFromClipboard::ConvertColorBitMap(unsigned char * aInputBuffer, PBITMAPINFO pBitMapInfo, unsigned char * aOutBuffer)
{
uint8_t bitCount = pBitMapInfo->bmiHeader.biBitCount;
uint32_t imageSize = pBitMapInfo->bmiHeader.biSizeImage; // may be zero for BI_RGB bitmaps which means we need to calculate by hand
uint32_t 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.
uint32_t 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)
{
int32_t bytesToSkip = (pBitMapInfo->bmiHeader.biClrUsed ? pBitMapInfo->bmiHeader.biClrUsed : (1 << bitCount) ) * sizeof(RGBQUAD);
aInputBuffer += 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 = (*((uint32_t*)&(pBitMapInfo->bmiColors[0])));
colorMasks.green = (*((uint32_t*)&(pBitMapInfo->bmiColors[1])));
colorMasks.blue = (*((uint32_t*)&(pBitMapInfo->bmiColors[2])));
CalcBitShift(&colorMasks);
aInputBuffer += 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;
}
// The windows clipboard image format inverts the rows
InvertRows(aInputBuffer, imageSize, rowSize);
if (!pBitMapInfo->bmiHeader.biCompression || pBitMapInfo->bmiHeader.biCompression == BI_BITFIELDS)
{
uint32_t index = 0;
uint32_t writeIndex = 0;
unsigned char redValue, greenValue, blueValue;
uint8_t colorTableEntry = 0;
int8_t bit; // used for grayscale bitmaps where each bit is a pixel
uint32_t numPixelsLeftInRow = pBitMapInfo->bmiHeader.biWidth; // how many more pixels do we still need to read for the current row
uint32_t pos = 0;
while (index < imageSize)
{
switch (bitCount)
{
case 1:
for (bit = 7; bit >= 0 && numPixelsLeftInRow; bit--)
{
colorTableEntry = (aInputBuffer[index] >> bit) & 1;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbRed;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbGreen;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbBlue;
numPixelsLeftInRow--;
}
pos += 1;
break;
case 4:
{
// each aInputBuffer[index] entry contains data for two pixels.
// read the first pixel
colorTableEntry = aInputBuffer[index] >> 4;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbRed;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbGreen;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbBlue;
numPixelsLeftInRow--;
if (numPixelsLeftInRow) // now read the second pixel
{
colorTableEntry = aInputBuffer[index] & 0xF;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbRed;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbGreen;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[colorTableEntry].rgbBlue;
numPixelsLeftInRow--;
}
pos += 1;
}
break;
case 8:
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[aInputBuffer[index]].rgbRed;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[aInputBuffer[index]].rgbGreen;
aOutBuffer[writeIndex++] = pBitMapInfo->bmiColors[aInputBuffer[index]].rgbBlue;
numPixelsLeftInRow--;
pos += 1;
break;
case 16:
{
uint16_t num = 0;
num = (uint8_t) aInputBuffer[index+1];
num <<= 8;
num |= (uint8_t) aInputBuffer[index];
redValue = ((uint32_t) (((float)(num & 0xf800) / 0xf800) * 0xFF0000) & 0xFF0000)>> 16;
greenValue = ((uint32_t)(((float)(num & 0x07E0) / 0x07E0) * 0x00FF00) & 0x00FF00)>> 8;
blueValue = ((uint32_t)(((float)(num & 0x001F) / 0x001F) * 0x0000FF) & 0x0000FF);
// now we have the right RGB values...
aOutBuffer[writeIndex++] = redValue;
aOutBuffer[writeIndex++] = greenValue;
aOutBuffer[writeIndex++] = blueValue;
numPixelsLeftInRow--;
pos += 2;
}
break;
case 32:
case 24:
if (pBitMapInfo->bmiHeader.biCompression == BI_BITFIELDS)
{
uint32_t val = *((uint32_t*) (aInputBuffer + index) );
aOutBuffer[writeIndex++] = (val & colorMasks.red) >> colorMasks.redRightShift << colorMasks.redLeftShift;
aOutBuffer[writeIndex++] = (val & colorMasks.green) >> colorMasks.greenRightShift << colorMasks.greenLeftShift;
aOutBuffer[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
{
aOutBuffer[writeIndex++] = aInputBuffer[index+2];
aOutBuffer[writeIndex++] = aInputBuffer[index+1];
aOutBuffer[writeIndex++] = aInputBuffer[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
}
return NS_OK;
}
void nsImageFromClipboard::CalcBitmask(uint32_t aMask, uint8_t& aBegin, uint8_t& aLength)
{
// find the rightmost 1
uint8_t pos;
bool started = false;
aBegin = aLength = 0;
for (pos = 0; pos <= 31; pos++)
{
if (!started && (aMask & (1 << pos)))
{
aBegin = pos;
started = true;
}
else if (started && !(aMask & (1 << pos)))
{
aLength = pos - aBegin;
break;
}
}
}
void nsImageFromClipboard::CalcBitShift(bitFields * aColorMask)
{
uint8_t 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;
}
-93
View File
@@ -1,93 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImageClipboard_h
#define nsImageClipboard_h
/* Things To Do 11/8/00
Check image metrics, can we support them? Do we need to?
Any other render format? HTML?
*/
#include "nsError.h"
#include <windows.h>
#include "nsCOMPtr.h"
#include "imgIContainer.h"
#include "nsIInputStream.h"
//
// nsImageToClipboard
//
// A utility class that takes an imgIContainer and does all the bitmap magic
// to allow us to put it on the clipboard
//
class nsImageToClipboard
{
public:
nsImageToClipboard(imgIContainer* aInImage, bool aWantDIBV5 = true);
~nsImageToClipboard();
// Call to get the actual bits that go on the clipboard. If |nullptr|, the
// setup operations have failed.
//
// NOTE: The caller owns the handle and must delete it with ::GlobalRelease()
nsresult GetPicture ( HANDLE* outBits ) ;
private:
// Computes # of bytes needed by a bitmap with the specified attributes.
int32_t CalcSize(int32_t aHeight, int32_t aColors, WORD aBitsPerPixel, int32_t aSpanBytes);
int32_t CalcSpanLength(uint32_t aWidth, uint32_t aBitCount);
// Do the work
nsresult CreateFromImage ( imgIContainer* inImage, HANDLE* outBitmap );
nsCOMPtr<imgIContainer> mImage; // the image we're working with
bool mWantDIBV5;
}; // class nsImageToClipboard
struct bitFields {
uint32_t red;
uint32_t green;
uint32_t blue;
uint8_t redLeftShift;
uint8_t redRightShift;
uint8_t greenLeftShift;
uint8_t greenRightShift;
uint8_t blueLeftShift;
uint8_t blueRightShift;
};
//
// nsImageFromClipboard
//
// A utility class that takes a DIB from the win32 clipboard and does
// all the bitmap magic to convert it to a PNG or a JPEG in the form of a nsIInputStream
//
class nsImageFromClipboard
{
public:
nsImageFromClipboard () ;
~nsImageFromClipboard ( ) ;
// Retrieve the newly created image
nsresult GetEncodedImageStream (unsigned char * aClipboardData, const char * aMIMEFormat, nsIInputStream** outImage);
private:
void InvertRows(unsigned char * aInitialBuffer, uint32_t aSizeOfBuffer, uint32_t aNumBytesPerRow);
nsresult ConvertColorBitMap(unsigned char * aInputBuffer, PBITMAPINFO pBitMapInfo, unsigned char * aOutBuffer);
void CalcBitmask(uint32_t aMask, uint8_t& aBegin, uint8_t& aLength);
void CalcBitShift(bitFields * aColorMask);
}; // nsImageFromClipboard
#endif
+2
View File
@@ -8,6 +8,7 @@
#include "mozilla/EventStates.h"
#include "mozilla/Logging.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/gfx/Types.h" // for Color::FromABGR
#include "nsDeviceContext.h"
#include "nsRenderingContext.h"
#include "nsRect.h"
@@ -41,6 +42,7 @@
using mozilla::IsVistaOrLater;
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::widget;
extern mozilla::LazyLogModule gWindowsLog;