Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

WPF - Line 요소를 Canvas에 위치시켰을 때 흐림(blur) 현상

WPF 관련 업무에서 멀어지다 보니, 이젠 아래의 글을 어떻게 작성했는지 조차 기억이 가물가물합니다. ^^;

WPF/Silverlight의 그래픽 단위와 Anti-aliasing 처리를 이해하자
; https://www.sysnet.pe.kr/2/0/1281

그런데, 아래와 같은 질문이 들어왔군요. (그래서 이전에 쓴 제 글을 다시 읽어봤습니다. ^^)

안녕하세요. SnapsToDevicePixels 질문입니다.
; https://www.sysnet.pe.kr/3/0/4775

어쨌든, 위의 질문을 간단하게 재현해 보면 이렇습니다. 다음과 같은 XAML을 만들고,

<Window x:Class="SnapToPixelTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" x:Name="xWindow" Height="350" Width="300">
    <Grid x:Name="xGrid">
        <Canvas x:Name="xCanvas">
            <Line Canvas.Top="10" Canvas.Left="10" X2="50" Y2="0" Stroke="Black" StrokeThickness="1" SnapsToDevicePixels="True" />
        </Canvas>
    </Grid>
</Window>

실행해 보면, 선이 뚜렷하게 안 보이고 약간 굵으면서 흐린 부분이 보입니다. 확대해 보면 2pixel 굵기로 위는 흐리고 아래는 뚜렷합니다.

[그림 1: SnapsToDevicePixels == True인 경우, 상단 1pixel 흐리고 하단 1pixel 뚜렷함]
wpf_snap_1.png

"WPF/Silverlight의 그래픽 단위와 Anti-aliasing 처리를 이해하자" 글의 내용대로라면, 위의 코드는 소수점을 포함하는 그 어떠한 연산도 없기 때문에 흐리게 나와야 할 이유가 없습니다. 게다가 런타임 시에 위치를 보정해 주는 SnapsToDevicePixels 옵션까지 True로 설정했는데 흐리게 나오다니요????!!!!! (사실 이건 이유를 잘 모르겠습니다.)

물론, SnapsToDevicePixels 옵션을 제거하면 상황이 더 안 좋게 나옵니다.

<Canvas x:Name="xCanvas">
    <Line Canvas.Top="10" Canvas.Left="10" X2="50" Y2="0" Stroke="Black" StrokeThickness="1" />
</Canvas>

[그림 2: SnapsToDevicePixels == False인 경우, 2pixel 모두 흐림]
wpf_snap_2.png

그렇긴 한데, 저 결과는 "WPF/Silverlight의 그래픽 단위와 Anti-aliasing 처리를 이해하자" 글에 나온 대로여서 이상할 것이 없습니다.

따라서, 위의 상황에서 1pixel짜리 선을 뚜렷하게 표현하려면 정수(integer) 단위의 물리 Pixel을 맞춰주면 됩니다. 이를 위해 0.5를 보정하면,

<Canvas x:Name="xCanvas">
    <Line Canvas.Top="9.5" Canvas.Left="10" X2="50" Y2="0" Stroke="Black" StrokeThickness="1" />
</Canvas>

이렇게 잘 나옵니다.

[그림 3: 뚜렷한 1pixel]
wpf_snap_3.png

재미있는 것은 위의 코드에 SnapsToDevicePixels 옵션을 주면,

<Canvas x:Name="xCanvas">
    <Line Canvas.Top="9.5" Canvas.Left="10" X2="50" Y2="0" Stroke="Black" StrokeThickness="1" 
            SnapsToDevicePixels="True" />
</Canvas>

다시 흐려집니다.

[그림 4: 흐린 1pixel]
wpf_snap_4.png

그런데, 더 재미있는 것이 있습니다. Canvas.Top, Canvas.Left를 지정하지 않고 Line 자체의 X1, Y1과 함께 SnapsToDevicePixels 옵션을 이렇게 지정해도 뚜렷한 1pixel을 그릴 수 있습니다.

<Canvas x:Name="xCanvas">
    <Line X1="10" Y1="10" X2="50" Y2="10" Stroke="Black" StrokeThickness="1" SnapsToDevicePixels="True"  />
</Canvas>

하지만 여기다 UseLayoutRounding="True"를 주면 오히려,

<Canvas x:Name="xCanvas" UseLayoutRounding="True" >
    <Line X1="10" Y1="10" X2="50" Y2="10" Stroke="Black" StrokeThickness="1" SnapsToDevicePixels="True" />
</Canvas>

"[그림 2: SnapsToDevicePixels == False인 경우, 2pixel 모두 흐림]"처럼 2pixel 흐린 선이 나옵니다. 일단, UseLayoutRounding이 사용되면 이제 다시 소수점 처리로 들어가야 합니다. (아래에서는 SnapsToDevicePixels 유무에 상관없습니다.)

