Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS Tech Support » Image resizing in CExtLabel (or CExtGridCellPicture) Collapse All
Subject Author Date
Rolf Boerkoel Sep 21, 2009 - 7:53 AM

Dear support,


I have a CExtLabel to which I assign a bitmap. This bitmap is larger than the available client area of the CExtLabel, therefore I like it to be drawn smaller while retaining its original aspect ratio. How can I get this to work with the CExtLabel control? It looks like the bitmap is always drawn in a "stretch to fit" manner in this particular case, no matter what image mode or style is applied.


The same holds for the CExtGridCellPicture.


Thanks,


Marco

Rolf Boerkoel Sep 27, 2009 - 8:11 AM

I think you missed the point, I’ll try and explain it in other words.


The first remark is about the following piece of code. I think it is incorrect and can even be left out:


if( eImageMode == eTouchInside ) {     if( sizeNew.cx > sizeTouchSrc.cx )   rcTouchDst.OffsetRect( ( sizeNew.cx - sizeTouchSrc.cx ) / 2, 0 );  if( sizeNew.cy > sizeTouchSrc.cy )   rcTouchDst.OffsetRect( 0, ( sizeNew.cy - sizeTouchSrc.cy ) / 2 ); } else {  rcTouchDst.OffsetRect( - ( sizeNew.cx - sizeTouchSrc.cx ) / 2, 0 );  rcTouchDst.OffsetRect( 0, - ( sizeNew.cy - sizeTouchSrc.cy ) / 2 ); }


If "eImageMode == eTouchInside" evaluates to TRUE, the code within the block will never do anything because sizeNew.cx and sizeNew.cy can and will never become bigger than sizeTouchSrc.cx and sizeTouchSrc.cy (the new image size is already adapted to the client area)! So this part does nothing at all. As a result the whole image will be drawn within the client area positioned at the top-left corner. See the green asterisks in the attached image.


When "eImageMode <> eTouchInside" the effect of the "rcTouchDst.OffsetRect" will be that the center part of an image is filling the complete client area leaving out the left/right (or top/bottom) parts of the image when necessary, see the red asterisks. This may be the behaviour you intended, but it is inconsistent with the eTouchInside mode which doesn’t do any centering. I would rather leave this part out. So this whole if( eImageMode == eTouchInside ) { .... } else { .... } can better be omitted.




My second remark is about a solution to the positioning of the image within the client area. The native SS_LEFT, SS_CENTER and SS_RIGHT styles could be used to specify the horizontal position of the image within the client area. The SS_CENTERIMAGE can be used to specify positioning at the top (SS_CENTERIMAGE excluded) or middle (SS_CENTERIMAGE included). See attached image.




Regards,

Marco


image


 

Rolf Boerkoel Sep 24, 2009 - 3:42 AM

Thanks very much! However I think the following conditional test was intended?


//if( eImageMode == eTouchInside ) {
if( eImageMode == eTouchOutside )
  // Some OffsetRect calculations
} else {
  // Some other OffsetRect calculations
}



It also would be a great feature if the image can be aligned based on the SS_ static styles, especially left/top and hcenter/vcenter positioning. But for now I can get by with the current solution.


 

Technical Support Sep 25, 2009 - 6:40 AM

We used conditions like if( eImageMode == eTouchInside || eImageMode == eTouchOutside ) because both algorithms are exactly the same. They just use different scaling coefficients. The static styles are not needed because there is a much better approach is available even with old versions of label control and picture grid cell. The new versions stretch the image on-the-fly and each time when it should be painted. The stretching quality is enough good but it’s enough far from the real best stretching quality. We recommend you not to use the new feature. It’s much better to stretch the image before assigning it to label control or picture grid cell. The CExtBitmap class supports linear image scaling algorithms which provide much better stretching quality than GDI. Here is how you can scale an image:

CExtBitmap _bmp;
      _bmp.Load . . . ( . . . );
CSize sizeDst = . . . ; // compute it from real label control size for instance
CExtBitmap::Filter _f( CExtBitmap::Filter::lanczos );
      _bmp.Scale( sizeDst.cx, sizeDst.cy, _f );

The high quality stretching performed in the code snippet above is enough fast to do during label control or picture grid cell initialization.

Technical Support Sep 23, 2009 - 10:06 AM

Thank you for the interesting question. First of all, we improved the image mode enumeration with adding two new constants for proportional image stretching:

   enum e_ImageMode_t
            {
                        eAlign = 0,                                // The image is aligned according to the text aligning styles (the __EGCS_TA_HORZ_xxx and __EGCS_TA_VERT_xxx styles)
                        eTile = 1,                                   // The image is repeated until the entire available area is filled. 
                        eStretch = 2,                 // The image is stretched to fit all the available area.
                        eTouchInside = 3,          // Stretch the image proportionally and touch inside.
                        eTouchOutside = 4,       // Stretch the image proportionally and touch outside.
            };

