이 더 직접 Lock
를 사용하여 쓰기 가능한 비트 맵의 데이터에 액세스 할 수 있습니다, 귀하의 질문에 대답하기 위해,하지만은 다음과 같습니다 간단한 예를 들어 , 당신이 좀 걸릴 수 있습니다 일반적으로 그림의 내용을 기반으로 도면을 작성하지 않는 한 필요하지 않습니다. 더 일반적으로 새로운 버퍼를 생성하여 비트 맵으로 만들 수 있습니다.
즉, 픽셀 조작을 사용하지 않고도 혁신적인 드로잉을 수행 할 수있는 WPF에는 많은 확장 점이 있습니다. 대부분의 컨트롤에서 기존의 WPF primitives (테두리, 선, 사각형, 이미지 등)이 충분합니다. 많은 요소를 사용하는 것에 대해 염려하지 말고 사용하기에 저렴합니다. 복잡한 컨트롤의 경우 DrawingContext을 사용하여 D3D 프리미티브를 그릴 수 있습니다. 이미지 효과의 경우 을 Effect
class으로 사용하거나 내장 효과 (흐림 및 그림자)를 사용할 수 있습니다.
그러나 상황에 직접적인 픽셀 액세스가 필요한 경우 pixel format을 선택하고 글을 시작하십시오. BGRA32는 이해하기 쉽기 때문에 BGRA32를 제안하고 아마도 가장 공통적 인 논의 일 것입니다.
BGRA32는 픽셀 데이터가 이미지의 파랑, 녹색, 빨강 및 알파 채널을이 순서로 나타내는 4 바이트로 메모리에 저장된다는 의미입니다. 이는 각 픽셀이 32 비트 정수로 저장하기 위해 4 바이트 경계로 끝나기 때문에 편리합니다. 32 비트 정수를 처리 할 때는 대부분의 플랫폼에서 순서가 바뀌므로 염두에 두십시오 (복수 플랫폼을 지원해야하는 경우 런타임시 올바른 바이트 순서를 확인하려면 BitConverter.IsLittleEndian
을 확인하십시오. x86 및 x86_64는 모두 리틀 엔디안입니다)
이미지 데이터는 하나의 행을 이미지의 너비로 구성하는 하나의 너비 인 stride
인 가로 스트립에 저장됩니다. stride
너비는 항상 선택한 형식의 픽셀 당 바이트 수를 곱한 이미지의 픽셀 너비보다 크거나 같습니다. 특정 상황에서는 스트라이드가 특정 아키텍처에만 해당하는 width * bytesPerPixel
보다 길어질 수 있으므로 폭을 곱하는 대신 스트라이드 너비를 사용하여 행의 시작을 계산해야합니다. 우리가 4 바이트의 와이드 픽셀 포맷을 사용하고 있기 때문에, 우리의 보폭은 width * 4
이 되겠지만, 그것에 의존해서는 안됩니다.언급 한 바와 같이
, 난 당신이 기존 이미지에 액세스하는 경우 WritableBitmap가 사용하는 것이 좋습니다 것입니다 경우에만, 그 아래의 예 그래서 :
전/후 :
![image run through below algorithm](https://i.stack.imgur.com/wIYvs.png)
// must be compiled with /UNSAFE
// get an image to draw on and convert it to our chosen format
BitmapSource srcImage = JpegBitmapDecoder.Create(File.Open("img13.jpg", FileMode.Open),
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
if (srcImage.Format != PixelFormats.Bgra32)
srcImage = new FormatConvertedBitmap(srcImage, PixelFormats.Bgra32, null, 0);
// get a writable bitmap of that image
var wbitmap = new WriteableBitmap(srcImage);
int width = wbitmap.PixelWidth;
int height = wbitmap.PixelHeight;
int stride = wbitmap.BackBufferStride;
int bytesPerPixel = (wbitmap.Format.BitsPerPixel + 7)/8;
wbitmap.Lock();
byte* pImgData = (byte*)wbitmap.BackBuffer;
// set alpha to transparent for any pixel with red < 0x88 and invert others
int cRowStart = 0;
int cColStart = 0;
for (int row = 0; row < height; row++)
{
cColStart = cRowStart;
for (int col = 0; col < width; col++)
{
byte* bPixel = pImgData + cColStart;
UInt32* iPixel = (UInt32*)bPixel;
if (bPixel[2 /* bgRa */] < 0x44)
{
// set to 50% transparent
bPixel[3 /* bgrA */] = 0x7f;
}
else
{
// invert but maintain alpha
*iPixel = *iPixel^0x00ffffff;
}
cColStart += bytesPerPixel;
}
cRowStart += stride;
}
wbitmap.Unlock();
// if you are going across threads, you will need to additionally freeze the source
wbitmap.Freeze();
가
그러나 기존 이미지를 수정하지 않는 경우에는 실제로 필요하지 않습니다. 예를 들어, 모든 안전 코드를 사용하여 바둑판 패턴을 그릴 수 있습니다 :
출력 :
![checkerboard pattern at 25 percent zoom](https://i.stack.imgur.com/wXkLe.png)
// draw rectangles
int width = 640, height = 480, bytesperpixel = 4;
int stride = width * bytesperpixel;
byte[] imgdata = new byte[width * height * bytesperpixel];
int rectDim = 40;
UInt32 darkcolorPixel = 0xffaaaaaa;
UInt32 lightColorPixel = 0xffeeeeee;
UInt32[] intPixelData = new UInt32[width * height];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
intPixelData[row * width + col] = ((col/rectDim) % 2) != ((row/rectDim) % 2) ?
lightColorPixel : darkcolorPixel;
}
}
Buffer.BlockCopy(intPixelData, 0, imgdata, 0, imgdata.Length);
// compose the BitmapImage
var bsCheckerboard = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, imgdata, stride);
그리고 당신은 바이트로 작성하는 경우 당신은 정말도, Int32 형식 중간 필요하지 않습니다 배열 직접.
출력 :
이
// draw using byte array
int width = 640, height = 480, bytesperpixel = 4;
int stride = width * bytesperpixel;
byte[] imgdata = new byte[width * height * bytesperpixel];
// draw a gradient from red to green from top to bottom (R00 -> ff; Gff -> 00)
// draw a gradient of alpha from left to right
// Blue constant at 00
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
// BGRA
imgdata[row * stride + col * 4 + 0] = 0;
imgdata[row * stride + col * 4 + 1] = Convert.ToByte((1 - (col/(float)width)) * 0xff);
imgdata[row * stride + col * 4 + 2] = Convert.ToByte((col/(float)width) * 0xff);
imgdata[row * stride + col * 4 + 3] = Convert.ToByte((row/(float)height) * 0xff);
}
}
var gradient = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, imgdata, stride);
편집 : 분명히, 당신은 이미지 편집기의 어떤 종류를 만들기 위해 WPF를 사용하려고합니다. 난 여전히 셰이프 및 소스 비트 맵에 대한 WPF 기본 형식을 사용하고 RenderTransform
의 비트 맵 효과 Effect
과 같은 WPF 모델 내에서 모든 것을 유지하고 크기 변환, 회전을 구현합니다. 그러나 그것이 당신을 위해 작동하지 않는 경우, 우리는 다른 많은 옵션이 있습니다. 당신은 아래 WritableBitmap
와 함께 사용할 PixelFormat
을 선택 한 RenderTargetBitmap
에 렌더링 WPF 프리미티브를 사용할 수
:
Canvas cvRoot = new Canvas();
// position primitives on canvas
var rtb = new RenderTargetBitmap(width, height, dpix, dpiy, PixelFormats.Bgra32);
var wb = new WritableBitmap(rtb);
그런 다음 비트 맵으로로 렌더링 GDI 스타일의 명령을 실행하는 WPF DrawingVisual
을 사용할 수 있습니다 RenderTargetBitmap
페이지의 샘플에서 시연했습니다.
당신은 InteropBitmap
가 Bitmap.GetHBitmap
방법에서 검색된 HBITMAP
에서 System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap
를 사용하여 생성하여 GDI를 사용할 수 있습니다. 그래도 HBITMAP
을 유출하지 않도록하십시오.
답변 주셔서 감사합니다. 귀하의 코드를 테스트 해보려고했지만 Pbgra32 형식을 사용해야합니다. 전환 코드가 작동하도록 노력했지만 광산은 BitmapSource가 아닌 WriteableBitmap으로 저장되므로 얻을 수 없습니다. 그것은 작동합니다. WriteableBitmap을 Bgra32로 변환 할 수 있습니까? – Oztaco
@ leaf68,'WritableBitmap'은'BitmapSource'에서 상속 받고'FormatConvertedBitmap' 클래스를 사용하여 pixelformats간에 변환 할 수 있지만 다시는 필요하지 않습니다. 당신이하려는 일에 대해 설명해 주시겠습니까? PBGRA는 일반적으로 편집이나 생성이 아닌 이미지 저장에만 사용됩니다. – Mitch
셰이프를 그리기 위해 WriteableBitmapEx 라이브러리를 사용하고 있으며 Pbgra만을 입력으로 허용한다는 사실을 알았습니다. 흠, Pbgra의 원시 데이터를 Bgra와 비슷하게 편집 할 수 있습니까? 알파 값이 색상에 미리 혼합 되었기 때문입니까? – Oztaco