Windows with C++
Windows Imaging Component 소개
Kenny Kerr

목차
Microsoft® WIC(Windows® Imaging Component)는 이미지를 인코딩, 디코딩 및 조작하기 위한 확장 가능한 프레임워크입니다.원래는 Windows Vista®와 WPF(Windows Presentation Foundation)용으로 설계되었지만 현재 WIC는 Windows Vista 및 Microsoft .NET Framework 3.0 이상 버전에 기본 제공되는 것은 물론 Windows XP 및 Windows Server® 2003에서 네이티브 응용 프로그램에 사용할 수 있도록 다운로드로도 제공됩니다.
WIC는 WPF에 기능을 더하는 여러 가지 강력한 네이티브 프레임워크 중 하나이며 System.Windows.Media.Imaging 네임스페이스의 구현에서 사용됩니다.WIC는 또한 COM 인터페이스 집합을 통해 간단하면서도 강력한 API를 공개하므로 C++로 개발된 네이티브 응용 프로그램에도 이상적입니다.
WIC는 확장 가능한 이미징 코덱 집합을 사용하여 다양한 이미지 형식을 지원합니다.각 코덱은 다른 이미지 형식을 지원하며 일반적으로 인코더와 디코더를 모두 제공합니다.WIC는 Windows BMP뿐 아니라 PNG, JPEG, GIF, TIFF, HDP(HD Photo), ICO 등 거의 모든 주요 이미지 형식을 포함하는 기본 제공 코덱 집합을 제공합니다.
처음 접하는 형식으로는 HDP가 유일할 것입니다.HDP는 개발 당시에는 Windows Media Photo라고 불렸으며 기존 형식의 몇 가지 한계를 극복하고 고품질 이미지의 성능을 개선하기 위해 Windows Vista와 함께 개발되었습니다.HDP에 대한 자세한 내용을 보려면
microsoft.com/whdc/xps/wmphoto.mspx에서 사양을 확인하십시오.다행스럽게도 WIC는 이 새로운 이미지 형식을 훌륭하게 지원하므로 응용 프로그램에서는 이 형식을 사용하기 위한 세부 사항을 알 필요가 없습니다.
이달에는 WIC를 사용하여 다른 이미지 형식을 인코드 및 디코드하는 방법과 이에 관련된 몇 가지 내용을 살펴보겠습니다.다음 칼럼에서는 몇 가지 더 고급 기능에 대해 살펴보고 WIC를 확장하여 직접 이미징 코덱을 만드는 방법을 알아볼 계획입니다.
시작하기
WIC API는 COM 인터페이스, 함수, 구문, 오류 코드, 그리고 다양한 코덱, 컨테이너 및 형식을 식별하기 위한 GUID로 구성됩니다.필요한 모든 선언은 Windows SDK의 일부로 제공되는 wincodec.h 및 wincodecsdk.h 헤더 파일에 포함되어 있으며 Visual Studio® 2008에서 제공됩니다. 이 밖에도 필요한 다양한 정의를 제공하는 WindowsCodecs.lib 라이브러리를 연결해야 합니다.이를 위해서 프로젝트의 미리 컴파일된 헤더에 다음 항목을 추가할 수 있습니다.
#include <wincodec.h>
#include <wincodecsdk.h>
#pragma comment(lib, "WindowsCodecs.lib")
WIC API는 주로 COM 인터페이스로 구성되므로 필자는 인터페이스 포인터를 만들고 관리하기 위해 ATL(액티브 템플릿 라이브러리) CComPtr 클래스를 사용합니다.여러분도 필자와 같이 하려면 CComPtr 템플릿 클래스를 정의하는 atlbase.h 헤더 파일도 포함해야 합니다.
WIC API는 COM 라이브러리도 사용하므로 API를 사용하는 모든 스레드에서는 CoInitializeEx 함수를 호출해야 합니다.
마지막으로, WIC API는 오류를 나타내기 위해 HRESULT를 사용합니다.이 기사의 샘플에서는 HR 매크로를 사용하여 메서드에서 검사가 필요한 HRESULT를 반환하는 지점을 명확하게 확인합니다.이 부분은 예외를 발생시키거나 직접 HRESULT를 반환하는 등의 여러분이 작성한 오류 처리 전략으로 대체할 수 있습니다.
이미지 디코딩
디코더는 IWICBitmapDecoder 인터페이스를 통해 나타냅니다.WIC는 디코더 개체를 만드는 몇 가지 방법을 제공하지만 최소한 특정 디코더의 CLSID를 사용해도 인스턴스를 만들 수 있습니다.다음 예에서는 TIFF 이미지용 디코더를 만듭니다.
CComPtr<IWICBitmapDecoder> decoder;
HR(decoder.CoCreateInstance(CLSID_WICTiffDecoder));
그림 1에는 WIC에 포함된 코덱을 나열하고 다양한 디코더를 만드는 데 사용할 수 있는 CLSID가 나열되어 있습니다.디코더가 생성되면 디코더가 인식할 수 있는 형식의 픽셀 및 선택적인 메타데이터를 포함하는 스트림으로 이러한 디코더를 초기화해야 합니다.

