만들고 SkiaSharp 비트맵에 그리기Creating and drawing on SkiaSharp bitmaps

샘플 다운로드 샘플 다운로드Download Sample Download the sample

살펴보았습니다 방법을 통해 응용 프로그램 웹, 응용 프로그램 리소스 및 사용자의 사진 라이브러리에서 비트맵을 로드할 수 있습니다.You've seen how an application can load bitmaps from the Web, from application resources, and from the user's photo library. 응용 프로그램 내에서 새 비트맵을 만들 수 이기도 합니다.It's also possible to create new bitmaps within your application. 가장 간단한 방법은의 생성자 중 하나를 포함 SKBitmap :The simplest approach involves one of the constructors of SKBitmap:

SKBitmap bitmap = new SKBitmap(width, height);

합니다 widthheight 매개 변수는 정수 및 비트맵의 픽셀 크기를 지정 합니다.The width and height parameters are integers and specify the pixel dimensions of the bitmap. 이 생성자는 픽셀당 4 바이트를 사용 하 여 전체 색 비트맵을 만듭니다: 빨강, 녹색, 파랑 및 알파 (불투명) 구성 요소에 대 한 1 바이트입니다.This constructor creates a full-color bitmap with four bytes per pixel: one byte each for the red, green, blue, and alpha (opacity) components.

새 비트맵을 만든 후 비트맵의 화면에 결과가 표시 해야 합니다.After you've created a new bitmap, you need to get something on the surface of the bitmap. 일반적으로 두 가지 방법 중 하나에서 이렇게합니다.You generally do this in one of two ways:

  • Standard를 사용 하 여 비트맵에 드로잉 Canvas 그리기 메서드.Draw on the bitmap using standard Canvas drawing methods.
  • 픽셀 비트에 직접 액세스 합니다.Access the pixel bits directly.

이 문서에서는 첫 번째 방법을 보여 줍니다.This article demonstrates the first approach:

샘플을 그리기Drawing Sample

두 번째 방법은 문서에 설명 되어 SkiaSharp 비트맵 픽셀에 액세스합니다.The second approach is discussed in the article Accessing SkiaSharp Bitmap Pixels.

비트맵에 그리기Drawing on the bitmap

비트맵의 화면에서 그리기 비디오 디스플레이에 그리기와 같습니다.Drawing on the surface of a bitmap is the same as drawing on a video display. 가져올 비디오 디스플레이에 그릴를 SKCanvas 에서 개체를 PaintSurface 이벤트 인수입니다.To draw on a video display, you obtain an SKCanvas object from the PaintSurface event arguments. 만든 비트맵을 그리려면를 SKCanvas 를 사용 하 여 개체를 SKCanvas 생성자:To draw on a bitmap, you create an SKCanvas object using the SKCanvas constructor:

SKCanvas canvas = new SKCanvas(bitmap);

비트맵을 토대로 작업을 완료 하는 경우의 삭제할 수 있습니다는 SKCanvas 개체입니다.When you're finished drawing on the bitmap, you can dispose of the SKCanvas object. 이러한 이유로 합니다 SKCanvas 생성자에서 일반적으로 호출 되는 using 문:For this reason, the SKCanvas constructor is generally called in a using statement:

using (SKCanvas canvas = new SKCanvas(bitmap))
{
    ··· // call drawing function
}

비트맵 표시 될 수 있습니다.The bitmap can then be displayed. 나중에 프로그램을 새로 만들 수 있습니다 SKCanvas 동일한, 비트맵 및 조금 더 그릴 개체 기반으로 합니다.At a later time, the program can create a new SKCanvas object based on that same bitmap, and draw on it some more.

Hello 비트맵 페이지에 SkiaSharpFormsDemos 응용 프로그램 작성 텍스트 "Hello, 비트맵!"The Hello Bitmap page in the SkiaSharpFormsDemos application writes the text "Hello, Bitmap!" 비트맵에 여러 번 비트맵을 표시 됩니다.on a bitmap and then displays that bitmap multiple times.

생성자는 HelloBitmapPage 만들어 시작을 SKPaint 텍스트 표시에 대 한 개체입니다.The constructor of the HelloBitmapPage begins by creating an SKPaint object for displaying text. 텍스트 문자열의 크기를 결정 하 고 해당 차원을 사용 하 여 비트맵을 만듭니다.It determines the dimensions of a text string and creates a bitmap with those dimensions. 그런 다음 만듭니다는 SKCanvas 해당 비트맵, 호출을 기반으로 하는 개체 Clear, 다음 호출 DrawText.It then creates an SKCanvas object based on that bitmap, calls Clear, and then calls DrawText. 호출 하는 것이 좋습니다는 항상 Clear 새 비트맵을 사용 하 여 새로 만든된 비트맵 임의 데이터를 포함 될 수 있습니다.It's always a good idea to call Clear with a new bitmap because a newly created bitmap might contain random data.

