dib.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 804 行 · 第 1/2 页

CPP
804
字号
    }

    if ( !biClrUsed )
    {
        return ConvertToBitmap((BITMAPINFO *)&ds.dsBmih, hdc, ds.dsBm.bmBits);
    }
    else
    {
        // fake a BITMAPINFO w/o bits, just the palette info
        wxCharBuffer bmi(sizeof(BITMAPINFO) + (biClrUsed - 1)*sizeof(RGBQUAD));
        BITMAPINFO *pBmi = (BITMAPINFO *)bmi.data();
        MemoryHDC hDC;
        // get the colour table
        SelectInHDC sDC(hDC, m_handle);
        ::GetDIBColorTable(hDC, 0, biClrUsed, pBmi->bmiColors);
        memcpy(&pBmi->bmiHeader, &ds.dsBmih, ds.dsBmih.biSize);

        return ConvertToBitmap(pBmi, hdc, ds.dsBm.bmBits);
    }
}

/* static */
HBITMAP wxDIB::ConvertToBitmap(const BITMAPINFO *pbmi, HDC hdc, void *bits)
{
    wxCHECK_MSG( pbmi, 0, _T("invalid DIB in ConvertToBitmap") );

    // here we get BITMAPINFO struct followed by the actual bitmap bits and
    // BITMAPINFO starts with BITMAPINFOHEADER followed by colour info
    const BITMAPINFOHEADER *pbmih = &pbmi->bmiHeader;

    // get the pointer to the start of the real image data if we have a plain
    // DIB and not a DIB section (in the latter case the pointer must be passed
    // to us by the caller)
    if ( !bits )
    {
        // we must skip over the colour table to get to the image data
        //
        // colour table either has the real colour data in which case its
        // number of entries is given by biClrUsed or is used for masks to be
        // used for extracting colour information from true colour bitmaps in
        // which case it always have exactly 3 DWORDs
        int numColors;
        switch ( pbmih->biCompression )
        {
            case BI_BITFIELDS:
                numColors = 3;
                break;

            case BI_RGB:
                // biClrUsed has the number of colors but it may be not initialized at
                // all
                numColors = pbmih->biClrUsed;
                if ( !numColors )
                {
                    numColors = GetNumberOfColours(pbmih->biBitCount);
                }
                break;

            default:
                // no idea how it should be calculated for the other cases
                numColors = 0;
        }

        bits = (char *)pbmih + sizeof(*pbmih) + numColors*sizeof(RGBQUAD);
    }

    HBITMAP hbmp = ::CreateDIBitmap
                     (
                        hdc ? hdc           // create bitmap compatible
                            : (HDC) ScreenHDC(),  //  with this DC
                        pbmih,              // used to get size &c
                        CBM_INIT,           // initialize bitmap bits too
                        bits,               // ... using this data
                        pbmi,               // this is used for palette only
                        DIB_RGB_COLORS      // direct or indexed palette?
                     );

    if ( !hbmp )
    {
        wxLogLastError(wxT("CreateDIBitmap"));
    }

    return hbmp;
}

/* static */
size_t wxDIB::ConvertFromBitmap(BITMAPINFO *pbi, HBITMAP hbmp)
{
    wxASSERT_MSG( hbmp, wxT("invalid bmp can't be converted to DIB") );

    // prepare all the info we need
    BITMAP bm;
    if ( !::GetObject(hbmp, sizeof(bm), &bm) )
    {
        wxLogLastError(wxT("GetObject(bitmap)"));

        return 0;
    }

    // we need a BITMAPINFO anyhow and if we're not given a pointer to it we
    // use this one
    BITMAPINFO bi2;

    const bool wantSizeOnly = pbi == NULL;
    if ( wantSizeOnly )
        pbi = &bi2;

    // just for convenience
    const int h = bm.bmHeight;

    // init the header
    BITMAPINFOHEADER& bi = pbi->bmiHeader;
    wxZeroMemory(bi);
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bm.bmWidth;
    bi.biHeight = h;
    bi.biPlanes = 1;
    bi.biBitCount = bm.bmBitsPixel;

    // memory we need for BITMAPINFO only
    DWORD dwLen = bi.biSize + GetNumberOfColours(bm.bmBitsPixel) * sizeof(RGBQUAD);

    // get either just the image size or the image bits
    if ( !::GetDIBits
            (
                ScreenHDC(),                        // the DC to use
                hbmp,                               // the source DDB
                0,                                  // first scan line
                h,                                  // number of lines to copy
                wantSizeOnly ? NULL                 // pointer to the buffer or
                             : (char *)pbi + dwLen, // NULL if we don't have it
                pbi,                                // bitmap header
                DIB_RGB_COLORS                      // or DIB_PAL_COLORS
            ) )
    {
        wxLogLastError(wxT("GetDIBits()"));

        return 0;
    }

    // return the total size
    return dwLen + bi.biSizeImage;
}