Figure 1 기본 제공 WIC 코덱을 위한 CLSID
| 형식 |
디코더 |
인코더 |
| BMP |
CLSID_WICBmpDecoder |
CLSID_WICBmpEncoder |
| PNG |
CLSID_WICPngDecoder |
CLSID_WICPngEncoder |
| ICO |
CLSID_WICIcoDecoder |
사용할 수 없음 |
| JPEG |
CLSID_WICJpegDecoder |
CLSID_WICJpegEncoder |
| GIF |
CLSID_WICGifDecoder |
CLSID_WICGifEncoder |
| TIFF |
CLSID_WICTiffDecoder |
CLSID_WICTiffEncoder |
| HDP |
CLSID_WICWmpDecoder |
CLSID_WICWmpEncoder |
CComPtr<IStream> stream;
// Create stream object here...
HR(decoder->Initialize(
stream,
WICDecodeMetadataCacheOnDemand));
스트림에 대해서는 이 기사 뒷부분에서 설명하겠지만, IStream은 2007년 4월 MSDN
® Magazine(
msdn.microsoft.com/msdnmag/issues/07/04/Xml)에서 필자가 다루었던 XmlLite 파서를 포함하여 여러 API에서 사용되는 전통적인 COM 스트림 인터페이스입니다.
Initialize 메서드의 두 번째 매개 변수는 디코더가 스트림에서 이미지 정보를 읽는 방법을 지정합니다.WICDecodeMetadataCacheOnDemand는 디코더가 스트림에서 필요한 이미지 정보만 읽도록 지정합니다.이것은 이미지 형식이 여러 프레임을 포함하거나 지원하는 경우에 유용합니다.다른 선택 사항인 WICDecodeMetadataCacheOnLoad를 사용하면 디코더가 모든 이미지 정보를 즉시 캐시합니다.그러면 디코더에 대한 모든 이후의 요청은 메모리를 통해 직접 수행됩니다.이러한 방식은 조금 뒤에 설명하려는 관리 코드에서는 몇 가지 의미가 있습니다.
디코더를 초기화한 다음에는 자유롭게 디코더에서 정보를 쿼리할 수 있습니다.디코더에 요구하는 가장 일반적인 정보는 이미지를 구성하는 프레임 집합일 것입니다.프레임은 픽셀을 포함하는 실제 비트맵입니다.이미지 형식을 프레임을 위한 컨테이너라고 생각할 수 있습니다.설명했듯이 일부 이미지 형식은 여러 프레임을 지원합니다.
GetFrameCount 함수는 이미지에 있는 프레임의 수를 확인하는 데 사용됩니다.
UINT frameCount = 0;
HR(decoder->GetFrameCount(&frameCount));
프레임의 수를 얻은 다음에는 GetFrame 메서드를 사용하여 개별 프레임을 검색할 수 있습니다.
for (UINT index = 0; index < frameCount; ++index)
{
CComPtr<IWICBitmapFrameDecode> frame;
HR(decoder->GetFrame(index, &frame));
}
GetFrame에서 반환하는 IWICBitmapFrameDecode 인터페이스는 읽기 전용 비트맵을 나타내는 IWICBitmapSource 인터페이스에서 파생됩니다.IWICBitmapFrameDecode는 메타데이터 및 색 프로필과 같이 프레임과 연관된 정보를 제공합니다.IWICBitmapSource는 비트맵의 크기 및 해상도, 픽셀 형식 및 해당 색 테이블과 같은 다른 선택적인 특성을 제공합니다.IWICBitmapSource는 또한 비트맵에서 실제로 픽셀을 읽는 데 사용되는 CopyPixels 메서드를 제공합니다.
GetSize 메서드를 사용하여 프레임의 크기(픽셀)를 얻을 수 있습니다.
UINT width = 0;
UINT height = 0;
HR(frame->GetSize(&width, &height));
그리고 GetResolution 메서드를 사용하여 DPI(인치당 도트 수) 단위로 프레임의 해상도를 얻을 수 있습니다.
double dpiX = 0;
double dpiY = 0;
HR(frame->GetResolution(&dpiX, &dpiY));
해상도는 픽셀 자체에는 영향을 주지 않지만 WPF에서 사용되는 것과 같은 논리 좌표계에서 이미지를 표시할 때 영향을 줍니다.
중요한 마지막 특성은 프레임의 픽셀 형식입니다.픽셀 형식은 메모리 내의 픽셀 레이아웃을 기술하며 색 범위와 지원하는 색 공간을 나타냅니다.GetPixelFormat 메서드는 픽셀 형식을 반환합니다.
GUID pixelFormat = { 0 };
HR(frame->GetPixelFormat(&pixelFormat));
픽셀 형식은 GUID로 정의되며 이 GUID의 이름은 메모리 레이아웃을 알아보기 쉽게 나타냅니다.예를 들어 GUID_WICPixelFormat24bppRGB 형식은 각 픽셀이 24비트(3바이트)를 사용하며 색 채널당 1바이트 저장소를 사용한다는 것을 나타냅니다.또한 빨강(R), 초록(R), 파랑(B) 문자는 덜 중요한 것에서 가장 중요한 것까지 바이트의 순서를 나타냅니다.예를 들어 GUID_WICPixelFormat32bppBGRA 형식은 각 픽셀이 색 채널당 1바이트와 알파 채널 1바이트를 포함하여 32비트(4바이트) 저장소를 사용한다는 것을 지정합니다.이 경우에 채널은 덜 중요한 파랑(B) 채널에서 가장 중요한 알파(A) 순서로 배치됩니다.
실제 픽셀은 CopyPixels 메서드를 사용하여 검색할 수 있습니다.
HRESULT CopyPixels(
const WICRect* rect,
UINT stride,
UINT bufferSize,
BYTE* buffer);
rect 매개 변수는 비트맵 내에서 복사할 사각형을 지정합니다.이 매개 변수를 0으로 설정하면 전체 비트맵을 복사합니다.stride에 대해서는 조금 뒤에 이야기하겠습니다.buffer와 bufferSize 매개 변수는 픽셀을 기록할 위치 및 해당 위치에 필요한 공간을 나타냅니다.
stride는 비트맵의 측면 중에서 다소 이해하기 어려울 수 있습니다.stride는 스캐닝선 간의 바이트 수입니다.일반적으로 말해 비트맵의 픽셀을 구성하는 비트는 행을 기준으로 저장됩니다.단일 행은 비트맵 픽셀의 한 행을 저장할 만큼 길이가 되어야 합니다.stride는 바이트 단위로 계산한 행의 길이를 가장 가까운 DWORD(4바이트)로 반올림한 것입니다.이러한 방법을 사용하면 32bpp(픽셀당 비트 수) 미만의 비트맵을 적은 메모리로 구현할 수 있으며 향상된 성능을 제공할 수 있습니다.다음과 같은 함수를 사용하여 지정한 비트맵의 stride를 계산할 수 있습니다.
UINT GetStride(
const UINT width, // image width in pixels
const UINT bitCount) { // bits per pixel
ASSERT(0 == bitCount % 8);
const UINT byteCount = bitCount / 8;
const UINT stride = (width * byteCount + 3) & ~3;
ASSERT(0 == stride % sizeof(DWORD));
return stride;
}
앞서와 같은 함수를 정의한 다음에는 프레임이 32bpp 비트맵을 가정하는 경우 다음과 같이 CopyPixels 메서드를 호출할 수 있습니다.
const UINT stride = GetStride(width, 32);
CAtlArray<BYTE> buffer;
VERIFY(buffer.SetCount(stride * height));
HR(frame->CopyPixels(
0, // entire bitmap
stride,
buffer.GetCount(),
&buffer[0]));
이 예에서는 버퍼를 할당하기 위해 ATL CAtlArray 컬렉션 클래스를 사용했지만 원하는 어떤 저장소라도 사용할 수 있습니다.큰 비트맵을 더 효과적으로 처리하려면 CopyPixels를 여러 번 호출하여 비트맵의 다른 부분을 읽을 수 있습니다.
이미지 인코딩
인코더는 IWICBitmapEncoder 인터페이스를 통해 표현됩니다.디코더와 마찬가지로 WIC는 인코더 개체를 만드는 몇 가지 방법을 제공하지만 최소한 특정 인코더의 CLSID를 사용하여 이를 만들 수 있습니다.이 코드를 예로 들면 PNG 이미지용 인코더를 만듭니다.
CComPtr<IWICBitmapEncoder> encoder;
HR(encoder.CoCreateInstance(CLSID_WICPngEncoder));
그림 1에는 WIC에 포함된 다양한 인코더를 만드는 데 사용할 수 있는 CLSID가 나열되어 있습니다.인코더를 만든 다음에는 인코드된 픽셀과 선택적인 메타데이터를 최종적으로 수신할 스트림으로 인코더를 초기화해야 합니다.
CComPtr<IStream> stream;
// Create stream object here...
HR(encoder->Initialize(
stream,
WICBitmapEncoderNoCache));
현재 Initialize 메서드의 두 번째 매개 변수에 지원되는 유일한 플래그는 WICBitmapEncoderNoCache입니다.
인코더가 초기화되면 프레임 추가를 시작할 수 있습니다.CreateNewFrame 메서드를 사용하면 구성하고 픽셀을 기록할 수 있는 새로운 프레임을 만들 수 있습니다.
CComPtr<IWICBitmapFrameEncode> frame;
CComPtr<IPropertyBag2> properties;
HR(encoder->CreateNewFrame(
&frame,
&properties));
CreateNewFrame은 새로운 프레임을 나타내는 IWICBitmapFrameEncode 인터페이스와 함께 IPropertyBag2 인터페이스를 반환합니다.후자는 선택적이며 JPEG의 이미지 품질이나 TIFF의 압축 알고리즘과 같은 인코더별 속성을 지정하는 데 사용됩니다.예를 들어 다음은 JPEG 이미지의 이미지 품질을 설정하는 방법을 보여 줍니다.
PROPBAG2 name = { 0 };
name.dwType = PROPBAG2_TYPE_DATA;
name.vt = VT_R4;
name.pstrName = L"ImageQuality";
CComVariant value(0.75F);
HR(properties->Write(
1, // property count
&name,
&value));
이미지 품질의 값은 가장 낮은 품질을 나타내는 0.0에서 가장 높은 품질을 나타내는 1.0 사이여야 합니다.
인코더 속성을 설정한 다음에는 프레임을 구성하고 기록하기 전에 Initialize 메서드를 호출해야 합니다.
HR(frame->Initialize(properties));
다음 단계는 픽셀을 기록하기 전에 새로운 프레임의 크기와 픽셀 형식을 설정하는 것입니다.
HR(frame->SetSize(width, height));
GUID pixelFormat = GUID_WICPixelFormat24bppBGR;
HR(frame->SetPixelFormat(&pixelFormat));
ASSERT(GUID_WICPixelFormat24bppBGR == pixelFormat);
SetPixelFormat 매개 변수는 [입력, 출력] 매개 변수입니다.입력은 원하는 픽셀 형식을 지정하며,출력은 지원되는 가장 비슷한 픽셀 형식을 포함합니다.형식이 런타임에 설정되거나 가령 다른 비트멥의 픽셀 형식에 따라 설정되는 경우가 아니라면 이것은 문제가 되지 않습니다.
프레임에 픽셀을 기록하는 작업은 다음과 같은 WritePixels 메서드를 사용하여 수행됩니다.
HRESULT WritePixels(
UINT lineCount,
UINT stride,
UINT bufferSize,
BYTE* buffer);
lineCount 매개 변수는 기록되는 픽셀 줄 수를 지정합니다.이것은 WritePixels를 여러 번 호출하여 전체 프레임을 기록할 수 있음을 의미합니다.stride 매개 변수는 버퍼의 픽셀이 행을 구성하는 방법을 지정합니다.stride를 계산하는 방법은 이전 섹션에서 설명했습니다.
WritePixels를 한 번 이상 호출하여 전체 프레임을 기록한 다음에는 프레임의 Commit 메서드를 호출하여 프레임이 준비되었음을 인코더에 알려야 합니다.그리고 이미지를 구성하는 모든 프레임을 커밋한 다음에는 인코더의 Commit 메서드를 호출하여 이미지를 저장할 준비가 되었음을 인코더에 알려야 합니다.
지금까지 이미지 인코딩 및 디코딩에 대한 기본 사항을 다루었습니다.진행하기 전에 간단한 예를 살펴보겠습니다.그림 2에서는 아이콘을 구성하는 개별 비트맵을 읽고 이를 다중 프레임 TIFF 이미지로 복사하는 CopyIconToTiff 함수를 보여 줍니다.

