2013년 2월 28일 목요일

WPF Custom Toggle Image Button

http://www.syntaxstudio.co.uk/2012/12/creating-a-toggle-image-button-control-in-wpf/

사이트의 소스를 수정하여 ImageButton 을 구현하였다. 기존 소스를 확장하여
Check/Uncheck 토글뿐 아니라 일반 버튼으로도 사용할 수 있다.

< ImageButton.xaml >
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
           xmlns:local="clr-namespace:SyntaxStudio.ToggleImageButton">  
   
   <Style x:Key="ImageButton" TargetType="{x:Type local:ImageButton}">  
     <Setter Property="Foreground" Value="#FFB8B8B8" />  
     <Setter Property="FontWeight" Value="Bold" />  
     <Setter Property="FontSize" Value="14" />  
     <Setter Property="Template">  
       <Setter.Value>  
         <ControlTemplate TargetType="{x:Type local:ImageButton}">  
           <Border x:Name="PART_Border"  
               Background="{TemplateBinding Background}"  
               BorderBrush="{TemplateBinding BorderBrush}"  
               BorderThickness="{TemplateBinding BorderThickness}">  
   
             <StackPanel>  
               <Grid>  
                 <Image x:Name="PART_Icon"  
                     Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentImage}"   
                     Width="{TemplateBinding Width}"   
                     Height="{TemplateBinding Height}" />  
                 <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"   
                     Margin="0,0,0,12"  
                     HorizontalAlignment="Center"  
                     VerticalAlignment="Bottom">  
                 </ContentPresenter>  
               </Grid>  
             </StackPanel>  
           </Border>  
           <!--  
           <ControlTemplate.Triggers>  
             <Trigger Property="IsChecked" Value="True">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveIcon}" />  
             </Trigger>  
             <Trigger Property="IsChecked" Value="False">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InActiveIcon}" />  
             </Trigger>  
           </ControlTemplate.Triggers>  
           -->  
         </ControlTemplate>  
       </Setter.Value>  
     </Setter>  
   </Style>  
   
   <Style x:Key="ImageButton1" TargetType="{x:Type local:ImageButton}">  
     <Setter Property="Foreground" Value="#FFB8B8B8" />  
     <Setter Property="Template">  
       <Setter.Value>  
         <ControlTemplate TargetType="{x:Type local:ImageButton}">  
           <Border x:Name="PART_Border"  
               Background="{TemplateBinding Background}"  
               BorderBrush="{TemplateBinding BorderBrush}"  
               BorderThickness="{TemplateBinding BorderThickness}">  
   
             <StackPanel>  
               <Grid>  
                 <Image x:Name="PART_Icon"  
                     Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentImage}"   
                     Width="{TemplateBinding Width}"   
                     Height="{TemplateBinding Height}" />  
                 <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"   
                     HorizontalAlignment="Center"  
                     VerticalAlignment="Center">  
                 </ContentPresenter>  
               </Grid>  
             </StackPanel>  
           </Border>  
           <!--  
           <ControlTemplate.Triggers>  
             <Trigger Property="IsChecked" Value="True">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveIcon}" />  
             </Trigger>  
             <Trigger Property="IsChecked" Value="False">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InActiveIcon}" />  
             </Trigger>  
           </ControlTemplate.Triggers>  
           -->  
         </ControlTemplate>  
       </Setter.Value>  
     </Setter>  
   </Style>  
 </ResourceDictionary>  