<Canvas x:Name="xCanvas" UseLayoutRounding="True">
    <Line X1="10" Y1="10.5" X2="50" Y2="10.5" Stroke="Black" StrokeThickness="1" />
</Canvas>

위의 결과는 다시 "[그림 3: 뚜렷한 1pixel]"처럼 나옵니다. 그나저나... 저런 규칙 아닌 규칙들을 다 알아야 한다는 것이 좀 우습군요.

(첨부파일은 질문 글의 소스 코드를 1pixel 뚜렷한 걸로 바꾼 것입니다.)




여기서 힘든 부분이 있습니다. UseLayoutRounding은 하위 요소로 상속되기 때문에 Canvas를 사용하는 상단 중에 하나라도 설정되어 있으면 저렇게 소수점 처리를 해야 합니다. 휴~~~ 좀 좋은 방법 없을까요? 저도 이젠 피곤해지는군요. ^^;




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/13/2016]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 31  32  33  34  [35]  36  37  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12755정성태8/5/20217179개발 환경 구성: 593. MSTest - 단위 테스트에 static/instance 유형의 private 멤버 접근 방법파일 다운로드1
12754정성태8/5/20218099오류 유형: 750. manage.py - Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
12753정성태8/5/20218368오류 유형: 749. PyCharm - Error: Django is not importable in this environment
12752정성태8/4/20216445개발 환경 구성: 592. JetBrains의 IDE(예를 들어, PyCharm)에서 Visual Studio 키보드 매핑 적용
12751정성태8/4/20219531개발 환경 구성: 591. Windows 10 WSL2 환경에서 docker-compose 빌드하는 방법
12750정성태8/3/20216320디버깅 기술: 181. windbg - 콜 스택의 "Call Site" 오프셋 값이 가리키는 위치
12749정성태8/2/20215745개발 환경 구성: 590. Visual Studio 2017부터 단위 테스트에 DataRow 특성 지원
12748정성태8/2/20216337개발 환경 구성: 589. Azure Active Directory - tenant의 관리자(admin) 계정 로그인 방법
12747정성태8/1/20216945오류 유형: 748. 오류 기록 - MICROSOFT GRAPH – HOW TO IMPLEMENT IAUTHENTICATIONPROVIDER파일 다운로드1
12746정성태7/31/20218961개발 환경 구성: 588. 네트워크 장비 환경을 시뮬레이션하는 Packet Tracer 프로그램 소개
12745정성태7/31/20216802개발 환경 구성: 587. Azure Active Directory - tenant의 관리자 계정 로그인 방법
12744정성태7/30/20217422개발 환경 구성: 586. Azure Active Directory에 연결된 App 목록을 확인하는 방법?
12743정성태7/30/20218098.NET Framework: 1083. Azure Active Directory - 외부 Token Cache 저장소를 사용하는 방법파일 다운로드1
12742정성태7/30/20217375개발 환경 구성: 585. Azure AD 인증을 위한 사용자 인증 유형
12741정성태7/29/20218581.NET Framework: 1082. Azure Active Directory - Microsoft Graph API 호출 방법파일 다운로드1
12740정성태7/29/20217246오류 유형: 747. SharePoint - InvalidOperationException 0x80131509
12739정성태7/28/20217214오류 유형: 746. Azure Active Directory - IDW10106: The 'ClientId' option must be provided.
12738정성태7/28/20217801오류 유형: 745. Azure Active Directory - Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).
12737정성태7/28/20216756오류 유형: 744. Azure Active Directory - The resource principal named api://...[client_id]... was not found in the tenant
12736정성태7/28/20217271오류 유형: 743. Active Azure Directory에서 "API permissions"의 권한 설정이 "Not granted for ..."로 나오는 문제
12735정성태7/27/20217787.NET Framework: 1081. C# - Azure AD 인증을 지원하는 데스크톱 애플리케이션 예제(Windows Forms) [2]파일 다운로드1
12734정성태7/26/202123779스크립트: 20. 특정 단어로 시작하거나/끝나는 문자열을 포함/제외하는 정규 표현식 - Look-around
12733정성태7/23/202111121.NET Framework: 1081. Self-Contained/SingleFile 유형의 .NET Core/5+ 실행 파일을 임베딩한다면? [1]파일 다운로드2
12732정성태7/23/20216405오류 유형: 742. SharePoint - The super user account utilized by the cache is not configured.
12731정성태7/23/20217540개발 환경 구성: 584. Add Internal URLs 화면에서 "Save" 버튼이 비활성화 된 경우
12730정성태7/23/20219060개발 환경 구성: 583. Visual Studio Code - Go 코드에서 입력을 받는 경우
... 31  32  33  34  [35]  36  37  38  39  40  41  42  43  44  45  ...