Figure 2 CopyIconToTiff
HRESULT CopyIconToTiff(
IStream* sourceStream,
IStream* targetStream) {
// Prepare the ICO decoder
CComPtr<IWICBitmapDecoder> decoder;
HR(decoder.CoCreateInstance(CLSID_WICIcoDecoder));
HR(decoder->Initialize(
sourceStream,
WICDecodeMetadataCacheOnDemand));
// Prepare the TIFF encoder
CComPtr<IWICBitmapEncoder> encoder;
HR(encoder.CoCreateInstance(CLSID_WICTiffEncoder));
HR(encoder->Initialize(
targetStream,
WICBitmapEncoderNoCache));
UINT frameCount = 0;
HR(decoder->GetFrameCount(&frameCount));
for (UINT index = 0; index < frameCount; ++index) {
// Get the source frame info
CComPtr<IWICBitmapFrameDecode> sourceFrame;
HR(decoder->GetFrame(index, &sourceFrame));
UINT width = 0;
UINT height = 0;
HR(sourceFrame->GetSize(&width, &height));
GUID pixelFormat = { 0 };
HR(sourceFrame->GetPixelFormat(&pixelFormat));
// Prepare the target frame
CComPtr<IWICBitmapFrameEncode> targetFrame;
HR(encoder->CreateNewFrame(
&targetFrame,
0)); // no properties
HR(targetFrame->Initialize(0)); // no properties
HR(targetFrame->SetSize(width, height));
HR(targetFrame->SetPixelFormat(&pixelFormat));
// Copy the pixels and commit frame
HR(targetFrame->WriteSource(sourceFrame, 0));
HR(targetFrame->Commit());
}
// Commit image to stream
HR(encoder->Commit());
return S OK;
}
이 예에서는 WritePixels 메서드에 대한 대안을 활용하여 작업을 더욱 간소화해 보겠습니다.원본 프레임에서 픽셀을 복사한 다음 이를 대상 프레임으로 기록하는 대신 지정한 IWICBitmapSource 인터페이스에서 직접 픽셀을 읽는 WriteSource 메서드를 사용할 것입니다.IWICBitmapFrameDecode 인터페이스는 IWICBitmapSource에서 파생되므로 정교한 해결책을 제공됩니다.
WIC 이미징 팩터리
WIC는 다양한 WIC 관련 개체를 만들기 위한 이미징 팩터리를 제공합니다.이 이미징 팩터리는 IWICImagingFactory 인터페이스를 통해 공개되며 다음과 같이 만들 수 있습니다.
CComPtr<IWICImagingFactory> factory;
HR(factory.CoCreateInstance(CLSID_WICImagingFactory));
구현을 나타내는 CLSID를 지정하고 특정 이미지 형식을 위한 디코더를 만드는 방법은 이미 살펴보았습니다.그러나 특정 구현을 지정할 필요가 없거나 이미지의 형식을 하드코드할 필요가 없다면 더욱 유용할 것입니다.
다행스럽게도 WIC는 해결책을 제공합니다.WIC는 디코더를 만들기 전에 지정된 스트림에서 이미지 형식을 식별할 수 있는 패턴을 검색할 수 있습니다.가장 일치하는 항목이 발견되면 해당하는 디코더를 만들고 동일한 스트림으로 이를 초기화할 수 있습니다.이 기능은 CreateDecoderFromStream 메서드에서 제공됩니다.
CComPtr<IWICBitmapDecoder> decoder;
HR(factory->CreateDecoderFromStream(
stream,
0, // vendor
WICDecodeMetadataCacheOnDemand,
&decoder));
두 번째 매개 변수는 디코더의 공급업체를 지정합니다.이 매개 변수는 선택적이지만 특정 공급업체의 코덱을 선호하는 경우에 유용합니다.이 정보는 힌트로만 사용되며 특정 공급업체의 적합한 디코더가 설치되어 있지 않은 경우에는 공급업체에 관계없이 디코더가 선택됩니다.
IWICImagingFactory는 또한 각각 파일에 대한 경로와 파일 핸들을 지정하면 동일한 기능을 수행하는 CreateDecoderFromFilename 및 CreateDecoderFromFileHandle 메서드를 제공합니다.또는 CLSID나 스트림을 지정하지 않고 대신 이미지 형식을 지정하여 디코더를 만들 수 있습니다.CreateDecoder 메서드가 이러한 작업을 수행합니다.
CComPtr<IWICBitmapDecoder> decoder;
HR(factory->CreateDecoder(
GUID_ContainerFormatIco,
0, // vendor
&decoder));
HR(decoder->Initialize(
stream,
WICDecodeMetadataCacheOnDemand));
비슷하게 다음과 같이 CreateEncoder 메서드를 사용하면 특정 이미지 형식용 인코더를 해당 구현 등에 대해 관계없이 만들 수 있습니다.
CComPtr<IWICBitmapEncoder> encoder;
HR(factory->CreateEncoder(
GUID_ContainerFormatBmp,
0, // vendor
&encoder));
HR(encoder->Initialize(
stream,
WICBitmapEncoderNoCache));
그림 3에서는 컨테이너 형식이라고도 하는 구현 독립적 이미지 형식을 식별하는 GUID를 나열합니다.