만들어 마지막 생성자는 SKCanvasView 비트맵을 표시 하는 개체:The constructor concludes by creating an SKCanvasView object to display the bitmap:

public partial class HelloBitmapPage : ContentPage
{
    const string TEXT = "Hello, Bitmap!";
    SKBitmap helloBitmap;

    public HelloBitmapPage()
    {
        Title = TEXT;

        // Create bitmap and draw on it
        using (SKPaint textPaint = new SKPaint { TextSize = 48 })
        {
            SKRect bounds = new SKRect();
            textPaint.MeasureText(TEXT, ref bounds);

            helloBitmap = new SKBitmap((int)bounds.Right,
                                       (int)bounds.Height);

            using (SKCanvas bitmapCanvas = new SKCanvas(helloBitmap))
            {
                bitmapCanvas.Clear();
                bitmapCanvas.DrawText(TEXT, 0, -bounds.Top, textPaint);
            }
        }

        // Create SKCanvasView to view result 
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear(SKColors.Aqua);

        for (float y = 0; y < info.Height; y += helloBitmap.Height)
            for (float x = 0; x < info.Width; x += helloBitmap.Width)
            {
                canvas.DrawBitmap(helloBitmap, x, y);
            }
    }
}

PaintSurface 처리기에 여러 번 표시의 행과 열 비트맵을 렌더링 합니다.The PaintSurface handler renders the bitmap multiple times in rows and columns of the display. 있음을 Clear 에서 메서드를 PaintSurface 처리기의 인수에 SKColors.Aqua, 화면 배경의 색입니다.Notice that the Clear method in the PaintSurface handler has an argument of SKColors.Aqua, which colors the background of the display surface:

Hello, 비트맵! Hello, Bitmap!

바다색 백그라운드 모양의 텍스트를 제외 하 고 투명 한 비트맵 임을 알 수 있습니다.The appearance of the aqua background reveals that the bitmap is transparent except for the text.

지우기 및 투명도Clearing and transparency

표시 된 Hello 비트맵 페이지 비트맵 만든 프로그램은 검은색 텍스트로 제외 하 고 투명 하 게 하는 방법을 보여 줍니다.The display of the Hello Bitmap page demonstrates that the bitmap the program created is transparent except for the black text. 이유는 바다색 색 화면을 통해 보여 줍니다.That's why the aqua color of the display surface shows through.

설명서의 Clear 의 메서드 SKCanvas 문을 사용 하 여 이러한 기능을 설명 합니다. "현재 클립 캔버스의 모든 픽셀을 대체합니다."The documentation of the Clear methods of SKCanvas describes them with the statement: "Replaces all the pixels in the canvas' current clip." "대체" 라는 단어를 사용 하 여 이러한 메서드의 중요 한 특징 표시 됩니다. 모든 그리기 메서드 SKCanvas 기존 화면에 내용을 추가 합니다.The use of the word "replaces" reveals an important characteristic of these methods: All the drawing methods of SKCanvas add something to the existing display surface. 합니다 Clear 메서드 대체 가 이미 있습니다.The Clear methods replace what's already there.

Clear 두 가지 버전에 있습니다.Clear exists in two different versions:

  • 합니다 Clear 메서드를 SKColor 매개 변수는 픽셀 색을 디스플레이 화면의 픽셀을 대체 합니다.The Clear method with an SKColor parameter replaces the pixels of the display surface with pixels of that color.

  • 합니다 Clear 메서드 매개 변수 없이 사용 하 여 픽셀을 대체 합니다 SKColors.Empty 색 (빨강, 녹색, 파랑 및 알파) 구성 요소는 0으로 설정 하는 모든 색입니다.The Clear method with no parameters replaces the pixels with the SKColors.Empty color, which is a color in which all the components (red, green, blue, and alpha) are set to zero. 이 색은 라고도 함 투명 검정입니다.This color is sometimes referred to as "transparent black."

호출 Clear 새 비트맵에 인수를 사용 하 여 완전히 투명 하 게 전체 비트맵을 초기화 합니다.Calling Clear with no arguments on a new bitmap initializes the entire bitmap to be entirely transparent. 비트맵에 이후에 그려지는 아무것도 보통은 불투명 하 게 또는 부분적으로 불투명 합니다.Anything subsequently drawn on the bitmap will usually be opaque or partially opaque.

다음은 하려는 것입니다. 에 Hello 비트맵 페이지에서 자리를 Clear 에 적용 하는 메서드는 bitmapCanvas 이 사용 하 여:Here's something to try: In the Hello Bitmap page, replace the Clear method applied to the bitmapCanvas with this one:

bitmapCanvas.Clear(new SKColor(255, 0, 0, 128));