< ImageButton.cs >
 // --------------------------------  
 // <copyright file="ToggleImageButton.cs" company="SyntaxStudio">  
 //   Copyright 2012, www.syntaxstudio.co.uk  
 // </copyright>  
 // <author>Ryan Haworth</author>  
 // ---------------------------------  
   
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 using System.Windows;  
 using System.Windows.Controls;  
 using System.Windows.Data;  
 using System.Windows.Documents;  
 using System.Windows.Input;  
 using System.Windows.Media;  
 using System.Windows.Media.Imaging;  
 using System.Windows.Navigation;  
 using System.Windows.Shapes;  
 using System.Windows.Controls.Primitives;  
   
 namespace SyntaxStudio.ToggleImageButton  
 {  
   /// <summary>  
   /// The is is a custom control class for the toggle button. This toggle button will  
   /// accept two images. As the button is toggled between an inactive state to an  
   /// active state the button will change its image.  
   /// </summary>  
   public class ImageButton : ButtonBase  
   {  
     private static readonly DependencyProperty CurrentImageProperty = DependencyProperty.Register("CurrentImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty UnCheckedImageProperty = DependencyProperty.Register("UnCheckedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty UnCheckedHoverImageProperty = DependencyProperty.Register("UnCheckedHoverImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty CheckedImageProperty = DependencyProperty.Register("CheckedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty CheckedHoverImageProperty = DependencyProperty.Register("CheckedHoverImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty PressedImageProperty = DependencyProperty.Register("PressedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty DisabledImageProperty = DependencyProperty.Register("DisabledImage", typeof(ImageSource), typeof(ImageButton));  
   
     private bool isHovering = false;  
   
     private bool isChecked = false;  
     public bool IsChecked  
     {  
       get { return isChecked; }  
       set  
       {  
         if (value == false)  
         {  
           if (isHovering == false)  
             CurrentImage = UnCheckedImage;  
           else  
             CurrentImage = UnCheckedHoverImage;  
         }  
         else  
         {  
           if (isHovering == false)  
             CurrentImage = CheckedImage;  
           else  
             CurrentImage = CheckedHoverImage;  
         }  
         isChecked = value;  
       }  
     }  
   
     private bool isDisabled = false;  
     public bool IsDisabled  
     {  
       get { return isDisabled; }  
       set  
       {  
         if (value == false)  
         {  
           CurrentImage = UnCheckedImage;  
         }  
         else  
         {  
           CurrentImage = DisabledImage;  
         }  
       }  
     }  
   
     public ImageSource CurrentImage  
     {  
       get { return (ImageSource)GetValue(CurrentImageProperty); }  
       set { SetValue(CurrentImageProperty, value); }  
     }  
   
     public ImageSource UnCheckedImage  
     {  
       get { return (ImageSource)GetValue(UnCheckedImageProperty); }  
       set { SetValue(UnCheckedImageProperty, value); }  
     }  
   
     public ImageSource UnCheckedHoverImage  
     {  
       get { return (ImageSource)GetValue(UnCheckedHoverImageProperty); }  
       set { SetValue(UnCheckedHoverImageProperty, value); }  
     }  
   
     public ImageSource CheckedImage  
     {  
       get { return (ImageSource)GetValue(CheckedImageProperty); }  
       set { SetValue(CheckedImageProperty, value); }  
     }  
   
     public ImageSource CheckedHoverImage  
     {  
       get { return (ImageSource)GetValue(CheckedHoverImageProperty); }  
       set { SetValue(CheckedHoverImageProperty, value); }  
     }  
   
     public ImageSource PressedImage  
     {  
       get { return (ImageSource)GetValue(PressedImageProperty); }  
       set { SetValue(PressedImageProperty, value); }  
     }  
   
     public ImageSource DisabledImage  
     {  
       get { return (ImageSource)GetValue(DisabledImageProperty); }  
       set { SetValue(DisabledImageProperty, value); }  
     }  
   
     public ImageButton()  
     {  
       CurrentImage = UnCheckedImage;  
     }  
   
     static ImageButton()  
     {  
       DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));  
     }  
   
     protected override void OnMouseEnter(MouseEventArgs e)  
     {  
       base.OnMouseEnter(e);  
   
       isHovering = true;  
   
       if (IsChecked == false)  
       {  
         if (UnCheckedHoverImage != null)  
           CurrentImage = UnCheckedHoverImage;  
       }  
       else  
       {  
         if (CheckedHoverImage != null)  
           CurrentImage = CheckedHoverImage;  
       }  
     }  
   
     protected override void OnMouseLeave(MouseEventArgs e)  
     {  
       base.OnMouseLeave(e);  
   
       isHovering = false;  
   
       if (IsChecked == false)  
       {  
         if (UnCheckedImage != null)  
           CurrentImage = UnCheckedImage;  
       }  
       else  
       {  
         if (CheckedImage != null)  
           CurrentImage = CheckedImage;  
       }  
     }  
   
     protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)  
     {  
       base.OnMouseLeftButtonDown(e);  
       if (PressedImage != null)  
         CurrentImage = PressedImage;  
     }  
   
     protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)  
     {  
       base.OnMouseLeftButtonUp(e);  
       if (PressedImage != null)  
       {  
         if (IsChecked == true)  
           CurrentImage = CheckedImage;  
         else  
           CurrentImage = UnCheckedImage;  
       }  
     }  
   }  
 }  
   


< 사용예 >
...
xmlns:ToggleImageButton="clr-namespace:SyntaxStudio.ToggleImageButton" 
...
                <ToggleImageButton:ImageButton x:Name="btnToggleBorder" Width="65" Height="35" 
                                               CurrentImage="/images/uncheck.png"
                                               UnCheckedImage="/images/uncheck.png" UnCheckedHoverImage="/images/uncheck_hover.png" 
                                               CheckedImage="/images/check.png" CheckedHoverImage="check_hover.png" 
                                               Click="btnToggleBorder_Click"/>
...
btnToggleBorder.IsChecked = true/false 를 줘서 토글상태 변경




WPF Grid Sliding Animation

http://www.codeproject.com/Articles/18379/WPF-Tutorial-Part-2-Writing-a-custom-animation-cla
에 있는 클래스를 조금 수정하여 Grid 패널을 숨기고 보여주는 기능을 애니메이션으로 구현.
기존 클래스를 사용하면 Grid Column/Row 의 GridLength 속성이 Pixel 인 경우 제대로 동작하지 않는 문제와 애니메이션후 동작하지않는 문제가 있었고 위 주소를 참조하여 이를 해결.

< GridLengthAnimation.cs >

namespace GridAnimationDemo
{
    internal class GridLengthAnimation : AnimationTimeline
    {
        static GridLengthAnimation()
        {
            FromProperty = DependencyProperty.Register("From", typeof(GridLength),
                typeof(GridLengthAnimation));

            ToProperty = DependencyProperty.Register("To", typeof(GridLength),
                typeof(GridLengthAnimation));
        }

        public override Type TargetPropertyType
        {
            get
            {
                return typeof(GridLength);
            }
        }

        protected override System.Windows.Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation();
        }

        public static readonly DependencyProperty FromProperty;
        public GridLength From
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.FromProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.FromProperty, value);
            }
        }

        public static readonly DependencyProperty ToProperty;
        public GridLength To
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.ToProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.ToProperty, value);
            }
        }

        public override object GetCurrentValue(object defaultOriginValue,
            object defaultDestinationValue, AnimationClock animationClock)
        {
            double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
            double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;

            if (fromVal > toVal)
            {
                return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal,
                ((GridLength)GetValue(GridLengthAnimation.FromProperty)).GridUnitType);
            }

            return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal,
            ((GridLength)GetValue(GridLengthAnimation.ToProperty)).GridUnitType);
        }
    }
}

< Show/Hide 애니메이션 >