/* static */
HGLOBAL wxDIB::ConvertFromBitmap(HBITMAP hbmp)
{
    // first calculate the size needed
    const size_t size = ConvertFromBitmap(NULL, hbmp);
    if ( !size )
    {
        // conversion to DDB failed?
        return NULL;
    }

    HGLOBAL hDIB = ::GlobalAlloc(GMEM_MOVEABLE, size);
    if ( !hDIB )
    {
        // this is an error which does risk to happen especially under Win9x
        // and which the user may understand so let him know about it
        wxLogError(_("Failed to allocated %luKb of memory for bitmap data."),
                   (unsigned long)(size / 1024));

        return NULL;
    }

    if ( !ConvertFromBitmap((BITMAPINFO *)(void *)GlobalPtrLock(hDIB), hbmp) )
    {
        // this really shouldn't happen... it worked the first time, why not
        // now?
        wxFAIL_MSG( _T("wxDIB::ConvertFromBitmap() unexpectedly failed") );

        return NULL;
    }

    return hDIB;
}

#endif // __WXWINCE__

// ----------------------------------------------------------------------------
// palette support
// ----------------------------------------------------------------------------

#if wxUSE_PALETTE

wxPalette *wxDIB::CreatePalette() const
{
    // GetDIBColorTable not available in eVC3
#if defined(_WIN32_WCE) && _WIN32_WCE < 400
    return NULL;
#else
    wxCHECK_MSG( m_handle, NULL, _T("wxDIB::CreatePalette(): invalid object") );

    DIBSECTION ds;
    if ( !GetDIBSection(m_handle, &ds) )
    {
        wxLogLastError(_T("GetObject(hDIB)"));

        return 0;
    }

    // how many colours are we going to have in the palette?
    DWORD biClrUsed = ds.dsBmih.biClrUsed;
    if ( !biClrUsed )
    {
        // biClrUsed field might not be set
        biClrUsed = GetNumberOfColours(ds.dsBmih.biBitCount);
    }

    if ( !biClrUsed )
    {
        // bitmaps of this depth don't have palettes at all
        //
        // NB: another possibility would be to return
        //     GetStockObject(DEFAULT_PALETTE) or even CreateHalftonePalette()?
        return NULL;
    }

    MemoryHDC hDC;

    // LOGPALETTE struct has only 1 element in palPalEntry array, we're
    // going to have biClrUsed of them so add necessary space
    LOGPALETTE *pPalette = (LOGPALETTE *)
        malloc(sizeof(LOGPALETTE) + (biClrUsed - 1)*sizeof(PALETTEENTRY));
    wxCHECK_MSG( pPalette, NULL, _T("out of memory") );

    // initialize the palette header
    pPalette->palVersion = 0x300;  // magic number, not in docs but works
    pPalette->palNumEntries = (WORD)biClrUsed;

    // and the colour table
    wxCharBuffer rgb(sizeof(RGBQUAD) * biClrUsed);
    RGBQUAD *pRGB = (RGBQUAD*)rgb.data();
    SelectInHDC selectHandle(hDC, m_handle);
    ::GetDIBColorTable(hDC, 0, biClrUsed, pRGB);
    for ( DWORD i = 0; i < biClrUsed; i++, pRGB++ )
    {
        pPalette->palPalEntry[i].peRed = pRGB->rgbRed;
        pPalette->palPalEntry[i].peGreen = pRGB->rgbGreen;
        pPalette->palPalEntry[i].peBlue = pRGB->rgbBlue;
        pPalette->palPalEntry[i].peFlags = 0;
    }

    HPALETTE hPalette = ::CreatePalette(pPalette);

    free(pPalette);

    if ( !hPalette )
    {
        wxLogLastError(_T("CreatePalette"));

        return NULL;
    }

    wxPalette *palette = new wxPalette;
    palette->SetHPALETTE((WXHPALETTE)hPalette);

    return palette;
#endif
}