순서는 SKColor 생성자 매개 변수는 빨간색, 녹색, 파랑 및 알파, 여기서 각 값의 범위는 0에서 255입니다.The order of the SKColor constructor parameters is red, green, blue, and alpha, where each value can range from 0 to 255. 알파 값이 0이 투명 알파 값이 255는 불투명 해지고 염두에 두어야 합니다.Keep in mind that an alpha value of 0 is transparent, while an alpha value of 255 is opaque.

(255, 0, 0, 128) 값을 50% 불투명도 사용 하 여 빨간색 픽셀로 비트맵 픽셀을 지웁니다.The value (255, 0, 0, 128) clears the bitmap pixels to red pixels with a 50% opacity. 이 비트맵 배경을 반투명 임을 의미 합니다.This means that the bitmap background is semi-transparent. 비트맵의 반투명 하 게 빨간색 배경이 회색 배경이 만들려는 화면 배경의 바다색와 결합 합니다.The semi-transparent red background of the bitmap combines with the aqua background of the display surface to create a gray background.

다음 할당에 배치 하 여 텍스트의 색을 투명 검정으로 설정 된 SKPaint 이니셜라이저:Try setting the color of the text to transparent black by putting the following assignment in the SKPaint initializer:

Color = new SKColor(0, 0, 0, 0)

이 투명 한 텍스트는는 바다색 배경의 화면 표시는 비트맵의 완전히 투명 한 영역을 만든 생각할 수 있습니다.You might think that this transparent text would create fully transparent areas of the bitmap through which you'd see the aqua background of the display surface. 하지만 다릅니다.But that is not so. 항목의 비트맵에 이미 있습니다. 위에 텍스트를 그립니다.The text is drawn on top of what's already on the bitmap. 투명 한 텍스트 표시 되지 않습니다 전혀 합니다.The transparent text will not be visible at all.

이상 Draw 메서드 적이 비트맵 보다 투명 하 게 만듭니다.No Draw method ever makes a bitmap more transparent. Clear 수행할 수 있습니다.Only Clear can do that.

비트맵 색 형식Bitmap color types

가장 간단한 SKBitmap 생성자를 사용 하면 정수 픽셀 너비 및 비트맵의 높이 지정할 수 있습니다.The simplest SKBitmap constructor allows you to specify an integer pixel width and height for the bitmap. 다른 SKBitmap 생성자는 더 복잡 합니다.Other SKBitmap constructors are more complex. 이 생성자에는 두 열거형 형식의 인수가 필요 합니다. SKColorType 하 고 SKAlphaType 합니다.These constructors require arguments of two enumeration types: SKColorType and SKAlphaType. 다른 생성자를 사용 합니다 SKImageInfo 구조는이 정보를 통합 합니다.Other constructors use the SKImageInfo structure, which consolidates this information.

SKColorType 열거형 멤버가 9입니다.The SKColorType enumeration has 9 members. 이러한 각각의이 멤버에는 비트맵 픽셀을 저장 하는 특정 방식으로 설명 합니다.Each of these members describes a particular way of storing the bitmap pixels:

  • Unknown
  • Alpha8 — 각 픽셀은 완전 불투명에서 완전 투명 알파 값을 나타내는 8 비트Alpha8 — each pixel is 8 bits, representing an alpha value from fully transparent to fully opaque
  • Rgb565 — 각 픽셀은 16 비트를 5 비트가 빨강 및 파랑 및 녹색 6Rgb565 — each pixel is 16 bits, 5 bits for red and blue, and 6 for green
  • Argb4444 — 각 픽셀은 16 비트를 4 각 알파, 빨강, 녹색 및 파랑Argb4444 — each pixel is 16 bits, 4 each for alpha, red, green, and blue
  • Rgba8888 — 각 픽셀은 각 8 빨간색, 녹색, 파랑 및 알파에 대 한 32 비트Rgba8888 — each pixel is 32 bits, 8 each for red, green, blue, and alpha
  • Bgra8888 — 각 픽셀은 각 8 파랑, 녹색, 빨강 및 알파에 대 한 32 비트Bgra8888 — each pixel is 32 bits, 8 each for blue, green, red, and alpha
  • Index8 — 각 픽셀은 8 비트 나타내고 인덱스는 SKColorTableIndex8 — each pixel is 8 bits and represents an index into an SKColorTable
  • Gray8 — 각 픽셀은 흰색으로 검정에서 회색 음영을 나타내는 8 비트Gray8 — each pixel is 8 bits representing a gray shade from black to white
  • RgbaF16 — 각 픽셀은 빨간색, 녹색, 파랑 및 알파는 16 비트 부동 소수점 형식에서 사용 하 여 64 비트RgbaF16 — each pixel is 64 bits, with red, green, blue, and alpha in a 16-bit floating-point format

