비트맵 이미지 만들기, 편집 및 저장Create, edit, and save bitmap images

이 문서에서는 bitmapdecoder에서BitmapEncoder 를 사용 하 여 이미지 파일을 로드 하 고 저장 하는 방법과, \bitmap 개체를 사용 하 여 비트맵 이미지를 나타내는 방법을 설명 합니다.This article explains how to load and save image files using BitmapDecoder and BitmapEncoder and how to use the SoftwareBitmap object to represent bitmap images.

\Bitmap 클래스는 이미지 파일, WriteableBitmap 개체, Direct3D 표면 및 코드를 비롯 한 여러 소스에서 만들 수 있는 다양 한 API입니다.The SoftwareBitmap class is a versatile API that can be created from multiple sources including image files, WriteableBitmap objects, Direct3D surfaces, and code. \Bitmap 을 사용 하면 서로 다른 픽셀 형식과 알파 모드 간에 쉽게 변환할 수 있으며 픽셀 데이터에 대 한 하위 수준 액세스를 사용할 수 있습니다.SoftwareBitmap allows you to easily convert between different pixel formats and alpha modes, and allows low-level access to pixel data. 또한 다음과 같은 Windows의 여러 기능에서 사용 되는 공용 인터페이스입니다.Also, SoftwareBitmap is a common interface used by multiple features of Windows, including:

  • CapturedFrame 를 사용 하 여 카메라에서 캡처한 프레임을 \비트맵으로 가져올 수 있습니다.CapturedFrame allows you to get frames captured by the camera as a SoftwareBitmap.

  • Videoframe 을 사용 하면 videoframe의 기능 비트맵 표현을 가져올 수 있습니다.VideoFrame allows you to get a SoftwareBitmap representation of a VideoFrame.

  • FaceDetector 를 사용 하면 기능 비트맵에서 얼굴을 검색할 수 있습니다.FaceDetector allows you to detect faces in a SoftwareBitmap.

이 문서의 샘플 코드에서는 다음 네임 스페이스의 Api를 사용 합니다.The sample code in this article uses APIs from the following namespaces.

using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.Graphics.Imaging;
using Windows.UI.Xaml.Media.Imaging;

Bitmapdecoder에서를 사용 하 여 이미지 파일에서 \비트맵 만들기Create a SoftwareBitmap from an image file with BitmapDecoder

파일에서 \Bitmap 비트맵 을 만들려면 이미지 데이터를 포함 하는 StorageFile 의 인스턴스를 가져옵니다.To create a SoftwareBitmap from a file, get an instance of StorageFile containing the image data. 이 예제에서는 Fileopenpicker 를 사용 하 여 사용자가 이미지 파일을 선택할 수 있도록 합니다.This example uses a FileOpenPicker to allow the user to select an image file.

FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".jpg");
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;

var inputFile = await fileOpenPicker.PickSingleFileAsync();

if (inputFile == null)
{
    // The user cancelled the picking operation
    return;
}

StorageFile 개체의 openasync 메서드를 호출 하 여 이미지 데이터를 포함 하는 임의 액세스 스트림을 가져옵니다.Call the OpenAsync method of the StorageFile object to get a random access stream containing the image data. 정적 메서드 bitmapdecoder에서 를 호출 하 여 지정 된 스트림에 대 한 bitmapdecoder에서 클래스의 인스턴스를 가져옵니다.Call the static method BitmapDecoder.CreateAsync to get an instance of the BitmapDecoder class for the specified stream. GetSoftwareBitmapAsync 를 호출 하 여 이미지를 포함 하는 \bitmap 개체를 가져옵니다.Call GetSoftwareBitmapAsync to get a SoftwareBitmap object containing the image.

SoftwareBitmap softwareBitmap;

using (IRandomAccessStream stream = await inputFile.OpenAsync(FileAccessMode.Read))
{
    // Create the decoder from the stream
    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);

    // Get the SoftwareBitmap representation of the file
    softwareBitmap = await decoder.GetSoftwareBitmapAsync();
}

BitmapEncoder를 사용 하 여 파일에 \Bitmap 저장Save a SoftwareBitmap to a file with BitmapEncoder

