| /* | |
| * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include "awt.h" | |
| #include "awt_DataTransferer.h" | |
| #include "awt_DnDDT.h" | |
| #include "awt_TextComponent.h" | |
| #include <shlobj.h> | |
| #include <shellapi.h> | |
| #include <sun_awt_windows_WDataTransferer.h> | |
| #include "locale_str.h" | |
| #define GALLOCFLG (GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT) | |
| #define WIN_TO_JAVA_PIXEL(r, g, b) (0xFF000000 | (r) << 16 | (g) << 8 | (b) << 0) | |
| DECLARE_JAVA_CLASS(dataTransfererClazz, "sun/awt/datatransfer/DataTransferer"); | |
| jobject | |
| AwtDataTransferer::GetDataTransferer(JNIEnv* env) { | |
| DECLARE_STATIC_OBJECT_JAVA_METHOD(getInstanceMethodID, dataTransfererClazz, | |
| "getInstance", | |
| "()Lsun/awt/datatransfer/DataTransferer;"); | |
| return env->CallStaticObjectMethod(clazz, getInstanceMethodID); | |
| } | |
| jbyteArray | |
| AwtDataTransferer::ConvertData(JNIEnv* env, jobject source, jobject contents, | |
| jlong format, jobject formatMap) { | |
| jobject transferer = GetDataTransferer(env); | |
| if (!JNU_IsNull(env, transferer)) { | |
| jbyteArray ret = NULL; | |
| DECLARE_OBJECT_JAVA_METHOD(convertDataMethodID, dataTransfererClazz, | |
| "convertData", | |
| "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B"); | |
| ret = (jbyteArray)env->CallObjectMethod(transferer, convertDataMethodID, | |
| source, contents, format, | |
| formatMap, AwtToolkit::IsMainThread()); | |
| if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) { | |
| env->ExceptionDescribe(); | |
| env->ExceptionClear(); | |
| } | |
| env->DeleteLocalRef(transferer); | |
| return ret; | |
| } else { | |
| return NULL; | |
| } | |
| } | |
| jobject | |
| AwtDataTransferer::ConcatData(JNIEnv* env, jobject obj1, jobject obj2) { | |
| jobject transferer = GetDataTransferer(env); | |
| if (!JNU_IsNull(env, transferer)) { | |
| jobject ret = NULL; | |
| DECLARE_OBJECT_JAVA_METHOD(concatDataMethodID, dataTransfererClazz, | |
| "concatData", | |
| "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); | |
| ret = env->CallObjectMethod(transferer, concatDataMethodID, obj1, obj2); | |
| if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) { | |
| env->ExceptionDescribe(); | |
| env->ExceptionClear(); | |
| } | |
| env->DeleteLocalRef(transferer); | |
| return ret; | |
| } else { | |
| return NULL; | |
| } | |
| } | |
| /** | |
| * This routine retrieves palette entries from enhanced metafile or | |
| * a logical color palette, builds appropriate LOGPALETTE structure, | |
| * writes it into a created Java byte array and returns a local | |
| * reference to the array. | |
| * This routine is used for image data transfer. | |
| * | |
| * @param hGdiObj - a handle to the GDI object to retrieve palette entries from, | |
| * it can be a handle to either a logical color palette (OBJ_PAL type) | |
| * or an enhanced metafile (OBJ_ENHMETAFILE). If it is neither of these | |
| * types the routine fails(see bFailSafe). | |
| * @param dwGdiObjType - a type of the passed GDI object. It should be specified | |
| * if the type of the passed GDI object is known to the caller. Otherwise | |
| * pass 0. | |
| * @param bFailSafe - if FALSE, the routine will return NULL in case of failure, | |
| * otherwise it will return an array with empty LOGPALETTE structure | |
| * in case of failure. | |
| * @return a local reference to Java byte array which contains LOGPALETTE | |
| * structure which defines a logical color palette or a palette of | |
| * an enhanced metafile. | |
| */ | |
| jbyteArray | |
| AwtDataTransferer::GetPaletteBytes(HGDIOBJ hGdiObj, DWORD dwGdiObjType, | |
| BOOL bFailSafe) { | |
| if (hGdiObj == NULL) { | |
| dwGdiObjType = 0; | |
| } else if (dwGdiObjType == 0) { | |
| dwGdiObjType = ::GetObjectType(hGdiObj); | |
| } else { | |
| DASSERT(::GetObjectType(hGdiObj) == dwGdiObjType); | |
| } | |
| if (!bFailSafe && dwGdiObjType == 0) { | |
| return NULL; | |
| } | |
| UINT nEntries = 0; | |
| switch (dwGdiObjType) { | |
| case OBJ_PAL: | |
| nEntries = | |
| ::GetPaletteEntries((HPALETTE)hGdiObj, 0, 0, NULL); | |
| break; | |
| case OBJ_ENHMETAFILE: | |
| nEntries = | |
| ::GetEnhMetaFilePaletteEntries((HENHMETAFILE)hGdiObj, 0, NULL); | |
| break; | |
| } | |
| if (!bFailSafe && (nEntries == 0 || nEntries == GDI_ERROR)) { | |
| return NULL; | |
| } | |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); | |
| jsize size = sizeof(LOGPALETTE) + nEntries * sizeof(PALETTEENTRY); | |
| jbyteArray paletteBytes = env->NewByteArray(size); | |
| if (JNU_IsNull(env, paletteBytes)) { | |
| throw std::bad_alloc(); | |
| } | |
| LOGPALETTE* pLogPalette = | |
| (LOGPALETTE*)env->GetPrimitiveArrayCritical(paletteBytes, NULL); | |
| if (pLogPalette == NULL) { | |
| env->DeleteLocalRef(paletteBytes); | |
| return NULL; | |
| } | |
| PALETTEENTRY* pPalEntries = (PALETTEENTRY*)pLogPalette->palPalEntry; | |
| pLogPalette->palVersion = 0x300; | |
| pLogPalette->palNumEntries = nEntries; | |
| switch (dwGdiObjType) { | |
| case OBJ_PAL: | |
| VERIFY(::GetPaletteEntries((HPALETTE)hGdiObj, 0, nEntries, | |
| pPalEntries) == nEntries); | |
| break; | |
| case OBJ_ENHMETAFILE: | |
| VERIFY(::GetEnhMetaFilePaletteEntries((HENHMETAFILE)hGdiObj, nEntries, | |
| pPalEntries) == nEntries); | |
| break; | |
| } | |
| env->ReleasePrimitiveArrayCritical(paletteBytes, pLogPalette, 0); | |
| return paletteBytes; | |
| } | |
| jbyteArray | |
| AwtDataTransferer::LCIDToTextEncoding(JNIEnv *env, LCID lcid) { | |
| LANGID langID = LANGIDFROMLCID(lcid); | |
| const char *encoding = getEncodingFromLangID(langID); | |
| // Warning C4244. | |
| // Cast SIZE_T (__int64 on 64-bit/unsigned int on 32-bit) | |
| // to jsize (long). | |
| // We assume that the encoding name length cannot exceed INT_MAX. | |
| jsize length = (jsize)strlen(encoding); | |
| jbyteArray retval = env->NewByteArray(length); | |
| if (retval == NULL) { | |
| throw std::bad_alloc(); | |
| } | |
| env->SetByteArrayRegion(retval, 0, length, (jbyte *)encoding); | |
| free((void *)encoding); | |
| return retval; | |
| } | |
| static VOID CALLBACK | |
| IdleFunc() { | |
| /* | |
| * Fix for 4485987 and 4669873. | |
| * If IdleFunc is a noop, the secondary message pump occasionally occupies | |
| * all processor time and causes drag freezes. GetQueueStatus is needed to | |
| * mark all messages that are currently in the queue as old, otherwise | |
| * WaitMessage will return immediately as we selectively get messages from | |
| * the queue. | |
| */ | |
| ::WaitMessage(); | |
| ::GetQueueStatus(QS_ALLINPUT); | |
| } | |
| static BOOL CALLBACK | |
| PeekMessageFunc(MSG& msg) { | |
| return ::PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE) || | |
| ::PeekMessage(&msg, NULL, WM_AWT_INVOKE_METHOD, WM_AWT_INVOKE_METHOD, PM_REMOVE) || | |
| ::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE); | |
| } | |
| void | |
| AwtDataTransferer::SecondaryMessageLoop() { | |
| DASSERT(AwtToolkit::MainThread() == ::GetCurrentThreadId()); | |
| AwtToolkit::GetInstance().MessageLoop(IdleFunc, | |
| PeekMessageFunc); | |
| } | |
| extern "C" { | |
| /* | |
| * Class: sun_awt_datatransfer_DataTransferer | |
| * Method: draqQueryFile | |
| * Signature: ([B)[Ljava/lang/String; | |
| */ | |
| JNIEXPORT jobjectArray JNICALL | |
| Java_sun_awt_windows_WDataTransferer_dragQueryFile | |
| (JNIEnv *env, jobject obj, jbyteArray bytes) | |
| { | |
| TRY; | |
| /* | |
| * Fix for the BugTraq ID 4327064 - inter-jvm DnD crashes the dropping jvm. | |
| * On Win9X DragQueryFile() doesn't accept a pointer to the local help as the first | |
| * argument, so we should dump the bits into global memory. | |
| */ | |
| UINT size = env->GetArrayLength(bytes); | |
| HGLOBAL hglobal = NULL; | |
| jbyte *bBytes = NULL; | |
| HDROP hdrop = NULL; | |
| LPTSTR buffer = NULL; | |
| hglobal = ::GlobalAlloc(GALLOCFLG, size); | |
| if (hglobal == NULL) { | |
| throw std::bad_alloc(); | |
| } | |
| try { | |
| bBytes = (jbyte*)::GlobalLock(hglobal); | |
| env->GetByteArrayRegion(bytes, 0, size, bBytes); | |
| hdrop = (HDROP)bBytes; | |
| UINT nFilenames = ::DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0); | |
| jclass str_clazz = env->FindClass("java/lang/String"); | |
| DASSERT(str_clazz != NULL); | |
| if (str_clazz == NULL) { | |
| throw std::bad_alloc(); | |
| } | |
| jobjectArray filenames = env->NewObjectArray(nFilenames, str_clazz, | |
| NULL); | |
| if (filenames == NULL) { | |
| throw std::bad_alloc(); | |
| } | |
| UINT bufsize = 512; // in characters, not in bytes | |
| buffer = (LPTSTR)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, bufsize, sizeof(TCHAR)); | |
| for (UINT i = 0; i < nFilenames; i++) { | |
| UINT size = ::DragQueryFile(hdrop, i, NULL, 0); | |
| if (size > bufsize) { | |
| bufsize = size; | |
| buffer = (LPTSTR)SAFE_SIZE_ARRAY_REALLOC(safe_Realloc, buffer, bufsize, sizeof(TCHAR)); | |
| } | |
| ::DragQueryFile(hdrop, i, buffer, bufsize); | |
| jstring name = JNU_NewStringPlatform(env, buffer); | |
| if (name == NULL) { | |
| throw std::bad_alloc(); | |
| } | |
| env->SetObjectArrayElement(filenames, i, name); | |
| } | |
| free(buffer); | |
| ::GlobalUnlock(hglobal); | |
| ::GlobalFree(hglobal); | |
| return filenames; | |
| } catch (std::bad_alloc&) { | |
| free(buffer); | |
| ::GlobalUnlock(hglobal); | |
| ::GlobalFree(hglobal); | |
| throw; | |
| } | |
| CATCH_BAD_ALLOC_RET(NULL); | |
| } | |
| /* | |
| * Class: sun_awt_windows_WDataTransferer | |
| * Method: platformImageBytesToImageData | |
| * Signature: ([BI)[I | |
| */ | |
| JNIEXPORT jintArray JNICALL | |
| Java_sun_awt_windows_WDataTransferer_platformImageBytesToImageData( | |
| JNIEnv *env, jobject self, jbyteArray bytes, jlong format) { | |
| TRY; | |
| HDC hdc = NULL; | |
| LOGPALETTE* pLogPalette = NULL; | |
| WORD uPaletteEntries = 0; | |
| SIZE_T uOffset = 0; | |
| HPALETTE hPalette = NULL; | |
| HPALETTE hOldPalette = NULL; | |
| BITMAPINFO* pSrcBmi = NULL; | |
| BITMAPINFOHEADER* pSrcBmih = NULL; | |
| LPVOID pSrcBits = NULL; | |
| BITMAPINFO* pDstBmi = NULL; | |
| BITMAPINFOHEADER* pDstBmih = NULL; | |
| LPVOID pDstBits = NULL; | |
| LPBYTE lpEnhMetaFileBits = NULL; | |
| HENHMETAFILE hEnhMetaFile = NULL; | |
| HBITMAP hDibSection = NULL; | |
| HBITMAP hOldBitmap = NULL; | |
| jintArray buffer = NULL; | |
| LONG width = 0; | |
| LONG height = 0; | |
| int numPixels = 0; | |
| if (JNU_IsNull(env, bytes)) { | |
| return NULL; | |
| } | |
| jsize size = env->GetArrayLength(bytes); | |
| if (size == 0) { | |
| return NULL; | |
| } | |
| jbyte* bBytes = (jbyte*)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, size, sizeof(jbyte)); | |
| try { | |
| env->GetByteArrayRegion(bytes, 0, size, bBytes); | |
| pLogPalette = (LOGPALETTE*)bBytes; | |
| uPaletteEntries = pLogPalette->palNumEntries; | |
| uOffset = sizeof(LOGPALETTE) + uPaletteEntries * sizeof(PALETTEENTRY); | |
| DASSERT(uOffset < (SIZE_T)size); | |
| if (uPaletteEntries == 0) { | |
| pLogPalette = NULL; | |
| } | |
| hdc = ::CreateCompatibleDC(NULL); | |
| if (hdc == NULL) { | |
| free(bBytes); | |
| return NULL; | |
| } | |
| switch (format) { | |
| case CF_DIB: | |
| pSrcBmi = (BITMAPINFO*)((LPSTR)bBytes + uOffset); | |
| pSrcBmih = &pSrcBmi->bmiHeader; | |
| width = pSrcBmih->biWidth; | |
| height = abs(pSrcBmih->biHeight); | |
| { | |
| DWORD nColorEntries = 0; | |
| switch (pSrcBmih->biBitCount) { | |
| case 0: nColorEntries = 0; break; | |
| case 1: nColorEntries = 2; break; | |
| case 4: | |
| case 8: | |
| nColorEntries = (pSrcBmih->biClrUsed != 0) ? | |
| pSrcBmih->biClrUsed : (1 << pSrcBmih->biBitCount); | |
| break; | |
| case 16: | |
| case 24: | |
| case 32: | |
| nColorEntries = pSrcBmih->biClrUsed; | |
| // If biBitCount is 16 or 32 and biCompression is | |
| // BI_BITFIELDS the color table will be prefixed with | |
| // three DWORD color masks. | |
| if (pSrcBmih->biCompression == BI_BITFIELDS && | |
| (pSrcBmih->biBitCount == 16 || | |
| pSrcBmih->biBitCount == 32)) { | |
| nColorEntries += 3; | |
| } | |
| break; | |
| default: | |
| // The header is probably corrupted. | |
| // Fail immediately to avoid memory access violation. | |
| free(bBytes); | |
| ::DeleteDC(hdc); | |
| return NULL; | |
| } | |
| pSrcBits = (LPSTR)pSrcBmi + pSrcBmih->biSize | |
| + nColorEntries * sizeof(RGBQUAD); | |
| } | |
| break; | |
| case CF_ENHMETAFILE: | |
| case CF_METAFILEPICT: | |
| lpEnhMetaFileBits = (BYTE*)bBytes + uOffset; | |
| // Warning C4244. size is jsize, uOffset is SIZE_T. | |
| // We assert that size > uOffset, so it is safe to cast to jsize. | |
| hEnhMetaFile = ::SetEnhMetaFileBits(size - (jsize)uOffset, | |
| lpEnhMetaFileBits); | |
| DASSERT(hEnhMetaFile != NULL); | |
| { | |
| UINT uHeaderSize = | |
| ::GetEnhMetaFileHeader(hEnhMetaFile, 0, NULL); | |
| DASSERT(uHeaderSize != 0); | |
| ENHMETAHEADER* lpemh = (ENHMETAHEADER*)safe_Malloc(uHeaderSize); | |
| VERIFY(::GetEnhMetaFileHeader(hEnhMetaFile, uHeaderSize, | |
| lpemh) == uHeaderSize); | |
| LPRECTL lpFrame = &lpemh->rclFrame; | |
| POINT p = { abs(lpFrame->right - lpFrame->left), | |
| abs(lpFrame->bottom - lpFrame->top) }; | |
| VERIFY(::SaveDC(hdc)); | |
| VERIFY(::SetMapMode(hdc, MM_HIMETRIC)); | |
| VERIFY(::LPtoDP(hdc, &p, 1)); | |
| VERIFY(::RestoreDC(hdc, -1)); | |
| width = p.x; | |
| height = -p.y; | |
| free(lpemh); | |
| } | |
| break; | |
| default: | |
| DASSERT(FALSE); // Other formats are not supported yet. | |
| free(bBytes); | |
| ::DeleteDC(hdc); | |
| return NULL; | |
| } | |
| // JNI doesn't allow to store more than INT_MAX in a single array. | |
| // We report conversion failure in this case. | |
| if (width * height > INT_MAX) { | |
| free(bBytes); | |
| ::DeleteDC(hdc); | |
| return NULL; | |
| } | |
| numPixels = width * height; | |
| if (pLogPalette != NULL) { | |
| hPalette = ::CreatePalette(pLogPalette); | |
| if (hPalette == NULL) { | |
| free(bBytes); | |
| ::DeleteDC(hdc); | |
| return NULL; | |
| } | |
| hOldPalette = ::SelectPalette(hdc, hPalette, FALSE); | |
| ::RealizePalette(hdc); | |
| } | |
| // allocate memory for BITMAPINFO | |
| pDstBmi = (BITMAPINFO *)safe_Calloc(1, sizeof(BITMAPINFO)); | |
| pDstBmih = &pDstBmi->bmiHeader; | |
| static const int BITS_PER_PIXEL = 32; | |
| // prepare BITMAPINFO for a 32-bit RGB bitmap | |
| pDstBmih->biSize = sizeof(BITMAPINFOHEADER); | |
| pDstBmih->biWidth = width; | |
| pDstBmih->biHeight = -height; // negative height means a top-down DIB | |
| pDstBmih->biPlanes = 1; | |
| pDstBmih->biBitCount = BITS_PER_PIXEL; | |
| pDstBmih->biCompression = BI_RGB; | |
| // NOTE: MSDN says that biSizeImage may be set to 0 for BI_RGB bitmaps, | |
| // but this causes CreateDIBSection to allocate zero-size memory block | |
| // for DIB data. It works okay when biSizeImage is explicitly specified. | |
| pDstBmih->biSizeImage = width * height * (BITS_PER_PIXEL >> 3); | |
| hDibSection = ::CreateDIBSection(hdc, (BITMAPINFO*)pDstBmi, | |
| DIB_RGB_COLORS, &pDstBits, | |
| NULL, 0); | |
| if (hDibSection == NULL) { | |
| free(pDstBmi); pDstBmi = NULL; | |
| if (hPalette != NULL) { | |
| VERIFY(::SelectPalette(hdc, hOldPalette, FALSE) != NULL); | |
| hOldPalette = NULL; | |
| VERIFY(::DeleteObject(hPalette)); hPalette = NULL; | |
| } | |
| VERIFY(::DeleteDC(hdc)); hdc = NULL; | |
| free(bBytes); bBytes = NULL; | |
| JNU_ThrowIOException(env, "failed to get drop data"); | |
| return NULL; | |
| } | |
| hOldBitmap = (HBITMAP)::SelectObject(hdc, hDibSection); | |
| DASSERT(hOldBitmap != NULL); | |
| switch (format) { | |
| case CF_DIB: | |
| VERIFY(::StretchDIBits(hdc, | |
| 0, 0, width, height, | |
| 0, 0, width, height, | |
| pSrcBits, pSrcBmi, | |
| DIB_RGB_COLORS, SRCCOPY) != GDI_ERROR); | |
| break; | |
| case CF_ENHMETAFILE: | |
| case CF_METAFILEPICT: { | |
| RECT rect = { 0, 0, width, height }; | |
| VERIFY(::PlayEnhMetaFile(hdc, hEnhMetaFile, &rect)); | |
| VERIFY(::DeleteEnhMetaFile(hEnhMetaFile)); hEnhMetaFile = NULL; | |
| break; | |
| } | |
| default: | |
| // Other formats are not supported yet. | |
| DASSERT(FALSE); | |
| break; | |
| } | |
| // convert Win32 pixel format (BGRX) to Java format (ARGB) | |
| DASSERT(sizeof(jint) == sizeof(RGBQUAD)); | |
| RGBQUAD* prgbq = (RGBQUAD*)pDstBits; | |
| for(int nPixel = 0; nPixel < numPixels; nPixel++, prgbq++) { | |
| jint jpixel = WIN_TO_JAVA_PIXEL(prgbq->rgbRed, | |
| prgbq->rgbGreen, | |
| prgbq->rgbBlue); | |
| // stuff the 32-bit pixel back into the 32-bit RGBQUAD | |
| *prgbq = *((RGBQUAD*)(&jpixel)); | |
| } | |
| buffer = env->NewIntArray(numPixels + 2); | |
| if (buffer == NULL) { | |
| throw std::bad_alloc(); | |
| } | |
| // copy pixels into Java array | |
| env->SetIntArrayRegion(buffer, 0, numPixels, (jint*)pDstBits); | |
| // copy dimensions into Java array | |
| env->SetIntArrayRegion(buffer, numPixels, 1, (jint*)&width); | |
| env->SetIntArrayRegion(buffer, numPixels + 1, 1, (jint*)&height); | |
| VERIFY(::SelectObject(hdc, hOldBitmap) != NULL); hOldBitmap = NULL; | |
| VERIFY(::DeleteObject(hDibSection)); hDibSection = NULL; | |
| free(pDstBmi); pDstBmi = NULL; | |
| if (hPalette != NULL) { | |
| VERIFY(::SelectPalette(hdc, hOldPalette, FALSE) != NULL); | |
| hOldPalette = NULL; | |
| VERIFY(::DeleteObject(hPalette)); hPalette = NULL; | |
| } | |
| VERIFY(::DeleteDC(hdc)); hdc = NULL; | |
| free(bBytes); bBytes = NULL; | |
| } catch (...) { | |
| if (hdc != NULL && hOldBitmap != NULL) { | |
| VERIFY(::SelectObject(hdc, hOldBitmap) != NULL); hOldBitmap = NULL; | |
| } | |
| if (hDibSection != NULL) { | |
| VERIFY(::DeleteObject(hDibSection)); hDibSection = NULL; | |
| } | |
| if (pDstBmi != NULL) { | |
| free(pDstBmi); pDstBmi = NULL; | |
| } | |
| if (hPalette != NULL) { | |
| if (hdc != NULL) { | |
| VERIFY(::SelectPalette(hdc, hOldPalette, FALSE) != NULL); | |
| hOldPalette = NULL; | |
| } | |
| VERIFY(::DeleteObject(hPalette)); hPalette = NULL; | |
| } | |
| if (hdc != NULL) { | |
| VERIFY(::DeleteDC(hdc)); hdc = NULL; | |
| } | |
| if (hEnhMetaFile != NULL) { | |
| VERIFY(::DeleteEnhMetaFile(hEnhMetaFile)); hEnhMetaFile = NULL; | |
| } | |
| if (bBytes != NULL) { | |
| free(bBytes); bBytes = NULL; | |
| } | |
| throw; | |
| } | |
| return buffer; | |
| CATCH_BAD_ALLOC_RET(NULL); | |
| } | |
| /* | |
| * Class: sun_awt_windows_WDataTransferer | |
| * Method: imageDataToPlatformImageBytes | |
| * Signature: ([BIII)[B | |
| */ | |
| JNIEXPORT jbyteArray JNICALL | |
| Java_sun_awt_windows_WDataTransferer_imageDataToPlatformImageBytes(JNIEnv *env, | |
| jobject self, jbyteArray imageData, | |
| jint width, jint height, | |
| jlong format) { | |
| TRY; | |
| if (JNU_IsNull(env, imageData)) { | |
| return NULL; | |
| } | |
| UINT size = env->GetArrayLength(imageData); | |
| if (size == 0) { | |
| return NULL; | |
| } | |
| // In the passed imageData array all lines are padded with zeroes except for | |
| // the last one, so we have to add one pad size here. | |
| int mod = (width * 3) % 4; | |
| int pad = mod > 0 ? 4 - mod : 0; | |
| int nBytes = sizeof(BITMAPINFO) + size + pad; | |
| BITMAPINFO* pinfo = (BITMAPINFO*)safe_Calloc(1, nBytes); | |
| static const int BITS_PER_PIXEL = 24; | |
| // prepare BITMAPINFO for a 24-bit BGR bitmap | |
| pinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| pinfo->bmiHeader.biWidth = width; | |
| pinfo->bmiHeader.biHeight = height; // positive height means a bottom-up DIB | |
| pinfo->bmiHeader.biPlanes = 1; | |
| pinfo->bmiHeader.biBitCount = BITS_PER_PIXEL; | |
| pinfo->bmiHeader.biCompression = BI_RGB; | |
| // NOTE: MSDN says that biSizeImage may be set to 0 for BI_RGB bitmaps, | |
| // but some programs (e.g. Imaging for Windows NT by Wang Laboratories) | |
| // don't handle such DIBs correctly, so we specify the size explicitly. | |
| pinfo->bmiHeader.biSizeImage = size + pad; | |
| jbyte *array = (jbyte*)((LPSTR)pinfo + sizeof(BITMAPINFOHEADER)); | |
| env->GetByteArrayRegion(imageData, 0, size, array); | |
| HRESULT hr = S_OK; | |
| jbyteArray bytes = NULL; | |
| switch (format) { | |
| case CF_DIB: | |
| bytes = env->NewByteArray(nBytes); | |
| if( NULL == bytes ) { | |
| hr = E_OUTOFMEMORY; | |
| } else { | |
| env->SetByteArrayRegion(bytes, 0, nBytes, (jbyte*)pinfo); | |
| } | |
| break; | |
| case CF_ENHMETAFILE: | |
| { | |
| HDC hdc = ::GetDC(NULL); | |
| if( NULL == hdc) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| POINT p = { width, height }; | |
| //We are trying to support context-independent metafile. | |
| //To implement it we have to select correct MM_HIMETRIC map mode. | |
| VERIFY(::SetMapMode(hdc, MM_HIMETRIC)); | |
| VERIFY(::DPtoLP(hdc, &p, 1)); | |
| //In accordance with CreateEnhMetaFile documentation the rectangle have to | |
| //be normal (left <= right, top <= bottom) | |
| RECT r = { min(0, p.x), min(0, p.y), max(0, p.x), max(0, p.y) }; | |
| //Due to inversed row order in source bitmap the destination | |
| //height have to be negative. | |
| HDC hemfdc = ::CreateEnhMetaFile(NULL, NULL, &r, NULL); | |
| if( NULL == hemfdc) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| int iMFHeight = r.bottom - r.top; | |
| int iMFWidth = r.right - r.left; | |
| VERIFY(::SetMapMode(hemfdc, MM_HIMETRIC)); | |
| if( GDI_ERROR == ::StretchDIBits(hemfdc, | |
| 0, iMFHeight, iMFWidth, -iMFHeight, | |
| 0, 0, width, height, | |
| (LPVOID)array, pinfo, | |
| DIB_RGB_COLORS, SRCCOPY)) | |
| { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } | |
| HENHMETAFILE hemf = ::CloseEnhMetaFile(hemfdc); | |
| if( NULL == hemf) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| if(SUCCEEDED(hr)){ | |
| UINT uEmfSize = ::GetEnhMetaFileBits(hemf, 0, NULL); | |
| if( 0 == uEmfSize) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| LPBYTE lpbEmfBuffer = NULL; | |
| try { | |
| lpbEmfBuffer = (LPBYTE)safe_Malloc(uEmfSize); | |
| VERIFY(::GetEnhMetaFileBits(hemf, uEmfSize, | |
| lpbEmfBuffer) == uEmfSize); | |
| bytes = env->NewByteArray(uEmfSize); | |
| if(NULL == bytes) { | |
| hr = E_OUTOFMEMORY; | |
| } else { | |
| env->SetByteArrayRegion(bytes, 0, uEmfSize, (jbyte*)lpbEmfBuffer); | |
| } | |
| } catch (std::bad_alloc &) { | |
| hr = E_OUTOFMEMORY; | |
| } | |
| free(lpbEmfBuffer); | |
| } | |
| } | |
| VERIFY(::DeleteEnhMetaFile(hemf)); | |
| } | |
| } | |
| VERIFY(::ReleaseDC(NULL, hdc)); | |
| } | |
| break; | |
| } | |
| case CF_METAFILEPICT: | |
| { | |
| HDC hdc = ::GetDC(NULL); | |
| if( NULL == hdc) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| POINT p = { width, height }; | |
| VERIFY(::SetMapMode(hdc, MM_HIMETRIC)); | |
| VERIFY(::DPtoLP(hdc, &p, 1)); | |
| RECT r = { min(0, p.x), min(0, p.y), max(0, p.x), max(0, p.y) }; | |
| HDC hmfdc = ::CreateMetaFile(NULL); | |
| if( NULL == hmfdc) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| VERIFY(::SetMapMode(hmfdc, MM_HIMETRIC)); | |
| int iMFHeight = r.bottom - r.top; | |
| int iMFWidth = r.right - r.left; | |
| //The destination Y coordinate (3d parameter in StretchDIBits call) is different for | |
| //CF_ENHMETAFILE and CF_METAFILEPICT formats due to applying MM_ANISOTROPIC map mode | |
| //at very last moment. MM_ANISOTROPIC map mode changes the Y-axis direction and can be | |
| //selected just for metafile header. | |
| if( GDI_ERROR == ::StretchDIBits(hmfdc, | |
| 0, 0, iMFWidth, -iMFHeight, | |
| 0, 0, width, height, | |
| (LPVOID)array, pinfo, | |
| DIB_RGB_COLORS, SRCCOPY)) | |
| { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } | |
| HMETAFILE hmf = ::CloseMetaFile(hmfdc); | |
| if( NULL == hmf) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| if(SUCCEEDED(hr)){ | |
| UINT uMfSize = ::GetMetaFileBitsEx(hmf, 0, NULL); | |
| if( 0 == uMfSize) { | |
| hr = HRESULT_FROM_WIN32(::GetLastError()); | |
| } else { | |
| LPBYTE lpbMfBuffer = NULL; | |
| try { | |
| lpbMfBuffer = (LPBYTE)SAFE_SIZE_STRUCT_ALLOC(safe_Malloc, | |
| sizeof(METAFILEPICT), uMfSize, 1); | |
| const UINT uMfSizeWithHead = uMfSize + sizeof(METAFILEPICT); | |
| VERIFY(::GetMetaFileBitsEx(hmf, uMfSize, | |
| lpbMfBuffer + sizeof(METAFILEPICT)) == uMfSize); | |
| bytes = env->NewByteArray(uMfSizeWithHead); | |
| if(NULL == bytes) { | |
| hr = E_OUTOFMEMORY; | |
| } else { | |
| LPMETAFILEPICT lpMfp = (LPMETAFILEPICT)lpbMfBuffer; | |
| lpMfp->mm = MM_ANISOTROPIC; // should use MM_ANISOTROPIC exactly (MSDN) | |
| lpMfp->xExt = iMFWidth; | |
| lpMfp->yExt = iMFHeight; | |
| env->SetByteArrayRegion(bytes, 0, uMfSizeWithHead, (jbyte*)lpbMfBuffer); | |
| } | |
| } catch (std::bad_alloc &) { | |
| hr = E_OUTOFMEMORY; | |
| } | |
| free(lpbMfBuffer); | |
| } | |
| } | |
| VERIFY(::DeleteMetaFile(hmf)); | |
| } | |
| } | |
| VERIFY(::ReleaseDC(NULL, hdc)); | |
| } | |
| break; | |
| } | |
| default: | |
| DASSERT(FALSE); // Other formats are not supported yet. | |
| hr = E_NOTIMPL; | |
| break; | |
| } | |
| free(pinfo); | |
| if(FAILED(hr)){ | |
| if(E_OUTOFMEMORY == hr) | |
| throw std::bad_alloc(); | |
| return NULL; | |
| } | |
| return bytes; | |
| CATCH_BAD_ALLOC_RET(NULL); | |
| } | |
| /* | |
| * Class: sun_awt_windows_WDataTransferer | |
| * Method: registerClipboardFormat | |
| * Signature: (Ljava/lang/String;)J | |
| */ | |
| JNIEXPORT jlong JNICALL | |
| Java_sun_awt_windows_WDataTransferer_registerClipboardFormat(JNIEnv *env, | |
| jclass cls, | |
| jstring str) | |
| { | |
| TRY; | |
| LPCTSTR cStr = JNU_GetStringPlatformChars(env, str, NULL); | |
| CHECK_NULL_RETURN(cStr, 0); | |
| jlong value = ::RegisterClipboardFormat(cStr); | |
| JNU_ReleaseStringPlatformChars(env, str, cStr); | |
| return value; | |
| CATCH_BAD_ALLOC_RET(0); | |
| } | |
| /* | |
| * Class: sun_awt_windows_WDataTransferer | |
| * Method: getClipboardFormatName | |
| * Signature: (J)Ljava/lang/String; | |
| */ | |
| JNIEXPORT jstring JNICALL | |
| Java_sun_awt_windows_WDataTransferer_getClipboardFormatName(JNIEnv *env, | |
| jclass cls, | |
| jlong format) | |
| { | |
| TRY; | |
| LPTSTR buf = new TCHAR[512]; // perhaps a bad idea to limit ourselves to 512 | |
| VERIFY(::GetClipboardFormatName((UINT)format, buf, 512)); | |
| jstring name = JNU_NewStringPlatform(env, buf); | |
| delete [] buf; | |
| if (name == NULL) { | |
| throw std::bad_alloc(); | |
| } | |
| return name; | |
| CATCH_BAD_ALLOC_RET(NULL); | |
| } | |
| /* | |
| * Class: sun_awt_windows_WToolkitThreadBlockedHandler | |
| * Method: startSecondaryEventLoop | |
| * Signature: ()V; | |
| */ | |
| JNIEXPORT void JNICALL | |
| Java_sun_awt_windows_WToolkitThreadBlockedHandler_startSecondaryEventLoop(JNIEnv *env, jclass) | |
| { | |
| TRY; | |
| AwtDataTransferer::SecondaryMessageLoop(); | |
| CATCH_BAD_ALLOC; | |
| } | |
| } |