2014-06-12 20 views
2

내 WPF 응용 프로그램에는 비트 맵의 ​​일부를 표시하기 위해 특별히 작성한 사용자 지정 컨트롤이 있습니다. 비트 맵은 내 회사의 백엔드 Windows 서비스에서 내 프로그램으로 전송 된 객체에 포함되어 있으며 byte 배열입니다. 데이터에 포함되는 것은 직사각형이며 관심있는 이미지의 부분을 표시해야합니다.클립 영역이 작동하지 않습니다.

또한 사용자가 컨트롤을 클릭하면 해당 영역의 세 가지 다른 "배율"사이를 전환합니다. 이 설정을 "줌 상태"라고합니다. 기본적으로 한 설정에서 표시되는 사각형의 너비는 데이터와 함께 전송되는 사각형으로 지정된 영역보다 60 픽셀 큽니다. 두 번째 설정에서 사각형의 너비는 25 % 증가하고 세 번째 설정에서는 50 % 증가합니다. 높이는 항상 비트 맵 부분의 종횡비가 컨트롤과 동일하게 유지되도록 계산됩니다.

지금까지 나는 위에서 설명한 사각형 계산에 의해 지정된 영역으로 잘린 데이터에서 새로운 비트 맵을 생성 해 왔습니다. 그러나 우리가받은 데이터의 양과 비트 맵의 ​​크기를 고려할 때 이것은 엄청난 양의 메모리를 사용하고 있습니다. 나는 메모리 소비를 줄이는 방법을 찾아야한다.

자르기에 대한 검색을 WPF &에서 찾았습니다. 이미지를 자르면 this MSDN article이 나옵니다. 이것은 이미 이상하게 보였습니다. 저는 이미 직사각형을 계산 중입니다. 메모리가 적게 사용됩니다. 따라서 오늘 아침에 코드를 수정하여 원래 이미지 &을 Int32Rect에서 CroppedBitmap으로 만드는 대신 RetangleGeometry 구조체를 만들고이 사각형에 컨트롤의 Clip 속성을 설정합니다. 결과는, 그러나, 나는 아무것도 전혀 보지 않고있다.

RectangleGeometry을 만드는 코드를 주석 처리했으며 그 시점에서 컨트롤의 전체 이미지를 보았으므로이 문제는 사각형을 계산하는 코드 어딘가에 있음을 알고 있습니다. 코드의 계산이 옳다는 것을 알고 있지만, RectangleGeometry으로 변환하면 뭔가 빠져 나가야합니다.

<Style TargetType="{x:Type local:ZoomableImage}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type local:ZoomableImage}"> 
       <Border BorderBrush="{TemplateBinding BorderBrush}" 
         BorderThickness="{TemplateBinding BorderThickness}" 
         ContextMenu="{TemplateBinding ContextMenu}" 
         Cursor="{TemplateBinding Cursor}" 
         Margin="{TemplateBinding Margin}" 
         Name="ImageBorder"> 
        <Image BitmapEffect="{TemplateBinding BitmapEffect}" 
          BitmapEffectInput="{TemplateBinding BitmapEffectInput}" 
          CacheMode="{TemplateBinding CacheMode}" 
          Effect="{TemplateBinding Effect}" 
          HorizontalAlignment="Stretch" 
          Name="Image" 
          Source="{TemplateBinding ImageToBeZoomed}" 
          Stretch="{TemplateBinding Stretch}" 
          VerticalAlignment="Stretch" /> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

을 그리고 여기에 클립 사각형을 계산하는 코드이다 :