Figure 3 컨테이너 형식 GUID
| 형식 |
GUID |
| BMP |
GUID_ContainerFormatBmp |
| PNG |
GUID_ContainerFormatBmp |
| ICO |
GUID_ContainerFormatIco |
| JPEG |
GUID_ContainerFormatJpeg |
| GIF |
GUID_ContainerFormatGif |
| TIFF |
GUID_ContainerFormatTiff |
| HDP |
GUID_ContainerFormatWmp |
스트림 작업
유효한 모든 IStream 구현을 WIC와 함께 사용할 수 있습니다.예를 들어 필자의 XmlLite 기사에서 설명한 CreateStreamOnHGlobal 또는 SHCreateStreamOnFile 함수를 사용하거나 여러분의 구현을 직접 작성할 수도 있습니다.WIC는 또한 유연한 IStream 구현을 제공합니다.
이전 섹션에서 소개한 이미징 팩터리를 사용하면 다음과 같이 초기화되지 않은 스트림 개체를 만들 수 있습니다.
CComPtr<IWICStream> stream;
HR(factory->CreateStream(&stream));
IWICStream 인터페이스는 IStream에서 상속받으며 스트림을 다른 배후 저장소와 연결하기 위한 몇 가지 메서드를 제공합니다.예를 들어 InitializeFromFilename을 사용하여 특정 파일과 연결된 스트림을 만들 수 있습니다.
HR(stream->InitializeFromFilename(
L"file path",
GENERIC_READ));
InitializeFromIStreamRegion을 사용하여 다른 스트림의 하위 집합으로 스트림을 만들거나 InitializeFromMemory를 사용하여 메모리 블록에 스트림을 만들 수 있습니다.
WPF를 통한 WIC 사용
앞서 설명했듯이 WIC는 WPF 이미징 기능이 기반을 두고 있는 프레임워크를 제공합니다.System.Windows.Media.Imaging 네임스페이스에 다양한 이미징 클래스가 정의되어 있습니다.그림 4는 비관리 코드에서 사용하는 것이 얼마나 쉬운지를 보여 주기 위해 그림 2의 CopyIconToTiff 함수를 WPF 래퍼 클래스를 사용하여 C#으로 다시 작성한 예입니다.