파일에 저장 비트맵 을 저장 하려면 이미지가 저장 될 StorageFile 의 인스턴스를 가져옵니다.To save a SoftwareBitmap to a file, get an instance of StorageFile to which the image will be saved. 이 예제에서는 FileSavePicker 를 사용 하 여 사용자가 출력 파일을 선택할 수 있도록 합니다.This example uses a FileSavePicker to allow the user to select an output file.

FileSavePicker fileSavePicker = new FileSavePicker();
fileSavePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileSavePicker.FileTypeChoices.Add("JPEG files", new List<string>() { ".jpg" });
fileSavePicker.SuggestedFileName = "image";

var outputFile = await fileSavePicker.PickSaveFileAsync();

if (outputFile == null)
{
    // The user cancelled the picking operation
    return;
}

StorageFile 개체의 openasync 메서드를 호출 하 여 이미지가 기록 될 임의 액세스 스트림을 가져옵니다.Call the OpenAsync method of the StorageFile object to get a random access stream to which the image will be written. 정적 메서드 BitmapEncoder 를 호출 하 여 지정 된 스트림에 대 한 BitmapEncoder 클래스의 인스턴스를 가져옵니다.Call the static method BitmapEncoder.CreateAsync to get an instance of the BitmapEncoder class for the specified stream. Createasync 에 대 한 첫 번째 매개 변수는 이미지를 인코딩하는 데 사용 해야 하는 코덱을 나타내는 GUID입니다.The first parameter to CreateAsync is a GUID representing the codec that should be used to encode the image. BitmapEncoder 클래스는 인코더가 지 원하는 각 코덱 (예: JpegEncoderId)에 대 한 ID를 포함 하는 속성을 노출 합니다.BitmapEncoder class exposes a property containing the ID for each codec supported by the encoder, such as JpegEncoderId.

Setsoftwarebitmap 메서드를 사용 하 여 인코딩할 이미지를 설정 합니다.Use the SetSoftwareBitmap method to set the image that will be encoded. BitmapTransform 속성의 값을 설정 하 여 인코딩 중인 이미지에 기본 변환을 적용할 수 있습니다.You can set values of the BitmapTransform property to apply basic transforms to the image while it is being encoded. IsThumbnailGenerated 속성은 인코더가 미리 보기를 생성할지 여부를 결정 합니다.The IsThumbnailGenerated property determines whether a thumbnail is generated by the encoder. 모든 파일 형식이 미리 보기를 지 원하는 것은 아니므로이 기능을 사용 하는 경우에는 미리 보기가 지원 되지 않는 경우에 throw 되는 지원 되지 않는 작업 오류를 catch 해야 합니다.Note that not all file formats support thumbnails, so if you use this feature, you should catch the unsupported operation error that will be thrown if thumbnails are not supported.

FlushAsync 를 호출 하 여 인코더가 이미지 데이터를 지정 된 파일에 쓰도록 합니다.Call FlushAsync to cause the encoder to write the image data to the specified file.

private async void SaveSoftwareBitmapToFile(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
    using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        // Create an encoder with the desired format
        BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);

        // Set the software bitmap
        encoder.SetSoftwareBitmap(softwareBitmap);

        // Set additional encoding parameters, if needed
        encoder.BitmapTransform.ScaledWidth = 320;
        encoder.BitmapTransform.ScaledHeight = 240;
        encoder.BitmapTransform.Rotation = Windows.Graphics.Imaging.BitmapRotation.Clockwise90Degrees;
        encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
        encoder.IsThumbnailGenerated = true;

        try
        {
            await encoder.FlushAsync();
        }
        catch (Exception err)
        {
            const int WINCODEC_ERR_UNSUPPORTEDOPERATION = unchecked((int)0x88982F81);
            switch (err.HResult)
            {
                case WINCODEC_ERR_UNSUPPORTEDOPERATION: 
                    // If the encoder does not support writing a thumbnail, then try again
                    // but disable thumbnail generation.
                    encoder.IsThumbnailGenerated = false;
                    break;
                default:
                    throw;
            }
        }

        if (encoder.IsThumbnailGenerated == false)
        {
            await encoder.FlushAsync();
        }


    }
}