GridLength orgGridWidth = gridMain.ColumnDefinitions[0].Width;
int minPanelWidth = 30;
        private void btnSlidePanel_Click(object sender, RoutedEventArgs e)
        {
            if (gridMain.ColumnDefinitions[0].Width.Value > minPanelWidth)  // hide grid
            {
                orgGridWidth = gridMain.ColumnDefinitions[0].Width;                
                GridLengthAnimation gla = new GridLengthAnimation();
                gla.From = gridMain.ColumnDefinitions[0].Width;
                gla.To = new GridLength(minPanelWidth, GridUnitType.Pixel);
                gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
                gla.FillBehavior = FillBehavior.HoldEnd;
                gla.Completed += delegate(object o, EventArgs evt)
                {
                    gridMain.ColumnDefinitions[0].Width = new GridLength(minPanelWidth, GridUnitType.Pixel);
                    gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, null);
                    gla.FillBehavior = FillBehavior.Stop;
                };
                gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, gla);
            }
            else // show grid
            {
                GridLengthAnimation gla = new GridLengthAnimation();
                gla.From = gridMain.ColumnDefinitions[0].Width;
                gla.To = orgGridWidth;
                gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
                gla.FillBehavior = FillBehavior.HoldEnd;
                gla.Completed += delegate(object o, EventArgs evt)
                {
                    gridMain.ColumnDefinitions[0].Width = orgGridWidth;
                    gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, null);
                    gla.FillBehavior = FillBehavior.Stop;
                };
                gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, gla);
            }
        }


2012년 11월 2일 금요일

ffmpeg deblocking filter disable 시키기

h264.c