private void CreateClipRectangle() { 
    Rect srcRect = new Rect(PlateRectangle.X, PlateRectangle.Y, PlateRectangle.Width, PlateRectangle.Height); 

    // We want to show some pixels outside the plate's rectangle, so add 60 to the PlateRectangle's Width. 
    srcRect.Width += 60.0; 

    // Adjust the Width property for the ZoomState, which increases the height & width of the rectangle around the license plate 
    if (ZoomState == ZoomStates.ZoomPlus25) { 
     srcRect.Width = srcRect.Width * 1.25; 
    } else if (ZoomState == ZoomStates.ZoomPlus50) { 
     srcRect.Width = srcRect.Width * 1.50; 
    } 

    // Make sure that srcRect.Width is not bigger than the ImageToBeZoomed's PixelWidth! 
    if (srcRect.Width > ImageToBeZoomed.PixelWidth) srcRect.Width = ImageToBeZoomed.PixelWidth; 

    // We need to keep the aspect ratio of the source rectangle the same as the Image's. 
    // Compute srcRect.Height so the srcRect will have the correct aspect ratio, but don't let 
    // the rectangle's height get bigger than the original image's height! 
    srcRect.Height = Math.Min(ImageToBeZoomed.PixelHeight, Math.Round(srcRect.Width * ImageBorder.ActualHeight/ImageBorder.ActualWidth)); 

    // Adjust srcRect.X & srcRect.Y to center the source image in the output image 
    srcRect.X = srcRect.X - (srcRect.Width - PlateRectangle.Width )/2.0; 
    srcRect.Y = srcRect.Y - (srcRect.Height - PlateRectangle.Height)/2.0; 

    // Adjust srcRect to keep the cropped region from going off the image's edges. 
    if (srcRect.X < 0) srcRect.X = 0.0; 
    if (srcRect.Y < 0) srcRect.Y = 0.0; 
    if ((srcRect.X + srcRect.Width ) > ImageToBeZoomed.PixelWidth ) srcRect.X = ImageToBeZoomed.PixelWidth - srcRect.Width; 
    if ((srcRect.Y + srcRect.Height) > ImageToBeZoomed.PixelHeight) srcRect.Y = ImageToBeZoomed.PixelHeight - srcRect.Height; 

    // Create a new RectangleGeometry object that we will use to clip the ImageToBeZoomed and put it into the Clip property. 
    ImageControl.Clip = new RectangleGeometry(srcRect, 0.0, 0.0); 
} 

ImageToBeZoomed 유형 BitmapSource의 인 DependencyProperty입니다

다음은 사용자 지정 컨트롤이 사용하는 템플릿입니다. PlateRectangle 속성

public static BitmapImage BitmapFromBytes(byte[] imageBytes) { 
     BitmapImage image = null; 
     if (imageBytes != null) { 
      image = new BitmapImage(); 
      try { 
       using (MemoryStream memoryStream = new MemoryStream(imageBytes)) { 
        image.BeginInit(); 
        image.CacheOption = BitmapCacheOption.OnLoad; 
        image.StreamSource = memoryStream; 
        image.EndInit(); 

        // Freeze the BitmapImage. This helps plug memory leaks & saves memory. 
        image.Freeze(); 
       } 
      } catch (Exception ex) { 
       // . . . 
      } 
     } 
     return image; 
    } 

값은 ints 그리고 그들이 픽셀 같습니다 byte 어레이는이 코드를 사용하여 BitmapImage로 변환된다. 문제가 픽셀에서 장치 독립적 인 단위로 변환해야 할 필요가있을 수 있습니까?

이 함께 연주에서 편집 한

, 나는 내가 Y는 0 또는 음의 값 이외의 다른 RectangleGeometry 구조체의 Rect 재산의 좌표를 설정하면 내가 어떤 이미지를 볼 것으로 나타났습니다. 이것은 나에게 의미가 없습니다. 대부분의 경우 관심 영역은 이미지 중간에 있습니다 & 아니 상단 또는 하단 가장자리 근처. 누구에게 이것이 왜 그렇게 대단한 아이디어가 있습니까?

편집 2

암 i를 WPF Clip 기능에 문제가있는 하나?

+0