BitmapPropertySet 개체를 만들고 인코더 설정을 나타내는 하나 이상의 BitmapTypedValue 개체로 채워서 BitmapEncoder 를 만들 때 추가 인코딩 옵션을 지정할 수 있습니다.You can specify additional encoding options when you create the BitmapEncoder by creating a new BitmapPropertySet object and populating it with one or more BitmapTypedValue objects representing the encoder settings. 지원 되는 인코더 옵션 목록은 BitmapEncoder options 참조를 참조 하세요.For a list of supported encoder options, see BitmapEncoder options reference.

var propertySet = new Windows.Graphics.Imaging.BitmapPropertySet();
var qualityValue = new Windows.Graphics.Imaging.BitmapTypedValue(
    1.0, // Maximum quality
    Windows.Foundation.PropertyType.Single
    );

propertySet.Add("ImageQuality", qualityValue);

await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(
    Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId,
    stream,
    propertySet
);

XAML 이미지 컨트롤과 함께 사용 비트맵 사용Use SoftwareBitmap with a XAML Image control

이미지 컨트롤을 사용 하 여 xaml 페이지 내에 이미지를 표시 하려면 먼저 xaml 페이지에서 이미지 컨트롤을 정의 합니다.To display an image within a XAML page using the Image control, first define an Image control in your XAML page.

<Image x:Name="imageControl"/>

현재 이미지 컨트롤은 BGRA8 encoding 및 미리 곱하기 또는 알파 채널을 사용 하지 않는 이미지만 지원 합니다.Currently, the Image control only supports images that use BGRA8 encoding and pre-multiplied or no alpha channel. 이미지를 표시 하기 전에 테스트 하 여 올바른 형식이 있는지 확인 하 고, 그렇지 않은 경우에 는 해당 이미지 를 지원 되는 Convert 형식으로 변환 합니다.Before attempting to display an image, test to make sure it has the correct format, and if not, use the SoftwareBitmap static Convert method to convert the image to the supported format.

SoftwareBitmapSource 개체를 만듭니다.Create a new SoftwareBitmapSource object. SetBitmapAsync를 호출하고 SoftwareBitmap을 전달하여 원본 개체의 콘텐츠를 설정합니다.Set the contents of the source object by calling SetBitmapAsync, passing in a SoftwareBitmap. 그런 다음 이미지 컨트롤의 Source 속성을 새로 만든 SoftwareBitmapSource로 설정할 수 있습니다.Then you can set the Source property of the Image control to the newly created SoftwareBitmapSource.

if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 ||
    softwareBitmap.BitmapAlphaMode == BitmapAlphaMode.Straight)
{
    softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}

var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(softwareBitmap);

// Set the source of the Image control
imageControl.Source = source;

SoftwareBitmapSource 를 사용 하 여 ImageBrush에 대 한 ImageSource 로는 ImageBrush 비트맵 을 설정할 수도 있습니다.You can also use SoftwareBitmapSource to set a SoftwareBitmap as the ImageSource for an ImageBrush.

WriteableBitmap에서 \Bitmap 비트맵 만들기Create a SoftwareBitmap from a WriteableBitmap

CreateCopyFromBuffer 를 호출 하 고 WriteableBitmapPixelBuffer 속성을 제공 하 여 픽셀 데이터를 설정 하면 기존 WriteableBitmap 에서 데이터 비트맵 을 만들 수 있습니다.You can create a SoftwareBitmap from an existing WriteableBitmap by calling SoftwareBitmap.CreateCopyFromBuffer and supplying the PixelBuffer property of the WriteableBitmap to set the pixel data. 두 번째 인수를 사용 하 여 새로 만든 WriteableBitmap에 대 한 픽셀 형식을 요청할 수 있습니다.The second argument allows you to request a pixel format for the newly created WriteableBitmap. WriteableBitmapPixelWidthPixelHeight 속성을 사용 하 여 새 이미지의 크기를 지정할 수 있습니다.You can use the PixelWidth and PixelHeight properties of the WriteableBitmap to specify the dimensions of the new image.

SoftwareBitmap outputBitmap = SoftwareBitmap.CreateCopyFromBuffer(
    writeableBitmap.PixelBuffer,
    BitmapPixelFormat.Bgra8,
    writeableBitmap.PixelWidth,
    writeableBitmap.PixelHeight
);

