/*
 * 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);
}
Beispiel #2
0
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);
}