예를 들어 픽셀 버퍼의 복사본을 만드는 것이 간단합니까? [이 답변] (http://stackoverflow.com/a/23810646/1136211)에 나와 있습니다. – Clemens

+0

그게 정확히 내가하고 있었던 일이고 비효율적입니다. 프로그램의 메모리 사용을 줄이려고합니다. –

+0

샘플 이미지 데이터로 코드 샘플을 게시 할 수 있습니까? 나는 그래픽에 몇 가지 특급을 가지고 있는데, 당신을 위해 뭔가를 가질 수 있습니다. – pushpraj

답변

1

같은 것을보십시오. 거기에는 몇 가지 다른 스케일링 문제가있는 것으로 보입니다. 이와

내가 당신 CreateClipRectangle 방법을 대체 (변경된 것을 위해 인라인 코멘트보기/추가) :

private void CreateClipRectangle() 
    { 
     Rect srcRect = new Rect(PlateRectangle.X, PlateRectangle.Y, PlateRectangle.Width, PlateRectangle.Height); 

     // We want to show some pixels outside the plate's rectangle, so add 60 to the PlateRectangle's Width. 
     srcRect.Width += 60.0; 

     // Adjust the Width property for the ZoomState, which increases the height & width of the rectangle around the license plate 
     if (ZoomState == ZoomStates.ZoomPlus25) 
     { 
      srcRect.Width = srcRect.Width * 1.25; 
     } 
     else if (ZoomState == ZoomStates.ZoomPlus50) 
     { 
      srcRect.Width = srcRect.Width * 1.50; 
     } 

     // Make sure that srcRect.Width is not bigger than the ImageToBeZoomed's PixelWidth! 
     if (srcRect.Width > ImageToBeZoomed.PixelWidth) srcRect.Width = ImageToBeZoomed.PixelWidth; 

     // We need to keep the aspect ratio of the source rectangle the same as the Image's. 
     // Compute srcRect.Height so the srcRect will have the correct aspect ratio, but don't let 
     // the rectangle's height get bigger than the original image's height! 
     double aspectRatio = ((double)ImageToBeZoomed.PixelHeight/ImageToBeZoomed.PixelWidth); // <-- ADDED 
     srcRect.Height = Math.Min(ImageToBeZoomed.PixelHeight, Math.Round(srcRect.Width * aspectRatio)); // <-- CHANGED 

     // Adjust srcRect.X & srcRect.Y to center the source image in the output image 
     srcRect.X = srcRect.X - srcRect.Width/2.0; // <-- CHANGED 
     srcRect.Y = srcRect.Y - srcRect.Height/2.0; // <-- CHANGED 

     // Adjust srcRect to keep the cropped region from going off the image's edges. 
     if (srcRect.X < 0) srcRect.X = 0.0; 
     if (srcRect.Y < 0) srcRect.Y = 0.0; 
     if ((srcRect.X + srcRect.Width) > ImageToBeZoomed.PixelWidth) srcRect.X = ImageToBeZoomed.PixelWidth - srcRect.Width; 
     if ((srcRect.Y + srcRect.Height) > ImageToBeZoomed.PixelHeight) srcRect.Y = ImageToBeZoomed.PixelHeight - srcRect.Height; 

     double scaleX = (ImageControl.ActualWidth/ImageToBeZoomed.PixelWidth); // <-- ADDED 
     double scaleY = (ImageControl.ActualHeight/ImageToBeZoomed.PixelHeight); // <-- ADDED 

     srcRect.X *= scaleX; // <-- ADDED 
     srcRect.Y *= scaleY; // <-- ADDED 
     srcRect.Width *= scaleX; // <-- ADDED 
     srcRect.Height *= scaleY; // <-- ADDED 

     // Create a new RectangleGeometry object that we will use to clip the ImageToBeZoomed and put it into the Clip property. 
     ImageControl.Clip = new RectangleGeometry(srcRect, 0.0, 0.0); 
    } 

내 ImageControl XAML은

했다
<Image Name="ImageControl" Stretch="Uniform" /> 

경계에 더 문제가있을 수 있습니다 점검하고, 나는 확실하지 않은 국경을 제거했지만 희망적으로 이것은 당신을 시작할 것입니다.

+0

감사합니다. 이것은 의미가 있습니다. 나는 그것을 시도하고 당신에게 알려 줄 것이다. –

+0

이것은 작동하지 않습니다. PlateRectangle은 이미지의 아주 작은 부분 일 뿐이고 전체 이미지보다 작은 부분에는 픽셀을 표시하려고합니다. . 그 영역을 표시하기 위해 'CroppedBitmap'을 사용하는 것이 유일한 방법입니다. 내가 생각하는 것은 화면의 영역이 전체 이미지보다 작기 때문에 표시 될 이미지의 부분이 컨트롤에 표시 될 영역에 포함되어 있지 않다는 것입니다. –

+0

작은 이미지 나 더 큰 영역을 사용하여 임시로 사용해 보셨습니까? 문제가 발생했는지 확인하기 위해 부품을 격리 된 프로젝트로 옮길 수 있습니다. 네가 원한다면 나에게 도움이되는 프로젝트를 제공 할 수있다. – Mills

0

HorizontalAlignment = "Stretch" 및 VerticalAlignment = "Stretch"를 사용하여 표시된 이미지의 좌표계를 벗어나는 문제 일 수 있습니다.

는 그것은 Rect 당신이 RectangleGeometry을 위해 사용하고 크기가 조절되지 않는 것 같습니다

<Image Source="...." HorizontalAlignment="Left" VerticalAlignment="Top" Stretch="None"/> 
관련 문제