프로그래밍 방식으로 \Bitmap 비트맵 만들기 또는 편집Create or edit a SoftwareBitmap programmatically

지금까지이 항목에서는 이미지 파일 작업에 대해 설명 했습니다.So far this topic has addressed working with image files. 코드에서 프로그래밍 방식으로 새 데이터 비트맵 을 만들고 동일한 기술을 사용 하 여 기능 비트맵의 픽셀 데이터에 액세스 하 고 수정할 수도 있습니다.You can also create a new SoftwareBitmap programatically in code and use the same technique to access and modify the SoftwareBitmap's pixel data.

\Bitmap 은 COM interop를 사용 하 여 픽셀 데이터를 포함 하는 원시 버퍼를 노출 합니다.SoftwareBitmap uses COM interop to expose the raw buffer containing the pixel data.

COM interop를 사용 하려면 프로젝트에 t e m 네임 스페이스에 대 한 참조를 포함 해야 합니다.To use COM interop, you must include a reference to the System.Runtime.InteropServices namespace in your project.

using System.Runtime.InteropServices;

네임 스페이스 내에 다음 코드를 추가 하 여 IMemoryBufferByteAccess COM 인터페이스를 초기화 합니다.Initialize the IMemoryBufferByteAccess COM interface by adding the following code within your namespace.

[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

원하는 픽셀 형식 및 크기를 사용 하 여 새 고가 비트맵 을 만듭니다.Create a new SoftwareBitmap with pixel format and size you want. 또는 픽셀 데이터를 편집 하려는 기존 데이터 비트맵 을 사용 합니다.Or, use an existing SoftwareBitmap for which you want to edit the pixel data. 데이터 버퍼를 나타내는 BitmapBuffer 클래스의 인스턴스를 가져오려면 \Bitmap. lockbuffer 를 호출 합니다.Call SoftwareBitmap.LockBuffer to obtain an instance of the BitmapBuffer class representing the pixel data buffer. BitmapBufferIMemoryBufferByteAccess COM 인터페이스로 캐스팅 한 다음 IMemoryBufferByteAccess 를 호출 하 여 바이트 배열을 데이터로 채웁니다.Cast the BitmapBuffer to the IMemoryBufferByteAccess COM interface and then call IMemoryBufferByteAccess.GetBuffer to populate a byte array with data. BitmapBuffer 메서드를 사용 하 여 각 픽셀에 대 한 버퍼의 오프셋을 계산 하는 데 도움이 되는 BitmapPlaneDescription 개체를 가져옵니다.Use the BitmapBuffer.GetPlaneDescription method to get a BitmapPlaneDescription object that will help you calculate the offset into the buffer for each pixel.

softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, 100, 100, BitmapAlphaMode.Premultiplied);

using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Write))
{
    using (var reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacity;
        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacity);

        // Fill-in the BGRA plane
        BitmapPlaneDescription bufferLayout = buffer.GetPlaneDescription(0);
        for (int i = 0; i < bufferLayout.Height; i++)
        {
            for (int j = 0; j < bufferLayout.Width; j++)
            {

                byte value = (byte)((float)j / bufferLayout.Width * 255);
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] = value;
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] = value;
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] = value;
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] = (byte)255;
            }
        }
    }
}

이 메서드는 Windows 런타임 형식을 기반으로 하는 원시 버퍼에 액세스 하므로 unsafe 키워드를 사용 하 여 선언 해야 합니다.Because this method accesses the raw buffer underlying the Windows Runtime types, it must be declared using the unsafe keyword. 또한 프로젝트의 속성 페이지를 열고, 빌드 속성 페이지를 클릭 하 고, 안전 하지 않은 코드 허용 확인란을 선택 하 여 안전 하지 않은 코드를 컴파일할 수 있도록 Microsoft Visual Studio에서 프로젝트를 구성 해야 합니다.You must also configure your project in Microsoft Visual Studio to allow the compilation of unsafe code by opening the project's Properties page, clicking the Build property page, and selecting the Allow Unsafe Code checkbox.

Direct3D surface에서에는 \비트맵 만들기Create a SoftwareBitmap from a Direct3D surface

Direct3D surface에서 개체를 만들려면 프로젝트Direct3D11 네임 스페이스를 포함 해야 합니다.To create a SoftwareBitmap object from a Direct3D surface, you must include the Windows.Graphics.DirectX.Direct3D11 namespace in your project.