Second, we modified the following method:
void CExtLabel::DoPaint(
            CDC * pDC,
            CRect & rcClient
            )
{
            ASSERT_VALID( this );
            ASSERT_VALID( pDC );
CExtMemoryDC dc( pDC, &rcClient );
CRgn rgnClient;
            if( rgnClient.CreateRectRgnIndirect( &rcClient ) )
                        dc.SelectClipRgn( &rgnClient );
            OnEraseBackground( dc, rcClient );
DWORD dwWndStyle = GetStyle();
DWORD dwWndType = (dwWndStyle&SS_TYPEMASK);
bool bCenterImage = ( (dwWndStyle&SS_CENTERIMAGE) != 0 );
            if( ! m_bmp.IsEmpty() )
            {
                        bool bSmootherAsPossible = true;
                        e_ImageMode_t eImageMode = GetImageMode();
                        if( eImageMode == eStretch )
                                    m_bmp.AlphaBlendSkinParts( dc.GetSafeHdc(), rcClient, CRect(0,0,0,0), CExtBitmap::__EDM_STRETCH, true, bSmootherAsPossible );
                        else if( eImageMode == eTouchInside || eImageMode == eTouchOutside )
                        {
                                    CRect rcTouchSurface = rcClient;
                                    CExtMemoryDC dcTouch( &dc, rcTouchSurface, CExtMemoryDC::MDCOPT_TO_MEMORY|CExtMemoryDC::MDCOPT_FILL_BITS|CExtMemoryDC::MDCOPT_RTL_COMPATIBILITY );
                                    if( dcTouch.GetSafeHdc() )
                                    {
                                                CSize sizeTouchSrc = rcTouchSurface.Size();
                                                CSize sizeBmp = m_bmp.GetSize();
                                                double lfAspectX = double(rcTouchSurface.Width())  / double(sizeBmp.cx);
                                                double lfAspectY = double(rcTouchSurface.Height()) / double(sizeBmp.cy);
                                                double lfAspect = ( eImageMode == eTouchInside ) ? ( min( lfAspectX, lfAspectY ) ) : ( max( lfAspectX, lfAspectY ) );
                                                CSize sizeNew( LONG(double(sizeBmp.cx)*lfAspect), LONG(double(sizeBmp.cy)*lfAspect) );
                                                CRect rcTouchDst( rcTouchSurface.left, rcTouchSurface.top, rcTouchSurface.left + sizeNew.cx, rcTouchSurface.top + sizeNew.cy );
                                                if( eImageMode == eTouchInside )
                                                {                                               
                                                            if( sizeNew.cx > sizeTouchSrc.cx )
                                                                        rcTouchDst.OffsetRect( ( sizeNew.cx - sizeTouchSrc.cx ) / 2, 0 );
                                                            if( sizeNew.cy > sizeTouchSrc.cy )
                                                                        rcTouchDst.OffsetRect( 0, ( sizeNew.cy - sizeTouchSrc.cy ) / 2 );
                                                }
                                                else
                                                {
                                                            rcTouchDst.OffsetRect( - ( sizeNew.cx - sizeTouchSrc.cx ) / 2, 0 );
                                                            rcTouchDst.OffsetRect( 0, - ( sizeNew.cy - sizeTouchSrc.cy ) / 2 );
                                                }
                                                INT nOldStretchBltMode = bSmootherAsPossible ? ( ::GetStretchBltMode( dcTouch.m_hDC ) ) : ( COLORONCOLOR ) ;
                                                if( bSmootherAsPossible )
                                                            ::SetStretchBltMode( dcTouch.m_hDC, ( g_PaintManager.m_bIsWinNT ) ? HALFTONE : COLORONCOLOR );
                                                m_bmp.AlphaBlend( dcTouch.m_hDC, rcTouchDst );
                                                if( bSmootherAsPossible )
                                                            ::SetStretchBltMode( dcTouch.m_hDC, nOldStretchBltMode );
                                    }
                        }
                        else if( eImageMode == eTile )
                                    m_bmp.AlphaBlendSkinParts(  dc.GetSafeHdc(), rcClient, CRect(0,0,0,0), CExtBitmap::__EDM_TILE, true, bSmootherAsPossible );
                        else if( eImageMode == eAlign )
                        {
                                    CSize szSize = m_bmp.GetSize();
                                    CRect rcDst( rcClient.left, rcClient.top, rcClient.left + szSize.cx, rcClient.top + szSize.cy );
                                    bool bCenterHorizontally = false;
                                    switch( dwWndType )
                                    {
                                    case SS_RIGHT:  rcDst.OffsetRect( rcClient.right - rcDst.right, 0 ); break;
                                    case SS_CENTER: bCenterHorizontally = true;        break;
                                    default: /* all the other types assumed as left */ break;
                                    }
                                    if( bCenterHorizontally )
                                                rcDst.OffsetRect( ( (rcClient.right - rcClient.left) - (rcDst.right - rcDst.left) ) / 2, 0 );
                                    if( bCenterImage )
                                                rcDst.OffsetRect( 0, ( (rcClient.bottom - rcClient.top) - (rcDst.bottom - rcDst.top) ) / 2 );
                                    CRect rcSrc( 0, 0, szSize.cx, szSize.cy );
                                    rcDst.top = max( rcDst.top, rcClient.top );
                                    rcDst.left = max( rcDst.left, rcClient.left );
                                    rcDst.bottom = min( rcDst.bottom, rcClient.bottom );
                                    rcDst.right = min( rcDst.right, rcClient.right );
                                    if( ::RectVisible( dc.GetSafeHdc(), &rcDst ) )
                                    {
                                                INT nOldStretchBltMode = bSmootherAsPossible ? ( ::GetStretchBltMode( dc.m_hDC ) ) : ( COLORONCOLOR );
                                                if( bSmootherAsPossible )
                                                            ::SetStretchBltMode( dc.m_hDC, ( g_PaintManager.m_bIsWinNT ) ? HALFTONE : COLORONCOLOR );
                                                m_bmp.AlphaBlend( dc.m_hDC, rcDst, rcSrc );
                                                if( bSmootherAsPossible )
                                                            ::SetStretchBltMode( dc.m_hDC, nOldStretchBltMode );
                                    }
                        }
            }
            else if( dwWndType == SS_ICON )
            {
                        HICON hIcon = GetIcon();
                        if( hIcon != NULL )
                        {
                                    CExtCmdIcon _icon;
                                    _icon.AssignFromHICON( hIcon, true );
                                    CSize szIcon = _icon.GetSize();
                                    int nOffsetX = bCenterImage ? (rcClient.Width() - szIcon.cx) / 2 : 0;
                                    int nOffsetY = bCenterImage ? (rcClient.Height() - szIcon.cy) / 2 : 0;
                                    _icon.Paint(
                                                PmBridge_GetPM(), dc.GetSafeHdc(), rcClient.left + nOffsetX, rcClient.top + nOffsetY, -1, -1,
                                                IsWindowEnabled() ? CExtCmdIcon::__PAINT_NORMAL : CExtCmdIcon::__PAINT_DISABLED
                                                );
                        }
            }
            else
            {
                        CExtSafeString strText;
                        int nTextLen = GetWindowTextLength();
                        if( nTextLen > 0 )
                        {
                                    GetWindowText( strText.GetBuffer( nTextLen + 2 ), nTextLen + 1 );
                                    strText.ReleaseBuffer();
                        }
                        if( strText.GetLength() > 0 )
                        {
                                    DWORD dwDrawTextFlags = 0;
                                    switch( dwWndType )
                                    {
                                    case SS_RIGHT:           dwDrawTextFlags = DT_RIGHT;  break; 
                                    case SS_CENTER:          dwDrawTextFlags = DT_CENTER; break;
                                    case SS_LEFTNOWORDWRAP:  dwDrawTextFlags = DT_LEFT;   break;
                                    default: /* all the other types assumed as left */ dwDrawTextFlags = DT_LEFT; break;
                                    } // switch( dwWndType )
                                    if( strText.Find( _T(’\t’) ) != -1 ) // do tabs expanding
                                                dwDrawTextFlags |= DT_EXPANDTABS;
                                    if(                     (dwWndType == SS_SIMPLE)
                                                ||           (dwWndStyle&(SS_CENTERIMAGE|SS_ENDELLIPSIS|SS_PATHELLIPSIS)) != 0
                                                )
                                    {
                                                dwDrawTextFlags |= DT_SINGLELINE;
                                                if( (dwWndStyle&SS_CENTERIMAGE) != 0 )
                                                            dwDrawTextFlags |= DT_VCENTER;
                                                if( (dwWndStyle&SS_ENDELLIPSIS) != 0 )
                                                            dwDrawTextFlags |= DT_END_ELLIPSIS;
                                                if( (dwWndStyle&SS_PATHELLIPSIS) != 0 )
                                                            dwDrawTextFlags |= DT_PATH_ELLIPSIS;
                                    }
                                    else
                                                dwDrawTextFlags |= DT_WORDBREAK;
                                    if( dwWndType == SS_LEFTNOWORDWRAP )
                                                dwDrawTextFlags &= ~(DT_WORDBREAK|DT_SINGLELINE);
                                    if( (dwWndStyle&SS_NOPREFIX) != 0 )
                                                dwDrawTextFlags |= DT_NOPREFIX;
                                    bool bEnabled = IsWindowEnabled() ? true : false;
                                    OnDrawLabelText( dc, rcClient, strText, dwDrawTextFlags, bEnabled );
                        } // if( strText.GetLength() > 0 )
            }
            PmBridge_GetPM()->OnPaintSessionComplete( this );
            if( rgnClient.GetSafeHandle() != NULL )
                        dc.SelectClipRgn( &rgnClient );  
}