각 픽셀은 32 픽셀 (4 바이트)는 두 가지 형식 이라고 컬러 형식입니다.The two formats where each pixel is 32 pixels (4 bytes) are often called full-color formats. 여러 비디오 자체 표시 되는 경우 한 번에서 다른 형식 날짜의 전체 색 수 있었습니다.Many of the other formats date from a time when video displays themselves were not capable of full color. 제한 된 색 비트맵이 디스플레이 대 한 적절 한 되었으며 비트맵 메모리에 더 적은 공간을 차지 하도록 허용 합니다.Bitmaps of limited color were adequate for these displays and allowed bitmaps to occupy less space in memory.

이러한 일 프로그래머에 게 거의 항상 전체 색 비트맵을 사용 하 여 및 다른 형식과 이름을 바꾸지 않습니다.These days, programmers almost always use full-color bitmaps and don't bother with other formats. 예외는 RgbaF16 컬러 형식에도 보다 큰 컬러 해상도 허용 하는 형식입니다.The exception is the RgbaF16 format, which allows greater color resolution than even the full-color formats. 그러나이 형식은 의료 이미지 등의 특수 한 용도로 사용 되 고 타당성을 많은 표준 컬러 디스플레이 사용 하는 경우.However, this format is used for specialized purposes, such as medical imaging, and doesn't make much sense when used with standard full-color displays.

이 문서 시리즈에서는 자체를 제한 합니다는 SKBitmap 하지 않으면 기본적으로 사용 되는 형식 색 SKColorType 멤버를 지정 합니다.This series of articles will restrict itself to the SKBitmap color formats used by default when no SKColorType member is specified. 이 기본 형식 기본 플랫폼을 기반으로 합니다.This default format is based on the underlying platform. Xamarin.Forms에서 지 원하는 플랫폼에 대 한 기본 색 유형은입니다.For the platforms supported by Xamarin.Forms, the default color type is:

  • Rgba8888 iOS 및 Android 용Rgba8888 for iOS and Android
  • Bgra8888 UWP에 대 한Bgra8888 for the UWP

유일한 차이점은 메모리에 4 바이트의 순서와이만 문제가 됩니다 픽셀 비트에 직접 액세스 하는 경우.The only difference is the order of the 4 bytes in memory, and this only becomes an issue when you directly access the pixel bits. 이 않습니다 더욱 중요 문서를 가져와야만 SkiaSharp 비트맵 픽셀에 액세스합니다.This won't become important until you get to the article Accessing SkiaSharp Bitmap Pixels.

SKAlphaType 열거형에는 네 가지 멤버:The SKAlphaType enumeration has four members:

  • Unknown
  • Opaque — 비트맵에 투명도 없이Opaque — the bitmap has no transparency
  • Premul — 색의 구성 요소는 알파 구성 요소를 곱한 미리Premul — color components are pre-multiplied by the alpha component
  • Unpremul — 색의 구성 요소는 하지 미리 곱한 알파 구성 요소Unpremul — color components are not pre-multiplied by the alpha component

순서 빨간색, 녹색, 파란색, 알파에 표시 된 바이트를 사용 하 여 50% 투명도 사용 하 여 4 바이트 빨간색 비트맵 픽셀은 다음과 같습니다.Here's a 4-byte red bitmap pixel with 50% transparency, with the bytes shown in the order red, green, blue, alpha:

0xFF 0x00 0x00 0x800xFF 0x00 0x00 0x80

반투명 하 게 픽셀을 포함 하는 비트맵을 화면에 렌더링 되 면 각 비트맵 픽셀의 색 구성 요소를 해당 픽셀의 알파 값을 곱할 수 있어야 합니다. 및 디스플레이 화면의 해당 픽셀의 색 구성 요소를 곱할 수 있어야 합니다. 알파 값을 뺀 255입니다.When a bitmap containing semi-transparent pixels is rendered on a display surface, the color components of each bitmap pixel must be multiplied by that pixel's alpha value, and the color components of the corresponding pixel of the display surface must be multiplied by 255 minus the alpha value. 다음 두 픽셀을 결합할 수 있습니다.The two pixels can then be combined. 비트맵을 렌더링할 수 더 빠르게 비트맵 픽셀에서 색 구성 요소를 이미 사전 mulitplied 알파 값을 기준으로 합니다.The bitmap can be rendered faster if the color components in the bitmap pixels have already been pre-mulitplied by the alpha value. 해당 동일한 빨간색 픽셀 같이 미리 곱한 형식으로 저장 됩니다.That same red pixel would be stored like this in a pre-multiplied format:

0x80 0x00 0x00 0x800x80 0x00 0x00 0x80

이러한 성능 향상은 이유 SkiaSharp 비트맵 기본적으로 사용 하 여 만들어진를 Premul 형식입니다.This performance improvement is why SkiaSharp bitmaps by default are created with a Premul format. 하지만이 액세스 하 고 픽셀 비트를 조작 하는 경우에 알고이 다시입니다.But again, it becomes necessary to know this only when you access and manipulate pixel bits.

