/* * 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); }
void *safe_Calloc_outofmem(size_t num, size_t size, const char *file, int line) throw (std::bad_alloc) { rand_alloc_fail(file, line); return safe_Calloc(num, size); }
/* * 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); }