Third, we modified the following method:
void CExtGridCellPictureBase::OnPaintText(
            const RECT & rcCellText,
            const CExtGridWnd & wndGrid,
            CDC & dc,
            LONG nVisibleColNo,
            LONG nVisibleRowNo,
            LONG nColNo,
            LONG nRowNo,
            INT nColType,
            INT nRowType,
            const RECT & rcCellExtra,
            const RECT & rcCell,
            const RECT & rcVisibleRange,
            DWORD dwAreaFlags,
            DWORD dwHelperPaintFlags
            ) const
{
            ASSERT_VALID( this );
            ASSERT_VALID( (&wndGrid) );
            ASSERT( dc.GetSafeHdc() != NULL );
            if( ! dc.RectVisible(&rcCellText) )
                        return;
            wndGrid; nVisibleColNo; nVisibleRowNo; nColNo; nRowNo; nColType; nRowType; rcCellExtra; rcCell; rcVisibleRange; dwAreaFlags; dwHelperPaintFlags;
            if( IsInvisible() || IsUndefined() || IsEmpty() )
                        return;
const CExtBitmap * pBmpBuffer = BitmapGetBuffer();
            if( pBmpBuffer == NULL || pBmpBuffer->IsEmpty() )
                        return;
            if( ( dwHelperPaintFlags & __EGCPF_SIMPLIFIED_RENDERING_TARGET ) != 0 )
            {
                        CRect rcX( rcCellExtra );
                        CExtMemoryDC dcX( &dc, &rcX );
                        OnPaintText( rcCellText, wndGrid, dcX, nVisibleColNo, nVisibleRowNo, nColNo, nRowNo, nColType, nRowType, rcCellExtra, rcCell, rcVisibleRange, dwAreaFlags, dwHelperPaintFlags&(~__EGCPF_SIMPLIFIED_RENDERING_TARGET) );
                        return;
            }
bool bSmootherAsPossible = true;
            if( m_eImageMode == eStretch )
            {
                        CRect rcStretch = rcCellText;
                        rcStretch.InflateRect( 2, 0 );
                        pBmpBuffer->AlphaBlendSkinParts( dc.GetSafeHdc(), rcStretch, CRect(0,0,0,0), CExtBitmap::__EDM_STRETCH, true, bSmootherAsPossible );
            }
            else if( m_eImageMode == eTouchInside || m_eImageMode == eTouchOutside )
            {
                        CRect rcTouchSurface = rcCellText;
                        CExtMemoryDC dcTouch( &dc, rcTouchSurface, CExtMemoryDC::MDCOPT_TO_MEMORY|CExtMemoryDC::MDCOPT_FILL_BITS|CExtMemoryDC::MDCOPT_RTL_COMPATIBILITY );
                        if( dcTouch.GetSafeHdc() )
                        {
                                    CSize sizeTouchSrc = rcTouchSurface.Size();
                                    CSize sizeBmp = pBmpBuffer->GetSize();
                                    double lfAspectX = double(rcTouchSurface.Width())  / double(sizeBmp.cx);
                                    double lfAspectY = double(rcTouchSurface.Height()) / double(sizeBmp.cy);
                                    double lfAspect = ( m_eImageMode == eTouchInside ) ? ( min( lfAspectX, lfAspectY ) ) : ( max( lfAspectX, lfAspectY ) );
                                    CSize sizeNew( LONG(double(sizeBmp.cx)*lfAspect), LONG(double(sizeBmp.cy)*lfAspect) );
                                    CRect rcTouchDst( rcTouchSurface.left, rcTouchSurface.top, rcTouchSurface.left + sizeNew.cx, rcTouchSurface.top + sizeNew.cy );
                                    if( m_eImageMode == eTouchInside )
                                    {                                               
                                                if( sizeNew.cx > sizeTouchSrc.cx )
                                                            rcTouchDst.OffsetRect( ( sizeNew.cx - sizeTouchSrc.cx ) / 2, 0 );
                                                if( sizeNew.cy > sizeTouchSrc.cy )
                                                            rcTouchDst.OffsetRect( 0, ( sizeNew.cy - sizeTouchSrc.cy ) / 2 );
                                    }
                                    else
                                    {
                                                rcTouchDst.OffsetRect( - ( sizeNew.cx - sizeTouchSrc.cx ) / 2, 0 );
                                                rcTouchDst.OffsetRect( 0, - ( sizeNew.cy - sizeTouchSrc.cy ) / 2 );
                                    }
                                    INT nOldStretchBltMode = bSmootherAsPossible ? ( ::GetStretchBltMode( dcTouch.m_hDC ) ) : ( COLORONCOLOR ) ;
                                    if( bSmootherAsPossible )
                                                ::SetStretchBltMode( dcTouch.m_hDC, ( g_PaintManager.m_bIsWinNT ) ? HALFTONE : COLORONCOLOR );
                                    pBmpBuffer->AlphaBlend( dcTouch.m_hDC, rcTouchDst );
                                    if( bSmootherAsPossible )
                                                ::SetStretchBltMode( dcTouch.m_hDC, nOldStretchBltMode );
                        }
            }
            else if( m_eImageMode == eTile )
                        pBmpBuffer->AlphaBlendSkinParts(  dc.GetSafeHdc(), rcCellText, CRect(0,0,0,0), CExtBitmap::__EDM_TILE, true, bSmootherAsPossible );
            else if( m_eImageMode == eAlign )
            {
                        CSize szSize = pBmpBuffer->GetSize();
                        CRect rcDst( rcCellText.left, rcCellText.top, rcCellText.left + szSize.cx, rcCellText.top + szSize.cy );
                        DWORD dwCellStyle = GetStyle();
                        switch( (dwCellStyle&__EGCS_TA_HORZ_MASK) )
                        {
                        case __EGCS_TA_HORZ_BY_TYPE:
                        case __EGCS_TA_HORZ_LEFT:
                        break;
                        case __EGCS_TA_HORZ_RIGHT:
                                    rcDst.OffsetRect( rcCellText.right - rcDst.right, 0 );
                        break;
                        case __EGCS_TA_HORZ_CENTER:
                                    rcDst.OffsetRect( ( (rcCellText.right - rcCellText.left) - (rcDst.right - rcDst.left) ) / 2, 0 );
                        break;
#ifdef _DEBUG
                        default:
                                    ASSERT( FALSE );
                        break;
#endif // _DEBUG
                        }
                        switch( (dwCellStyle&__EGCS_TA_VERT_MASK) )
                        {
                        case __EGCS_TA_VERT_BY_TYPE:
                        case __EGCS_TA_VERT_TOP:
                        break;
                        case __EGCS_TA_VERT_BOTTOM:
                                    rcDst.OffsetRect( 0, rcCellText.bottom - rcDst.bottom );
                        break;
                        case __EGCS_TA_VERT_MIDDLE:
                                    rcDst.OffsetRect( 0, ( (rcCellText.bottom - rcCellText.top) - (rcDst.bottom - rcDst.top) ) / 2 );
                        break;
#ifdef _DEBUG
                        default:
                                    ASSERT( FALSE );
                        break;
#endif // _DEBUG
                        }
                        CRect rcSrc( 0, 0, szSize.cx, szSize.cy );
                        rcDst.top = max( rcDst.top, rcCellText.top );
                        rcDst.left = max( rcDst.left, rcCellText.left );
                        rcDst.bottom = min( rcDst.bottom, rcCellText.bottom );
                        rcDst.right = min( rcDst.right, rcCellText.right );
                        if( ::RectVisible( dc.GetSafeHdc(), &rcDst ) )
                        {
                                    INT nOldStretchBltMode = bSmootherAsPossible ? ( ::GetStretchBltMode( dc.m_hDC ) ) : ( COLORONCOLOR ) ;
                                    if( bSmootherAsPossible )
                                                ::SetStretchBltMode( dc.m_hDC, ( g_PaintManager.m_bIsWinNT ) ? HALFTONE : COLORONCOLOR );
                                    pBmpBuffer->AlphaBlend( dc.m_hDC, rcDst, rcSrc );
                                    if( bSmootherAsPossible )
                                                ::SetStretchBltMode( dc.m_hDC, nOldStretchBltMode );
                        }
            }
}

Now both the label control and picture cell support proportional image resizing.