static int decode_slice_header(H264Context *h, H264Context *h0){
...
...

    h->deblocking_filter = 1;
    h->slice_alpha_c0_offset = 52;
    h->slice_beta_offset = 52;
    if( h->pps.deblocking_filter_parameters_present ) {
        tmp= get_ue_golomb_31(&s->gb);
        if(tmp > 2){
            av_log(s->avctx, AV_LOG_ERROR, "deblocking_filter_idc %u out of range\n", tmp);
            return -1;
        }
        h->deblocking_filter= tmp;
        if(h->deblocking_filter < 2)
            h->deblocking_filter^= 1; // 1<->0

        if( h->deblocking_filter ) {
            h->slice_alpha_c0_offset += get_se_golomb(&s->gb) << 1;
            h->slice_beta_offset     += get_se_golomb(&s->gb) << 1;
            if(   h->slice_alpha_c0_offset > 104U
               || h->slice_beta_offset     > 104U){
                av_log(s->avctx, AV_LOG_ERROR, "deblocking filter parameters %d %d out of range\n", h->slice_alpha_c0_offset, h->slice_beta_offset);
                return -1;
            }
        }
    }
h->deblocking_filter = 0;    // turn off deblocking filter
    if(   s->avctx->skip_loop_filter >= AVDISCARD_ALL
       ||(s->avctx->skip_loop_filter >= AVDISCARD_NONKEY && h->slice_type_nos != AV_PICTURE_TYPE_I)
       ||(s->avctx->skip_loop_filter >= AVDISCARD_BIDIR  && h->slice_type_nos == AV_PICTURE_TYPE_B)
       ||(s->avctx->skip_loop_filter >= AVDISCARD_NONREF && h->nal_ref_idc == 0))
        h->deblocking_filter= 0;
...
...

* 디블러킹 필터를 끄면 화질 열화현상이 발생하지만 약 20% 정도의 성능개선 효과를 볼 수 있다. 작은 화면을 플레이할때는 화질 열화현상이 눈에 잘 띄지않아서 경우에 따라 적용해봄직 하다.

2012년 10월 8일 월요일

DirectDraw Surface 에 Win32 GDI 함수로 그리기 - Drawing on DirectDraw Surface with Win32 GDI API


void drawSurface(RECT *rectTarget, IDirectDrawSurface7 *lpSurface)
{
HDC hdc;
hr=lpSurface->GetDC(&hdc);

int targetWidth = rectTarget->right-rectTarget->left;
int targetHeight = rectTarget->bottom-rectTarget->top;

HDC hMemDC;

hMemDC = CreateCompatibleDC(hdc);

HBITMAP hbmMem = CreateCompatibleBitmap(hdc, targetWidth, targetHeight);
SelectObject(hMemDC, hbmMem);
 .....
 .....
 TextOut(hMemDC, ....);
 .....
 TransparentBlt(hMemDC, ...);
 .....
 Rectangle(hMemDC, ...);
 ....
 ....

DeleteObject(hbmMem);
DeleteDC(hMemDC);

lpSurface->ReleaseDC(hdc);
}


* lpSurface->GetDC()/lpSurface->ReleaseDC() 하면서 lpSurface 는 lock/unlock 이 된다. 
따라서 위 함수 내부에서 DirectDraw Blt 함수가 호출되어서는 안된다.

* Win32 GDI 함수는 기본적으로 DirectDraw 계열 함수보다 훨씬 느리기때문에 위 함수를
사용할때는 두 개의 Surface를 두고 Blt 하는 Double-buffering 방식으로 구현하는것이 좋다.

2012년 9월 4일 화요일

ffmpeg audio encoding/decoding

< Encoder.h >
 #ifndef __ENCODER_H__  
 #define __ENCODER_H__  
 #include <windows.h>  
 extern "C" {  
 #include "libavcodec\avcodec.h"  
 }  
 class Encoder  
 {  
 public:  
      Encoder();  
      virtual ~Encoder();  
      static void initCodec();  
      virtual void close();  
      virtual int encodeFrame(AVFrame *frame) = 0;  
      AVFrame* frame() { return m_pFrame; }  
      unsigned char* outBuf() { return m_pOutBuf; }  
      int outBufSize() { return m_nOutBufSize; }  
 protected:  
      int prepareOpen(enum AVCodecID codecId);  
 protected:  
      AVCodec*          m_pCodec;  
      AVCodecContext*     m_pCodecCtx;  
      AVPacket          m_avPacket;  
      AVFrame*          m_pFrame;  
      unsigned char*     m_pOutBuf;  
      int                    m_nOutBufSize;  
      int                    m_nOutBufMaxSize;  
      static HANDLE     m_hMutexInit;  
      static bool          m_bInit;  
      static int          m_nCPU;  
 };  
 #endif  

< Encoder.cpp >
 #include "Encoder.h"  
 #include "GlobalEnv.h"  
 #include "FFMPEGUtil.h"  
 bool Encoder::m_bInit = false;  
 int Encoder::m_nCPU = 1;  
 HANDLE Encoder::m_hMutexInit = CreateMutex(NULL, FALSE, NULL);  
 Encoder::Encoder() : m_pCodec(NULL), m_pCodecCtx(NULL), m_pFrame(NULL)   
 {  
      initCodec();  
      m_pOutBuf = NULL;  
      m_nOutBufMaxSize = m_nOutBufSize = 0;  
 }  
 Encoder::~Encoder()  
 {  
      close();  
 }  
 void Encoder::initCodec()  
 {  
      WaitForSingleObject(m_hMutexInit, INFINITE);  
      if (!m_bInit) {  
           InitFFmpegLib();  
           SYSTEM_INFO sysinfo;  
           GetSystemInfo(&sysinfo);  
           m_nCPU = sysinfo.dwNumberOfProcessors;  
           m_bInit = true;  
      }  
      ReleaseMutex(m_hMutexInit);  
 }  
 int Encoder::prepareOpen(enum AVCodecID codecId)  
 {  
      av_init_packet(&m_avPacket);  
      m_pCodec = avcodec_find_encoder(codecId);  
      if (!m_pCodec) {  
           DXPRINTF("avcodec_find_encoder failed to find codec %d\n", codecId);  
           return -1;  
      }  
      m_pCodecCtx = avcodec_alloc_context3(m_pCodec);  
      if (!m_pCodecCtx) {  
           DXPRINTF("avcodec_alloc_context3 failed\n");  
           return -1;  
      }  
      m_pFrame = avcodec_alloc_frame();  
      if (!m_pFrame) {  
           DXPRINTF("avcodec_alloc_frame failed\n");            
           return -1;  
      }  
      return 0;  
 }  
 void Encoder::close()  
 {  
      if (m_pCodecCtx) {  
           avcodec_close(m_pCodecCtx);  
           av_free(m_pCodecCtx);  
           m_pCodecCtx = NULL;  
      }  
      avcodec_free_frame(&m_pFrame);  
      if (m_pOutBuf) {  
           delete[] m_pOutBuf;  
           m_pOutBuf = NULL;  
      }  
      m_nOutBufMaxSize = m_nOutBufSize = 0;  
 }  

< AudioEncoder.h >
 #ifndef __AUDIO_ENCODER_H__  
 #define __AUDIO_ENCODER_H__  
 #include "Encoder.h"  
 class AudioEncoder : public Encoder  
 {  
 public:  
      AudioEncoder();  
      virtual ~AudioEncoder();  
      int open(enum AVCodecID codec_id, int bit_rate, int sample_rate, int channels, int channel_layout, enum AVSampleFormat sample_fmt);  
      virtual void close();  
      virtual int encodeFrame(AVFrame *frame);  
      int prepareAudioFrame(unsigned short *pData, int size, int nb_samples);  
 };  
 #endif  

< AudioEncoder.cpp >
 #include "AudioEncoder.h"  
 #include "GlobalEnv.h"  
 /* check that a given sample format is supported by the encoder */  
 static int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt)  
 {  
   const enum AVSampleFormat *p = codec->sample_fmts;  
   while (*p != AV_SAMPLE_FMT_NONE) {  
     if (*p == sample_fmt)  
       return 1;  
     p++;  
   }  
   return 0;  
 }  
 /* just pick the highest supported samplerate */  
 static int select_sample_rate(AVCodec *codec)  
 {  
   const int *p;  
   int best_samplerate = 0;  
   if (!codec->supported_samplerates)  
     return 44100;  
   p = codec->supported_samplerates;  
   while (*p) {  
     best_samplerate = FFMAX(*p, best_samplerate);  
     p++;  
   }  
   return best_samplerate;  
 }  
 /* select layout with the highest channel count */  
 static int select_channel_layout(AVCodec *codec)  
 {  
   const uint64_t *p;  
   uint64_t best_ch_layout = 0;  
   int best_nb_channels  = 0;  
   if (!codec->channel_layouts)  
     return AV_CH_LAYOUT_STEREO;  
   p = codec->channel_layouts;  
   while (*p) {  
     int nb_channels = av_get_channel_layout_nb_channels(*p);  
     if (nb_channels > best_nb_channels) {  
       best_ch_layout  = *p;  
       best_nb_channels = nb_channels;  
     }  
     p++;  
   }  
   return best_ch_layout;  
 }  
 AudioEncoder::AudioEncoder() : Encoder()  
 {       
 }  
 AudioEncoder::~AudioEncoder()  
 {  
 }  
 int AudioEncoder::open(AVCodecID codec_id, int bit_rate, int sample_rate, int channels, int channel_layout, AVSampleFormat sample_fmt)  
 {  
      int err;  
      char errbuf[128];  
      err = prepareOpen(codec_id);  
      if (err < 0) return -1;  
      m_pCodecCtx->bit_rate = bit_rate;  
      m_pCodecCtx->sample_rate = sample_rate == 0 ? select_sample_rate(m_pCodec) : sample_rate;  
      m_pCodecCtx->channel_layout = channel_layout == 0 ? select_channel_layout(m_pCodec) : channel_layout;  
      m_pCodecCtx->channels = channels == 0 ? av_get_channel_layout_nb_channels(m_pCodecCtx->channel_layout) : channels;  
      m_pCodecCtx->sample_fmt = sample_fmt;  
      if (!check_sample_fmt(m_pCodec, m_pCodecCtx->sample_fmt)) {  
           DXPRINTF("failed to open audio encoder, Encoder does not support sample format %s\n",  
                av_get_sample_fmt_name(m_pCodecCtx->sample_fmt));  
           return -1;  
      }  
      if (err=avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avcodec_open2 %s open failed, err: %d %s\n", avcodec_get_name(codec_id), err, errbuf);  
           return -1;  
      }  
      DXPRINTF("audio encoder opened %s\n", avcodec_get_name(codec_id));  
      return 0;  
 }  
 void AudioEncoder::close()  
 {  
      Encoder::close();  
 }  
 int AudioEncoder::prepareAudioFrame(unsigned short *pData, int size, int nb_samples)  
 {  
      int err;  
      char errbuf[128];  
      avcodec_get_frame_defaults(m_pFrame);  
      m_pFrame->nb_samples = nb_samples == 0 ? m_pCodecCtx->frame_size : nb_samples;  
      m_pFrame->format = m_pCodecCtx->sample_fmt;  
      err = avcodec_fill_audio_frame(m_pFrame, m_pCodecCtx->channels, m_pCodecCtx->sample_fmt,  
           (const uint8_t *)pData, size, 0);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avcodec_fill_audio_frame failed, err : %d %s\n", err, errbuf);  
           return -1;  
      }  
      return 0;  
 }  
 int AudioEncoder::encodeFrame(AVFrame *frame)  
 {  
      int err;  
      char errbuf[128];  
      av_init_packet(&m_avPacket);  
      m_avPacket.data = NULL;  
      m_avPacket.size = 0;  
      int got_output;  
      err = avcodec_encode_audio2(m_pCodecCtx, &m_avPacket, frame, &got_output);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avcodec_encode_audio2 failed, err : %d %s\n", err, errbuf);  
           return -1;  
      }  
      if (got_output) {            
           if (m_avPacket.size > 0) {  
                if (m_nOutBufMaxSize < m_avPacket.size) {  
                     if (m_pOutBuf) delete[] m_pOutBuf;  
                     m_pOutBuf = new unsigned char[m_avPacket.size];  
                     m_nOutBufMaxSize = m_avPacket.size;  
                }  
                memcpy(m_pOutBuf, m_avPacket.data, m_avPacket.size);                 
           }  
           m_nOutBufSize = m_avPacket.size;  
           av_free_packet(&m_avPacket);  
      }  
      return got_output == 1 ? 0 : -1;  
 }  