에 기존 비트맵 그리기Drawing on existing bitmaps

에 그릴 새 비트맵을 만들 필요는 없습니다.It is not necessary to create a new bitmap to draw on it. 기존 비트맵 이미지에 그릴 수 있습니다.You can also draw on an existing bitmap.

Monkey 콧수염 페이지의 생성자를 사용 하 여 로드 하는 MonkeyFace.png 이미지입니다.The Monkey Moustache page uses its constructor to load the MonkeyFace.png image. 그런 다음 만듭니다는 SKCanvas 개체는 비트맵을 기반으로 하며 사용 하 여 SKPaintSKPath 는 콧수염에 그릴 개체:It then creates an SKCanvas object based on that bitmap, and uses SKPaint and SKPath objects to draw a moustache on it:

public partial class MonkeyMoustachePage : ContentPage
{
    SKBitmap monkeyBitmap;

    public MonkeyMoustachePage()
    {
        Title = "Monkey Moustache";

        monkeyBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MonkeyFace.png");

        // Create canvas based on bitmap
        using (SKCanvas canvas = new SKCanvas(monkeyBitmap))
        {
            using (SKPaint paint = new SKPaint())
            {
                paint.Style = SKPaintStyle.Stroke;
                paint.Color = SKColors.Black;
                paint.StrokeWidth = 24;
                paint.StrokeCap = SKStrokeCap.Round;

                using (SKPath path = new SKPath())
                {
                    path.MoveTo(380, 390);
                    path.CubicTo(560, 390, 560, 280, 500, 280);

                    path.MoveTo(320, 390);
                    path.CubicTo(140, 390, 140, 280, 200, 280);

                    canvas.DrawPath(path, paint);
                }
            }
        }

        // Create SKCanvasView to view result 
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(monkeyBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

만들어 마지막 생성자는 SKCanvasView 해당 PaintSurface 결과 단순히 표시 하는 처리기:The constructor concludes by creating an SKCanvasView whose PaintSurface handler simply displays the result:

조정 콧수염Monkey Moustache

복사 하 여 비트맵을 수정Copying and modifying bitmaps

메서드 SKCanvas 비트맵 포함 이미지를 그릴 사용할 수 있는 DrawBitmap합니다.The methods of SKCanvas that you can use to draw on a bitmap include DrawBitmap. 즉, 그릴 수 있는 하나의 비트맵, 일반적으로 어떤 방식으로든에서 수정 합니다.This means that you can draw one bitmap on another, usually modifying it in some way.

실제 픽셀 비트에 액세스 하는 비트맵을 수정 하는 가장 다양 한 방법, 문서에 설명 제목을 SkiaSharp 액세스 비트맵 픽셀 합니다.The most versatile way to modify a bitmap is through accessing the actual pixel bits, a subject covered in the article Accessing SkiaSharp bitmap pixels. 하지만 여러 가지 기술을 픽셀 비트에 액세스 되지 않아도 되는 비트맵을 수정할 수 있습니다.But there are many other techniques to modify bitmaps that don't require accessing the pixel bits.

포함 된 다음 비트맵을 SkiaSharpFormsDemos 응용 프로그램은 360 픽셀 너비 및 높이 480 픽셀:The following bitmap included with the SkiaSharpFormsDemos application is 360 pixels wide and 480 pixels in height:

Mountain ClimbersMountain Climbers

이 사진을 게시 하려면 왼쪽 monkey에서 권한을 받지 못했으면 가정.Suppose you haven't received permission from the monkey on the left to publish this photograph. 한 가지 해결책은 기법을 사용 하는 monkey의 얼굴을 모호 _pixelization_합니다.One solution is to obscure the monkey's face using a technique called pixelization. 기능을 수행할 수 없습니다 있도록 글꼴의 픽셀 색의 요소를 사용 하 여 대체 됩니다.The pixels of the face are replaced with blocks of color so you can't make out the features. 색의 요소는 일반적으로 이러한 블록을 해당 픽셀의 색을 구하여 원래 이미지에서 파생 됩니다.The blocks of color are usually derived from the original image by averaging the colors of the pixels corresponding to these blocks. 하지만이 평균 직접 수행할 필요가 없습니다.But you don't need to perform this averaging yourself. 이것이 자동으로 비트맵을 작은 픽셀 치수를 복사할 때.It happens automatically when you copy a bitmap into a smaller pixel dimension.

왼쪽된 monkey의 얼굴 약는 72 픽셀 사각형 영역 (112, 238) 지점에서 왼쪽 위 모퉁이 차지합니다.The left monkey's face occupies approximately a 72-pixel square area with an upper-left corner at the point (112, 238). 보겠습니다 8-8 하 여 픽셀 사각형은 각 색이 지정 된 블록의 9에서 9에 대 한 배열을 사용 하 여 해당 72 픽셀 사각형 영역을 대체 합니다.Let's replace that 72-pixel square area with a 9-by-9 array of colored blocks, each of which is 8-by-8 pixels square.

합니다 이미지 흠집 제거 이미지 페이지가 해당 비트맵에서 로드 되 고 먼저 라는 작은 9 픽셀 사각형 비트맵을 만들어 faceBitmap합니다.The Pixelize Image page loads in that bitmap and first creates a tiny 9-pixel square bitmap called faceBitmap. 이것이 바로 monkey의 얼굴을 복사 하는 것에 대 한 대상입니다.This is a destination for copying just the monkey's face. 대상 사각형은 단지 9 픽셀 사각형 이지만 소스 사각형 72 픽셀 사각형입니다.The destination rectangle is just 9-pixels square but the source rectangle is 72-pixels square. 모든 8-8 하 여 블록의 원본 픽셀 색 평균을 구하여 하나의 픽셀까지 통합 합니다.Every 8-by-8 block of source pixels is consolidated down to just one pixel by averaging the colors.

원래 비트맵을 호출 하는 동일한 크기의 새 비트맵을 복사 하려면 다음 단계는 pixelizedBitmap합니다.The next step is to copy the original bitmap into a new bitmap of the same size called pixelizedBitmap. 작은 faceBitmap 72 픽셀 사각형 대상 사각형을 사용 하 여 그 위에 후 복사 됩니다 있도록의 각 픽셀 faceBitmap 8 배 크기로 확장 됩니다.The tiny faceBitmap is then copied on top of that with a 72-pixel square destination rectangle so that each pixel of faceBitmap is expanded to 8 times its size:

public class PixelizedImagePage : ContentPage
{
    SKBitmap pixelizedBitmap;

    public PixelizedImagePage ()
    {
        Title = "Pixelize Image";

        SKBitmap originalBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MountainClimbers.jpg");

        // Create tiny bitmap for pixelized face
        SKBitmap faceBitmap = new SKBitmap(9, 9);

        // Copy subset of original bitmap to that
        using (SKCanvas canvas = new SKCanvas(faceBitmap))
        {
            canvas.Clear();
            canvas.DrawBitmap(originalBitmap,
                              new SKRect(112, 238, 184, 310),   // source
                              new SKRect(0, 0, 9, 9));          // destination

        }

        // Create full-sized bitmap for copy
        pixelizedBitmap = new SKBitmap(originalBitmap.Width, originalBitmap.Height);

        using (SKCanvas canvas = new SKCanvas(pixelizedBitmap))
        {
            canvas.Clear();

            // Draw original in full size
            canvas.DrawBitmap(originalBitmap, new SKPoint());

            // Draw tiny bitmap to cover face
            canvas.DrawBitmap(faceBitmap, 
                              new SKRect(112, 238, 184, 310));  // destination
        }

        // Create SKCanvasView to view result
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(pixelizedBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

만들어 마지막 생성자는 SKCanvasView 결과 표시:The constructor concludes by creating an SKCanvasView to display the result:

이미지를 이미지 흠집 제거Pixelize Image

비트맵 회전Rotating bitmaps

다른 일반적인 작업에는 비트맵을 회전 됩니다.Another common task is rotating bitmaps. IPhone 또는 iPad 사진 라이브러리에서 비트맵을 검색할 때 특히 유용 합니다.This is particularly useful when retrieving bitmaps from an iPhone or iPad photo library. 사진의 찍을 때 때 장치 특정 방향에 보유 된 경우가 아니면 그림이 거꾸로 또는 옆쪽 일 수입니다.Unless the device was held in a particular orientation when the photo was taken, the picture is likely to be upside-down or sideways.

첫 번째 크기가 같은 다른 비트맵을 만들기로 설정한 다음 첫 번째 두 번째 복사 하는 동안으로 180도 회전 변환이 필요 거꾸로 비트맵을 설정 합니다.Turning a bitmap upside-down requires creating another bitmap the same size as the first, and then setting a transform to rotate by 180 degrees while copying the first to the second. 이 섹션의 예의 모든 bitmap 되는 SKBitmap 회전 해야 하는 개체:In all of the examples in this section, bitmap is the SKBitmap object that you need to rotate:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

90도 회전 하면 높이 너비를 교환 하 여 원본 보다 다양 한 크기로 사용 되는 비트맵 만들기 해야 합니다.When rotating by 90 degrees, you need to create a bitmap that is a different size than the original by swapping the height and width. 예를 들어, 원래 비트맵 1200 픽셀 너비 및 800 픽셀, 회전된 비트맵 경우 1200 픽셀 너비 및 800 픽셀입니다.For example, if the original bitmap is 1200 pixels wide and 800 pixels high, the rotated bitmap is 800 pixels wide and 1200 pixels wide. 비트맵 왼쪽 위 구석에 해당 중심으로 회전 하 고 다음 뷰로 이동 되도록 변환 및 회전을 설정 합니다.Set translation and rotation so that the bitmap is rotated around its upper-left corner and then shifted into view. (에 유의 합니다 TranslateRotateDegrees 메서드가 적용 되는 방식의 반대 순서로 호출 됩니다.) 시계 방향으로 90도 회전 하는 것에 대 한 코드는 다음과 같습니다.(Keep in mind that the Translate and RotateDegrees methods are called in the opposite order of the way that they are applied.) Here's the code for rotating 90 degrees clockwise:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(bitmap.Height, 0);
    canvas.RotateDegrees(90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

그리고은 시계 반대 방향으로 90도 회전 하는 것에 대 한 유사한 기능.And here's a similar function for rotating 90 degrees counter-clockwise:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(0, bitmap.Width);
    canvas.RotateDegrees(-90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

이러한 두 메서드가 사용 됩니다 합니다 사진 퍼즐 문서에서 설명 하는 페이지 자르기 SkiaSharp 비트맵합니다.These two methods are used in the Photo Puzzle pages described in the article Cropping SkiaSharp Bitmaps.

90도 증가분에서 비트맵을 회전 시킬 수 있도록 프로그램으로 90도 회전 하는 것에 대 한 하나의 함수를 구현만 필요 합니다.A program that allows the user to rotate a bitmap in 90-degree increments needs only implement one function for rotating by 90 degrees. 사용자 한이 함수의 반복된 실행 하 여 90도의 모든 증가에 회전할 수 있습니다.The user can then rotate in any increment of 90 degrees by repeated execution of this one function.

또한 프로그램 모든 양만큼 비트맵을 회전할 수 있습니다.A program can also rotate a bitmap by any amount. 일반화 된를 180을 바꿔으로 180도 회전 하는 함수를 수정 하는 한 가지 간단한 방법은 것 angle 변수:One simple approach is to modify the function that rotates by 180 degrees by replacing 180 with a generalized angle variable:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(angle, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

그러나 일반적인 경우이 논리를 자르고 회전된 비트맵의 모퉁이.However, in the general case, this logic will crop off the corners of the rotated bitmap. 삼각 함수를 사용 하 여 해당 모퉁이 포함 하도록 회전된 비트맵의 크기를 계산 하는 것이 좋습니다.A better approach is to calculate the size of the rotated bitmap using trigonometry to include those corners.

이 trigonometry 같으며 합니다 비트맵 Rotator 페이지입니다.This trigonometry is shown in the Bitmap Rotator page. XAML 파일은는 SKCanvasViewSlider 는 까지일 수 있습니다 0에서 360도 사용 하 여는 Label 현재 값을 보여 주는:The XAML file instantiates an SKCanvasView and a Slider that can range from 0 through 360 degrees with a Label showing the current value:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapRotatorPage"
             Title="Bitmap Rotator">
    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="slider"
                Maximum="360"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference slider},
                              Path=Value,
                              StringFormat='Rotate by {0:F0}&#x00B0;'}"
               HorizontalTextAlignment="Center" />

    </StackLayout>
</ContentPage>

코드 숨김 파일 비트맵 리소스를 로드 하 고 라는 정적 읽기 전용 필드 이름으로 저장 originalBitmap합니다.The code-behind file loads a bitmap resource and saves it as a static read-only field named originalBitmap. 비트맵에 표시 합니다 PaintSurface 처리기는 rotatedBitmap, 처음 설정 된 originalBitmap:The bitmap displayed in the PaintSurface handler is rotatedBitmap, which is initially set to originalBitmap:

public partial class BitmapRotatorPage : ContentPage
{
    static readonly SKBitmap originalBitmap = 
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.Banana.jpg");

    SKBitmap rotatedBitmap = originalBitmap;

    public BitmapRotatorPage ()
    {
        InitializeComponent ();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(rotatedBitmap, info.Rect, BitmapStretch.Uniform);
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        double angle = args.NewValue;
        double radians = Math.PI * angle / 180;
        float sine = (float)Math.Abs(Math.Sin(radians));
        float cosine = (float)Math.Abs(Math.Cos(radians));
        int originalWidth = originalBitmap.Width;
        int originalHeight = originalBitmap.Height;
        int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
        int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);

        rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);

        using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
        {
            canvas.Clear(SKColors.LightPink);
            canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
            canvas.RotateDegrees((float)angle);
            canvas.Translate(-originalWidth / 2, -originalHeight / 2);
            canvas.DrawBitmap(originalBitmap, new SKPoint());
        }

        canvasView.InvalidateSurface();
    }
}

합니다 ValueChanged 처리기는 Slider 새 작업을 수행 rotatedBitmap 회전 각도 기반 합니다.The ValueChanged handler of the Slider performs the operations that create a new rotatedBitmap based on the rotation angle. 새 너비와 높이 기반한 absolute values의 사인 및 코사인 원래 너비 및 높이입니다.The new width and height are based on absolute values of sines and cosines of the original widths and heights. 회전된 된 비트맵에 원래 비트맵을 그릴 때 사용 되는 변환 각도 지정된 된 수 만큼 회전 고 회전된 된 비트맵의 가운데에는 center 번역할 원래 비트맵 center 원점으로 이동 합니다.The transforms used to draw the original bitmap on the rotated bitmap move the original bitmap center to the origin, then rotate it by the specified number of degrees, and then translate that center to the center of the rotated bitmap. (합니다 TranslateRotateDegrees 메서드 적용 되는 방식을 보다 반대 순서로 호출 됩니다.)(The Translate and RotateDegrees methods are called in the opposite order than how they are applied.)

사용 하 여는 Clear 메서드를 배경의 rotatedBitmap 연한 분홍.Notice the use of the Clear method to make the background of rotatedBitmap a light pink. 크기를 보여 주기 위해 전적으로 이것이 rotatedBitmap 디스플레이에서:This is solely to illustrate the size of rotatedBitmap on the display:

Rotator 비트맵Bitmap Rotator

회전된 된 비트맵 이하의 하지만 전체 원래 비트맵을 포함할 만큼 큰 경우The rotated bitmap is just large enough to include the entire original bitmap, but no larger.

비트맵을 대칭 이동Flipping bitmaps

일반적으로 비트맵에서 수행 하는 다른 작업 이라고 _대칭 이동_합니다.Another operation commonly performed on bitmaps is called flipping. 개념적으로 비트맵 세로 축 또는 center 비트맵의 가로 축 주위 3 차원에서 회전 합니다.Conceptually, the bitmap is rotated in three dimensions around a vertical axis or horizontal axis through the center of the bitmap. 수직 대칭 이동 미러 이미지를 만듭니다.Vertical flipping creates a mirror image.

비트맵 플리퍼 페이지에 SkiaSharpFormsDemos 응용 프로그램에서는 이러한 프로세스를 보여 줍니다.The Bitmap Flipper page in the SkiaSharpFormsDemos application demonstrates these processes. XAML 파일에는 SKCanvasView 세로 및 가로로 대칭 이동에 대 한 두 개의 단추:The XAML file contains an SKCanvasView and two buttons for flipping vertically and horizontally:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapFlipperPage"
             Title="Bitmap Flipper">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        
        <skia:SKCanvasView x:Name="canvasView"
                           Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Button Text="Flip Vertical"
                Grid.Row="1" Grid.Column="0"
                Margin="0, 10"
                Clicked="OnFlipVerticalClicked" />

        <Button Text="Flip Horizontal"
                Grid.Row="1" Grid.Column="1"
                Margin="0, 10"
                Clicked="OnFlipHorizontalClicked" />
    </Grid>
</ContentPage>

이러한 두 작업을 구현 하는 코드 숨김 파일은 Clicked 단추에 대 한 처리기:The code-behind file implements these two operations in the Clicked handlers for the buttons:

public partial class BitmapFlipperPage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    public BitmapFlipperPage()
    {
        InitializeComponent();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);
    }

    void OnFlipVerticalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(-1, 1, bitmap.Width / 2, 0);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }

    void OnFlipHorizontalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(1, -1, 0, bitmap.Height / 2);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }
}

수직 대칭 이동의 가로 배율 인수를 사용 하 여 크기 조정 변환을 통해 수행 됩니다 –1입니다.The vertical flip is accomplished by a scaling transform with a horizontal scaling factor of –1. 크기 조정 center 경우 비트맵의 세로 가운데The scaling center is the vertical center of the bitmap. 수평 대칭 이동 후에는의 세로 배율 인수를 사용 하 여 크기 조정 변환 –1입니다.The horizontal flip is a scaling transform with a vertical scaling factor of –1.

Monkey의 shirt 시 역방향된 처리에서 보듯이 대칭 이동 회전 같지는 않습니다.As you can see from the reversed lettering on the monkey's shirt, flipping is not the same as rotation. 이지만 오른쪽 UWP 스크린샷에서 보여 주듯이 둘 다가 가로 및 세로로 대칭 이동 180도 회전와 동일 합니다.But as the UWP screenshot on the right demonstrates, flipping both horizontally and vertically is the same as rotating 180 degrees:

플리퍼 비트맵Bitmap Flipper

비슷한 기술을 사용 하 여 처리할 수 있는 다른 일반적인 작업 비트맵 사각형 하위 집합을 자르기 됩니다.Another common task that can be handled using similar techniques is cropping a bitmap to a rectangular subset. 이 다음 문서에서 설명 자르기 SkiaSharp 비트맵합니다.This is described in the next article Cropping SkiaSharp Bitmaps.