Figure 4 C# 및 WPF로 다시 작성한 CopyIconToTiff
static void CopyIconToTiff(Stream sourceStream,
Stream targetStream) {
IconBitmapDecoder decoder = new IconBitmapDecoder(
sourceStream,
BitmapCreateOptions.None,
BitmapCacheOption.OnDemand);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
foreach (BitmapFrame frame in decoder.Frames) {
encoder.Frames.Add(frame);
}
encoder.Save(targetStream);
}
BitmapCacheOption.OnDemand 값은 네이티브 코드에 사용되는 WICDecodeMetadataCacheOnDemand 디코더 옵션에 해당합니다.그리고 이와 비슷하게 대체 BitmapCacheOption.OnLoad 값은 WICDecodeMetadataCacheOnLoad 디코더 옵션에 해당합니다.
디코더가 이미지 정보를 메모리에 읽을 때 이러한 옵션이 어떤 영향을 주는지는 이미 설명했습니다.그러나 관리 코드에서 이러한 옵션을 사용할 때 알아두어야 할 추가적인 효과가 있습니다.BitmapCacheOption.OnDemand를 지정했을 때 어떤 일이 일어날지 고려해 보겠습니다.디코더는 기본 스트림에 대한 참조를 보관하며 비트맵 디코더 개체가 생성된 후의 어떤 시점에는 이 스크림에서 읽기를 수행할 수도 있습니다.이 작업은 스트림을 아직 사용할 수 있다고 가정하고 수행됩니다.따라서 응용 프로그램에서 스트림을 너무 일찍 닫지 않도록 주의해야 합니다.스트림의 수명을 관리하여 디코더가 사용을 마치기 전에 닫는 일이 없도록 하는 것이 중요합니다.
네이티브 코드의 경우 IStream 인터페이스는 수명이 참조 카운트에 의해 제어되는 표준 COM 인터페이스이므로 해당하지 않습니다.응용 프로그램에서 해당 참조를 모두 해제하더라도 디코더는 필요한 만큼 스트림을 사용할 수 있습니다.게다가 스트림은 모든 인터페이스 포인터가 해제되어야 닫힙니다.
다음 단계
WIC는 이미징 요구 사항의 기반으로 활용할 수 있는 놀라울 정도로 강력하고 유연한 프레임워크를 제공합니다.풍부한 코덱 집합과 간단한 API를 통해 제공되는 다양한 기능을 신속하게 활용할 수 있습니다.
다음 칼럼에서는 WIC에서 제공하는 몇 가지 고급 기능에 대해 살펴보겠습니다.사용자가 직접 코덱을 개발하는 방법을 살펴보고 패턴 일치 기능을 포함하여 등록 및 검색 프로세스에 대해 설명할 것입니다.진행하는 동안 기본 제공 코덱에 있는 제한 중 하나를 해결하는 방법도 확인할 것입니다.
Kenny에게 질문이나 의견이 있으면 다음 전자 메일 주소로 보내시기 바랍니다: mmwincpp@microsoft.com.
Kenny Kerr는 Windows용 소프트웨어를 전문적으로 개발하는 소프트웨어 개발자로,프로그래밍과 소프트웨어 설계 분야에서 왕성한 집필 및 교육 활동을 하고 있습니다.문의 사항이 있으면
weblogs.asp.net/kennykerr를 방문하시기 바랍니다.