< AudioEncoder 사용 >
 AudioEncoder*     m_pEncoder = new AudioEncoder();  
 m_pEncoder->open(AV_CODEC_ID_PCM_MULAW, 64000, 8000, 1, AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_S16);  
 ...  
 m_pEncoder->prepareAudioFrame((unsigned short*)pData, size, nb_samples);  
 int ret = m_pEncoder->encodeFrame(m_pEncoder->frame());
if (ret > 0) {  
   fwrite(m_pEncoder->outBuf(), m_pEncoder->outBufSize(), 1, fp);  
 } 
 ...  
 m_pEncoder->close();  
 delete m_pEncoder;  

< Decoder.h >
 #ifndef __DECODER_H__  
 #define __DECODER_H__  
 #include <windows.h>  
 extern "C" {  
 #include "libavcodec\avcodec.h"  
 }  
 #define MAX_DECODE_BUFFER_SIZE               (1024*1024)  
 class Decoder  
 {  
 public:  
      Decoder();  
      virtual ~Decoder();  
      static void initCodec();  
      virtual int open(enum AVCodecID codecId, int channel);  
      virtual int open(AVCodecContext *codec, int channel);  
      virtual void close();  
      virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame) = 0;  
      void flush();  
      enum AVCodecID codecID();  
      AVFrame* frame() { return m_pFrame; }  
      int width();  
      int height();  
      enum AVPixelFormat pixelFormat();  
 protected:  
      virtual int open(enum AVCodecID codecId, AVCodecContext *codec, int channel) = 0;  
      int prepareOpen(enum AVCodecID codecId, AVCodecContext *codec);  
      void prepareDecBuffer(unsigned char *inBuf, int inLen);  
 protected:  
      AVCodec*          m_pCodec;  
      AVCodecContext*     m_pCodecCtx;  
      AVFrame*          m_pFrame;  
      AVPacket          m_avPacket;  
      unsigned char*     m_pDecBuffer;  
      int                    m_nDecBufferSize;  
      bool               m_bCodecCtxAlloc;  
      static HANDLE     m_hMutexInit;  
      static bool          m_bInit;  
      static int          m_nCPU;  
      // dump  
      FILE               *m_pFile;  
 };  
 #endif  