#endif // wxUSE_PALETTE

// ----------------------------------------------------------------------------
// wxImage support
// ----------------------------------------------------------------------------

#if wxUSE_IMAGE

bool wxDIB::Create(const wxImage& image)
{
    wxCHECK_MSG( image.Ok(), false, _T("invalid wxImage in wxDIB ctor") );

    const int h = image.GetHeight();
    const int w = image.GetWidth();

    // if we have alpha channel, we need to create a 32bpp RGBA DIB, otherwise
    // a 24bpp RGB is sufficient
    m_hasAlpha = image.HasAlpha();
    const int bpp = m_hasAlpha ? 32 : 24;

    if ( !Create(w, h, bpp) )
        return false;

    // DIBs are stored in bottom to top order (see also the comment above in
    // Create()) so we need to copy bits line by line and starting from the end
    const int srcBytesPerLine = w * 3;
    const int dstBytesPerLine = GetLineSize(w, bpp);
    const unsigned char *src = image.GetData() + ((h - 1) * srcBytesPerLine);
    const unsigned char *alpha = m_hasAlpha ? image.GetAlpha() + (h - 1)*w
                                            : NULL;
    unsigned char *dstLineStart = (unsigned char *)m_data;
    for ( int y = 0; y < h; y++ )
    {
        // copy one DIB line
        unsigned char *dst = dstLineStart;
        if ( alpha )
        {
            for ( int x = 0; x < w; x++ )
            {
                // RGB order is reversed, and we need to premultiply
                // all channels by alpha value for use with ::AlphaBlend.
                const unsigned char a = *alpha++;
                *dst++ = (unsigned char)((src[2] * a + 127) / 255);
                *dst++ = (unsigned char)((src[1] * a + 127) / 255);
                *dst++ = (unsigned char)((src[0] * a + 127) / 255);
                *dst++ = a;
                src += 3;
            }
        }
        else // no alpha channel
        {
            for ( int x = 0; x < w; x++ )
            {
                // RGB order is reversed.
                *dst++ = src[2];
                *dst++ = src[1];
                *dst++ = src[0];
                src += 3;
            }
        }

        // pass to the previous line in the image
        src -= 2*srcBytesPerLine;
        if ( alpha )
            alpha -= 2*w;

        // and to the next one in the DIB
        dstLineStart += dstBytesPerLine;
    }

    return true;
}

wxImage wxDIB::ConvertToImage() const
{
    wxCHECK_MSG( IsOk(), wxNullImage,
                    wxT("can't convert invalid DIB to wxImage") );

    // create the wxImage object
    const int w = GetWidth();
    const int h = GetHeight();
    wxImage image(w, h, false /* don't bother clearing memory */);
    if ( !image.Ok() )
    {
        wxFAIL_MSG( wxT("could not allocate data for image") );
        return wxNullImage;
    }

    if ( m_hasAlpha )
    {
        image.SetAlpha();
    }

    // this is the same loop as in Create() just above but with copy direction
    // reversed
    const int bpp = GetDepth();
    const int dstBytesPerLine = w * 3;
    const int srcBytesPerLine = GetLineSize(w, bpp);
    unsigned char *dst = image.GetData() + ((h - 1) * dstBytesPerLine);
    unsigned char *alpha = image.HasAlpha() ? image.GetAlpha() + (h - 1)*w
                                            : NULL;
    const bool is32bit = bpp == 32;
    const unsigned char *srcLineStart = (unsigned char *)GetData();
    for ( int y = 0; y < h; y++ )
    {
        // copy one DIB line
        const unsigned char *src = srcLineStart;
        for ( int x = 0; x < w; x++ )
        {
            dst[2] = *src++;
            dst[1] = *src++;
            dst[0] = *src++;

            dst += 3;

            if ( is32bit )
            {
                if ( alpha )
                    *alpha++ = *src;
                src++;
            }
        }

        // pass to the previous line in the image
        dst -= 2*dstBytesPerLine;
        if ( alpha )
            alpha -= 2*w;

        // and to the next one in the DIB
        srcLineStart += srcBytesPerLine;
    }

    return image;
}

#endif // wxUSE_IMAGE

#endif // wxUSE_WXDIB

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?