| /* |
| * 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); |
| 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 immediatelly 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 droping 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 immediatelly 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; |
| } |
| |
| } |