< Decoder.cpp >
 #include "Decoder.h"  
 #include "GlobalEnv.h"  
 #include "FFMPEGUtil.h"  
 bool Decoder::m_bInit = false;  
 int Decoder::m_nCPU = 1;  
 HANDLE Decoder::m_hMutexInit = CreateMutex(NULL, FALSE, NULL);  
 Decoder::Decoder() : m_pCodec(NULL), m_pCodecCtx(NULL), m_pFrame(NULL), m_bCodecCtxAlloc(false), m_pFile(NULL)  
 {  
      initCodec();  
      m_pDecBuffer = new unsigned char[MAX_DECODE_BUFFER_SIZE];  
      m_nDecBufferSize = MAX_DECODE_BUFFER_SIZE;  
 }  
 Decoder::~Decoder()  
 {  
      if (m_pDecBuffer) {  
           delete[] m_pDecBuffer;  
           m_pDecBuffer = NULL;  
      }  
 }  
 void Decoder::initCodec()  
 {  
      WaitForSingleObject(m_hMutexInit, INFINITE);  
      if (!m_bInit) {  
           InitFFmpegLib();  
           SYSTEM_INFO sysinfo;  
           GetSystemInfo(&sysinfo);  
           m_nCPU = sysinfo.dwNumberOfProcessors;  
           m_bInit = true;  
      }  
      ReleaseMutex(m_hMutexInit);  
 }  
 int Decoder::open(AVCodecID codecId, int channel)  
 {  
      return open(codecId, NULL, channel);  
 }  
 int Decoder::open(AVCodecContext *codec, int channel)  
 {  
      return open(codec->codec_id, codec, channel);  
 }  
 int Decoder::prepareOpen(enum AVCodecID codecId, AVCodecContext *codec)  
 {  
      av_init_packet(&m_avPacket);  
      m_pCodec = avcodec_find_decoder(codecId);  
      if (!m_pCodec) {  
           DXPRINTF("avcodec_find_decoder failed to find codec %d\n", codecId);  
           return -1;  
      }  
      if (codec) {  
           m_pCodecCtx = codec;  
           m_bCodecCtxAlloc = false;  
      } else {  
           m_pCodecCtx = avcodec_alloc_context3(m_pCodec);  
           if (!m_pCodecCtx) {  
                DXPRINTF("avcodec_alloc_context3 failed\n");  
                return -1;  
           }  
           m_bCodecCtxAlloc = true;  
      }  
      m_pFrame = avcodec_alloc_frame();  
      if (!m_pFrame) {  
           DXPRINTF("avcodec_alloc_frame failed\n");            
           return -1;  
      }  
      return 0;  
 }  
 void Decoder::close()  
 {  
      if (m_bCodecCtxAlloc) {  
           if (m_pCodecCtx)  
                avcodec_close(m_pCodecCtx);  
           av_free(m_pCodecCtx);  
           m_bCodecCtxAlloc = false;  
      }  
      m_pCodecCtx = NULL;  
      avcodec_free_frame(&m_pFrame);  
      if (m_pFile) {  
           fclose(m_pFile);  
           m_pFile = NULL;  
      }  
 }  
 enum AVCodecID Decoder::codecID()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->codec_id;  
      return AV_CODEC_ID_NONE;  
 }  
 int Decoder::width()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->width;  
      return 0;  
 }  
 int Decoder::height()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->height;  
      return 0;  
 }  
 enum AVPixelFormat Decoder::pixelFormat()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->pix_fmt;  
      return AV_PIX_FMT_NONE;  
 }  
 void Decoder::prepareDecBuffer(unsigned char *inBuf, int inLen)  
 {  
      if (inLen+FF_INPUT_BUFFER_PADDING_SIZE > m_nDecBufferSize) {  
           delete[] m_pDecBuffer;  
           m_pDecBuffer = new unsigned char[inLen+FF_INPUT_BUFFER_PADDING_SIZE];  
           m_nDecBufferSize = inLen+FF_INPUT_BUFFER_PADDING_SIZE;  
      }  
      memset(&m_pDecBuffer[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);  
      memcpy(m_pDecBuffer, inBuf, inLen);  
      av_init_packet(&m_avPacket);  
      m_avPacket.data = m_pDecBuffer;  
      m_avPacket.size = inLen;  
 }  
 void Decoder::flush()  
 {  
      if (m_pCodecCtx)  
           avcodec_flush_buffers(m_pCodecCtx);  
 }  

< AudioDecoder.h>
 #ifndef __AUDIO_DECODER_H__  
 #define __AUDIO_DECODER_H__  
 #include "Decoder.h"  
 extern "C" {  
 #include "libswresample\swresample.h"  
 #include "libavutil\opt.h"  
 }  
 class AudioDecoder : public Decoder  
 {  
 public:  
      AudioDecoder();  
      virtual ~AudioDecoder();  
      virtual void close();  
      virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame);  
      unsigned char* outBuf() { return m_pOutBuf; }  
      int outBufSize() { return m_nOutBufSize; }  
      void setExtraData(unsigned char *extradata, int extradatasize);  
 protected:  
      virtual int open(enum AVCodecID codecId, AVCodecContext *codec, int channel);  
      int initResampler(uint64_t channel_layout, int sample_rate, enum AVSampleFormat sample_fmt);  
      int checkResampler();  
 protected:  
      struct SwrContext*     m_pSwrCtx;  
      unsigned char*     m_pOutBuf;  
      int                    m_nOutBufSize;  
      int                    m_nOutBufMaxSize;  
      unsigned char*     m_pExtraData;  
      int                    m_nExtraDataSize;  
 };  
 #endif  