using Windows.Graphics.DirectX.Direct3D11;

CreateCopyFromSurfaceAsync 를 호출 하 여 화면에서 새 고가 비트맵 을 만듭니다.Call CreateCopyFromSurfaceAsync to create a new SoftwareBitmap from the surface. 이름이 나타내는 것 처럼 새 데이터 비트맵 에는 이미지 데이터의 개별 복사본이 있습니다.As the name indicates, the new SoftwareBitmap has a separate copy of the image data. 이 경우에는 Direct3D 화면에 영향을 주지 않습니다.Modifications to the SoftwareBitmap will not have any effect on the Direct3D surface.

private async void CreateSoftwareBitmapFromSurface(IDirect3DSurface surface)
{
    softwareBitmap = await SoftwareBitmap.CreateCopyFromSurfaceAsync(surface);
}

다른 픽셀 형식으로 \Bitmap 비트맵 변환Convert a SoftwareBitmap to a different pixel format

\Bitmap 클래스는 정적 메서드인 Convert를 제공 합니다 .이를 통해 기존 모델 비트맵에서 지정 하는 픽셀 형식 및 알파 모드를 사용 하는 새 모델을 쉽게 만들 수 있습니다.The SoftwareBitmap class provides the static method, Convert, that allows you to easily create a new SoftwareBitmap that uses the pixel format and alpha mode you specify from an existing SoftwareBitmap. 새로 만든 비트맵에는 이미지 데이터의 개별 복사본이 있습니다.Note that the newly created bitmap has a separate copy of the image data. 새 비트맵을 수정 해도 원본 비트맵에는 영향을 주지 않습니다.Modifications to the new bitmap will not affect the source bitmap.

SoftwareBitmap bitmapBGRA8 = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);

이미지 파일 트랜스 코딩Transcode an image file

이미지 파일을 bitmapdecoder에서 에서 BitmapEncoder로 직접 변환할 수 있습니다.You can transcode an image file directly from a BitmapDecoder to a BitmapEncoder. 트랜스 코딩 될 파일에서 IRandomAccessStream 를 만듭니다.Create a IRandomAccessStream from the file to be transcoded. 입력 스트림에서 새 bitmapdecoder에서 을 만듭니다.Create a new BitmapDecoder from the input stream. 인코더에서 쓸 새 InMemoryRandomAccessStream 을 만들고 BitmapEncoder를 호출 하 여 메모리 내 스트림과 디코더 개체를 전달 합니다.Create a new InMemoryRandomAccessStream for the encoder to write to and call BitmapEncoder.CreateForTranscodingAsync, passing in the in-memory stream and the decoder object. 트랜스 코딩 시에는 인코딩 옵션이 지원 되지 않습니다. 대신 Createasync를 사용 해야 합니다.Encode options are not supported when transcoding; instead you should use CreateAsync. 인코더에서 특별히 설정 하지 않은 입력 이미지 파일의 모든 속성은 변경 되지 않은 상태로 출력 파일에 기록 됩니다.Any properties in the input image file that you do not specifically set on the encoder, will be written to the output file unchanged. FlushAsync 를 호출 하 여 인코더가 메모리 내 스트림으로 인코딩합니다.Call FlushAsync to cause the encoder to encode to the in-memory stream. 마지막으로 파일 스트림과 메모리 내 스트림을 검색 하 여 시작 부분으로 이동 하 고 Copyasync 를 호출 하 여 메모리 내 스트림을 파일 스트림에 씁니다.Finally, seek the file stream and the in-memory stream to the beginning and call CopyAsync to write the in-memory stream out to the file stream.

private async void TranscodeImageFile(StorageFile imageFile)
{


    using (IRandomAccessStream fileStream = await imageFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);

        var memStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
        BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(memStream, decoder);

        encoder.BitmapTransform.ScaledWidth = 320;
        encoder.BitmapTransform.ScaledHeight = 240;

        await encoder.FlushAsync();

        memStream.Seek(0);
        fileStream.Seek(0);
        fileStream.Size = 0;
        await RandomAccessStream.CopyAsync(memStream, fileStream);

        memStream.Dispose();
    }
}