< AudioDecoder.cpp >
 #include "AudioDecoder.h"  
 #include "GlobalEnv.h"  
 AudioDecoder::AudioDecoder() : Decoder(),  
 m_pSwrCtx(NULL), m_pOutBuf(NULL), m_nOutBufSize(0), m_nOutBufMaxSize(0), m_pExtraData(NULL), m_nExtraDataSize(0)  
 {  
 }  
 AudioDecoder::~AudioDecoder()  
 {  
 }  
 int AudioDecoder::open(enum AVCodecID codecId, AVCodecContext *codec, int channel)  
 {  
      int err;  
      char errbuf[128];  
      err = prepareOpen(codecId, codec);  
      if (err < 0) return -1;  
      if (m_bCodecCtxAlloc) {  
           m_pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;  
           m_pCodecCtx->channels = channel;  
           if (codecId == AV_CODEC_ID_PCM_MULAW) {  
                m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_U8;  
           } else if (codecId == AV_CODEC_ID_AAC) {  
                if (m_pExtraData) {  
                     m_pCodecCtx->extradata = m_pExtraData;  
                     m_pCodecCtx->extradata_size = m_nExtraDataSize;  
                }  
                m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;  
           } else {  
                m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;  
           }  
      }  
      if (err=avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avcodec_open2 %s open failed, err: %d %s\n", avcodec_get_name(codecId), err, errbuf);  
           goto exit;  
      }  
      int channel_layout = m_pCodecCtx->channel_layout;  
      if (channel_layout == 0) {  
           channel_layout = av_get_default_channel_layout(m_pCodecCtx->channels);  
      }  
      err = initResampler(channel_layout, m_pCodecCtx->sample_rate, m_pCodecCtx->sample_fmt);  
      if (err < 0) goto exit;  
      DXPRINTF("audio decoder opened %s\n", avcodec_get_name(codecId));  
 exit:  
      return err;  
 }  
 int AudioDecoder::initResampler(uint64_t channel_layout, int sample_rate, enum AVSampleFormat sample_fmt)  
 {  
      int err;  
      char errbuf[128];  
      if (m_pSwrCtx) {  
           swr_free(&m_pSwrCtx);  
           m_pSwrCtx = NULL;  
      }  
      m_pSwrCtx = swr_alloc();  
      av_opt_set_int(m_pSwrCtx, "in_channel_layout", channel_layout, 0);  
      av_opt_set_int(m_pSwrCtx, "in_sample_rate", sample_rate, 0);  
      av_opt_set_sample_fmt(m_pSwrCtx, "in_sample_fmt", sample_fmt, 0);  
      av_opt_set_int(m_pSwrCtx, "out_channel_layout", channel_layout, 0);  
      av_opt_set_int(m_pSwrCtx, "out_sample_rate", sample_rate, 0);  
      av_opt_set_sample_fmt(m_pSwrCtx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);  
      err = swr_init(m_pSwrCtx);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("swr_init failed, err: %d %s\n", err, errbuf);  
           return err;  
      }  
      return 0;  
 }  
 void AudioDecoder::close()  
 {  
      Decoder::close();  
      if (m_pSwrCtx) {  
           swr_free(&m_pSwrCtx);  
           m_pSwrCtx = NULL;  
      }  
      if (m_pExtraData) {  
           delete[] m_pExtraData;  
           m_pExtraData = NULL;  
           m_nExtraDataSize = 0;  
      }  
      if (m_pOutBuf) {  
           delete[] m_pOutBuf;  
           m_pOutBuf = NULL;  
           m_nOutBufMaxSize = m_nOutBufSize = 0;  
      }  
      if (m_pFile) {  
           fclose(m_pFile);  
           m_pFile = NULL;  
      }  
 }  
 int AudioDecoder::decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame)  
 {  
      if (!m_pCodecCtx || !inBuf || inLen <= 0) return 0;  
      int retLen = 0, got_frame;  
      prepareDecBuffer(inBuf, inLen);  
      __try  
      {  
           retLen = avcodec_decode_audio4(m_pCodecCtx, m_pFrame, &got_frame, &m_avPacket);  
      }  
      __except (EXCEPTION_EXECUTE_HANDLER)  
      {  
           GlobalEnv::WriteLogFile("[%s] avcodec_decode_audio4 crashed, (exception code : 0x%x)",   
                __FUNCTION__, GetExceptionCode());  
      }  
      if (retLen <= 0) {  
           DXPRINTF("audio decode error : %d\n", retLen);  
           return retLen;  
      }  
      int out_size = av_samples_get_buffer_size(NULL, m_pCodecCtx->channels, m_pFrame->nb_samples,  
           m_pCodecCtx->sample_fmt, 1);  
      if (out_size > m_nOutBufMaxSize) {  
           if (m_pOutBuf) delete[] m_pOutBuf;  
           m_pOutBuf = new unsigned char[out_size];  
           m_nOutBufMaxSize = out_size;  
      }  
      if (m_pSwrCtx) {  
           // audio resampling  
           if (checkResampler() < 0) return -1;  
           __try  
           {  
                retLen = swr_convert(m_pSwrCtx, &m_pOutBuf, out_size,   
                     (const uint8_t **)m_pFrame->extended_data, m_pFrame->nb_samples);  
           }  
           __except (EXCEPTION_EXECUTE_HANDLER)  
           {  
                GlobalEnv::WriteLogFile("[%s] swr_convert crashed, (exception code : 0x%x)",  
                     __FUNCTION__, GetExceptionCode());  
           }  
      } else {  
           if (m_pFrame->channels == 1) {  
                memcpy(m_pOutBuf, m_pFrame->extended_data[0], out_size);  
           } else {  
                uint8_t *data0 = m_pFrame->extended_data[0];  
                uint8_t *data1 = m_pFrame->extended_data[1];  
                int c = 0;  
                for (int i=0; i<out_size/2; i+=2) {  
                     m_pOutBuf[c++] = data0[i]; m_pOutBuf[c++] = data0[i+1];  
                     m_pOutBuf[c++] = data1[i]; m_pOutBuf[c++] = data1[i+1];  
                }  
                out_size = c;  
           }  
      }  
      out_size = retLen*m_pCodecCtx->channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);  
      m_nOutBufSize = out_size;  
      if (m_pFile)  
           fwrite(m_pOutBuf, m_nOutBufSize, 1, m_pFile);  
      return retLen;  
 }  
 void AudioDecoder::setExtraData(unsigned char *extradata, int extradatasize)  
 {  
      if (m_pExtraData) {  
           delete[] m_pExtraData;  
           m_pExtraData = NULL;  
           m_nExtraDataSize = 0;  
      }  
      if (extradata == NULL || extradatasize <= 0)  
           return;  
      m_pExtraData = new unsigned char[extradatasize];  
      memset(m_pExtraData, 0, extradatasize);  
      memcpy(m_pExtraData, extradata, extradatasize);  
      m_nExtraDataSize = extradatasize;  
 }  
 int AudioDecoder::checkResampler()  
 {  
      int err;  
      int64_t channel_layout = 0;  
      int64_t sample_rate = 0;  
      int64_t sample_fmt = -1;  
      av_opt_get_int(m_pSwrCtx, "in_channel_layout", 0, &channel_layout);  
      av_opt_get_int(m_pSwrCtx, "in_sample_rate", 0, &sample_rate);  
      av_opt_get_int(m_pSwrCtx, "in_sample_fmt", 0, &sample_fmt);  
      if ((m_pCodecCtx->channel_layout != 0) && (channel_layout != m_pCodecCtx->channel_layout) ||  
           sample_rate != m_pCodecCtx->sample_rate ||  
           sample_fmt != m_pCodecCtx->sample_fmt) {  
           err = initResampler(m_pCodecCtx->channel_layout, m_pCodecCtx->sample_rate, m_pCodecCtx->sample_fmt);  
           if (err < 0)   
                return err;  
      }  
      return 0;  
 }  

< AudioDecoder 사용 >
 AudioDecoder *m_pDecoder = new AudioDecoder();  
 m_pDecoder->open(codec_id, channel);  
 ...  
 int ret = m_pDecoder->decodeFrame(pData, size, m_pDecoder->frame());  
 if (ret > 0) {  
   fwrite(m_pDecoder->outBuf(), m_pDecoder->outBufSize(), 1, fp);  
 }  
 ...  
 m_pDecoder->close();  
 delete m_pDecoder;  

DirectDraw Surface Transparent Blt


void CopySurface(IDirectDrawSurface7 *lpSurfaceDst, RECT &rectTarget, IDirectDrawSurface7 *lpSurfaceSrc, RECT &rectSrc)
{
DDBLTFX ddbltfx;
ZeroMemory(&ddbltfx, sizeof(ddbltfx));
ddbltfx.dwSize = sizeof(ddbltfx);

DDCOLORKEY colorKey = {RGB(0,0,0), RGB(0,0,0)};    // Transparent Color - black
ddbltfx.ddckSrcColorkey = colorKey;

DWORD flags = DDBLT_ASYNC | DDBLT_KEYSRCOVERRIDE;

HRESULT hr = lpSurfaceDst->Blt(&rectTarget, lpSurfaceSrc, &rectSrc, flags, &ddbltfx);
if (FAILED(hr)) {
TRACE("[%s] hr:0x%x\n", __FUNCTION__, hr);
}
}

DirectDraw Surface 알파블랜딩 - DirectDraw Surface Alpha Blending


HBITMAP m_hbm;
BITMAP m_bmpInfo;
...

< 비트맵 로드 - bitmap load >


m_hbm = (HBITMAP) LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(123), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
GetObject(m_hbm, sizeof(m_bmpInfo), &m_bmpInfo);



< 알파블랜딩 - alpha blending >

void drawBitmapAlpha(RECT *rectTarget, IDirectDrawSurface7 *lpSurface)
{
HDC hdc;
if (lpSurface->GetDC(&hdc) != DD_OK || m_hbm == NULL)
return;

/* calculate bitmap size */
int targetWidth = rectTarget->right-rectTarget->left;
int targetHeight = rectTarget->bottom-rectTarget->top;
int cx = rectTarget->left+targetWidth/2;
int cy = rectTarget->top+targetHeight/2;

int bmpWidth = m_bmpInfo.bmWidth;
int bmpHeight = m_bmpInfo.bmHeight;

POINT pt;
pt.x = cx-bmpWidth/2;
pt.y = cy-bmpHeight/2;

HDC hMemDC, hAlphaDC;
BOOL ret;

hMemDC = CreateCompatibleDC(hdc);    // create bitmap dc
hAlphaDC = CreateCompatibleDC(hdc);    // create alpha dc

HBITMAP hbmAlpha = CreateCompatibleBitmap(hdc, bmpWidth, bmpHeight);

SelectObject(hMemDC, m_hbm);
SelectObject(hAlphaDC, hbmAlpha);
 
        // copy src image to alpha dc
ret = BitBlt(hAlphaDC, 0, 0, bmpWidth, bmpHeight, hdc, pt.x, pt.y, SRCCOPY);
if (!ret) {
TRACE("[%s] BitBlt failed, err:%d\n", __FUNCTION__, GetLastError());
}

        // copy bitmap image to alpha dc with transparent
ret = TransparentBlt(hAlphaDC, 0, 0, bmpWidth, bmpHeight, hMemDC, 0, 0, m_bmpInfo.bmWidth, m_bmpInfo.bmHeight, RGB(0, 0, 0));
if (!ret) {
TRACE("[%s] TransparentBlt failed, err:%d\n", __FUNCTION__, GetLastError());
}

BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 100;
bf.AlphaFormat = 0;

        // alpha blend alpha dc to dest dc
ret = AlphaBlend(hdc, pt.x, pt.y, bmpWidth, bmpHeight, hAlphaDC, 0, 0, bmpWidth, bmpHeight, bf);
if (!ret) {
TRACE("[%s] AlphaBlend failed, err:%d\n", __FUNCTION__, GetLastError());
}

DeleteObject(hbmAlpha);
DeleteDC(hAlphaDC);
DeleteDC(hMemDC);

lpSurface->ReleaseDC(hdc);
}