![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Вывести графику на Canvas с Anti-Aliasing'ом [D7, WinXP]
hypnotize © (2007-03-10 11:45:00)Здратвуйте Мастера! Не знает ли кто-нибудь из присутвующих как выводить на Канву графику (с Трансперентом) со сглаживанием (Анти-Алиасингом)? То есть нету ли какого либо аналога процедуры
Canvas.Draw(x,y,graphic);только, со сглаживанием)
DVM (2008-01-24 11:22:00) [1]
> Sapersky (23.01.08 18:37) [49]
В [47] и [48] копирование картинки с экрана в ваших функциях не производилось, а в моей производилось. Если в вашу фунцию с FastDIB добавить создание экземпляра TFastDIB на лету и копирование в него картинки с экрана, а потом вывод обратно на экран, то скорость будет примерно такая же как и в моей функции. Так и там и там используется одна и таже BitBlt и главные тормоза в ней. Пикселей перебирается не так уж много в алгоритме, поэтому сам алгоритм на скорость влияет мало, как и поиск строки в битмапе и точки в строке и вычисление ее цвета.
> У меня получилось - с учётом копирования быстрее DD, без
> копирования - "неDD".
ну собственно у меня так же.
Но вариант с DD мне все равно не подходит. Если только для удовлетворения любопытства.
Sapersky (2008-01-23 18:37:00) [2]Результаты буферных функций - с копированием буфера на экран или без? И если оно делается, то как - по моему методу, с заранее созданным буфером на всю форму (картинка с экрана в него не копируется), или как в [41], с созданием битмапа "на лету" и копированием туда-обратно?
Так, ради интереса. У меня получилось - с учётом копирования быстрее DD, без копирования - "неDD". Оно и понятно, функция одна и та же, отличается метод (и вообще наличие) передачи данных из/в видеопамять.
Т.е. DD быстрее для единичной отрисовки - драйвер, видимо, передаёт туда-обратно относительно небольшие блоки данных, не весь экран, хотя даётся команда на блокировку всей "первички". Но если рисовать нужно много, лучше использовать буферный метод - поднакопить данные в системной памяти и потом "одним движением" сбросить в видео.
DVM (2008-01-23 17:23:00) [3]Вариант через SetPixel 10625 и 33023 соответственно.
DVM (2008-01-23 17:19:00) [4]
> Sapersky (23.01.08 16:55) [46]
Спасибо посмотрел. Добавил ту свою функцию со сканлайном, что выше.
При маленьких размерах окружностей результаты таковы:
На основе FastDIB - 633
На основе DD - 1480
Моя функция со сканлайном - 1237
При рисовании на холсте 1280*1024:
На основе FastDIB - 3967
На основе DD - 3877
Моя функция со сканлайном - 9600
Т.е. DD не дает особого преимущества.
Sapersky (2008-01-23 16:55:00) [5]http://sapersky.narod.ru/files/DrawEllipse.rar
Глубина цвета рабочего стола должна быть 32 bpp.
Sapersky (2008-01-23 15:09:00) [6]А что внутри?
Там далеко не всё внутри, инициализация/финализация DDraw, например, снаружи. И полностью код трудно привести, используется много разных модулей. Если надо - позже выложу пример.
И, конечно, передаётся в функцию совсем не DC, так что внешнего сходства с GDI (если оно имеет значение) нет. Поэтому, возможно, проще всё-таки буферный битмап на всю форму (скорость сопоставимая).
DVM (2008-01-23 14:05:00) [7]
> При большом кол-ве отрисовок, особенно с наложениями, буферный
> битмап будет просто необходим - чтобы не мерцало.
Хотелось бы чтобы в функцию передавалось именно DC, как и в WinApi, но тогда буферный битмап придется создавать внутри функции, что мне не очень нравится.
DVM (2008-01-23 13:56:00) [8]
> Sapersky (23.01.08 13:50) [42]
> DrawWuCircleDDraw(fd, 400, 300, R, 0);
А что внутри?
> DrawCircle1 вообще не работает, GetScanLine возвращает nil.
У меня работает, но там код сырой, где то ошибка(и).
Sapersky (2008-01-23 13:50:00) [9]При большом кол-ве отрисовок, особенно с наложениями, буферный битмап будет просто необходим - чтобы не мерцало.
Но если так уж хочется ПРЯМО на экран - можно использовать DirectDraw с выводом на "первичку". Прямее некуда - без учёта перекрытия окон и строго в пиксельном формате рабочего стола.
Сейчас сравнил:
DrawWuCircle(400, 300, R, Canvas);
// R300 - 18 ms, R30 - 2 ms
DrawWuCircleDDraw(fd, 400, 300, R, 0);
// R300 - 1 ms, R30 - 0.15 ms
DrawCircle1 вообще не работает, GetScanLine возвращает nil.
DVM (2008-01-22 23:06:00) [10]А вот то же самое (черновой вариант) со сканлайнами
procedure DrawCircle1(DC: HDC; CX, CY, R: Integer; Color: TColor);
type
RGBTripleArray = array[0..0] of TRGBTriple;
var
oldpt, pt: tpoint;
bmi: BITMAPINFO;
PBits: pointer;
MemDC: HDC;
MemBmp: HBITMAP;
w,h: integer;
X : Integer;
Y : Integer;
T : Double;
D : Double;
J : Integer;
KX : Integer;
KY : Integer;
LastX : Integer;
res: integer;
rgn: HRGN;
function MixerColors(FromColor, ToColor: TColor; Rate: Single): TColor;
var
ra, rb: byte;
begin
RA := round(Rate * 255);
RB := 255 - RA;
result := (((Lo(FromColor) * RA + Lo(ToColor) * RB) shr 8) or
(((Lo(FromColor shr 8) * RA + Lo(ToColor shr 8) * RB) shr 8) shl 8) or
(((Lo(FromColor shr 16) * RA + Lo(ToColor shr 16) * RB) shr 8) shl 16));
end;
function BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Longint): Longint;
begin
Dec(Alignment);
Result := ((PixelsPerScanline * BitsPerPixel) + Alignment) and not Alignment;
Result := Result div 8;
end;
function GetScanLine(bmi: BITMAPINFO; Bits: pointer; Row: Integer): Pointer;
var
nRow: integer;
begin
result := nil;
if (Row < 0) or (Row >= bmi.bmiHeader.biHeight) then exit;
if bmi.bmiHeader.biHeight > 0 then
nRow := bmi.bmiHeader.biHeight - Row - 1
else
nRow := Row;
Integer(Result) := Integer(Bits) + nRow * BytesPerScanline(bmi.bmiHeader.biWidth, bmi.bmiHeader.biBitCount, 32);
end;
procedure SetPixel(bmi: BITMAPINFO; Bits: pointer; X : Integer; Y : Integer; Alpha : Double; Color: TColor);
var
Line: ^RGBTripleArray;
BkColor: TColor;
NewColor: TColor;
begin
Line := GetScanLine(bmi, Bits, Y);
if not assigned(line) then exit;
if (x<0) or (x>=bmi.bmiHeader.biWidth) then exit;
BkColor := RGB(Line[x].rgbtRed, Line[x].rgbtGreen, Line[x].rgbtBlue);
NewColor := MixerColors(COLOR, BkColor, alpha);
Line[x].rgbtRed := GetRValue(NewColor);
Line[x].rgbtGreen := GetgValue(NewColor);
Line[x].rgbtBlue := GetbValue(NewColor);
end;
begin
if (r <= 0) then exit;
W := 2 * r;
H := 2 * r;
ZeroMemory(@bmi, sizeof(bmi));
bmi.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biCompression := BI_RGB;
bmi.bmiHeader.biBitCount := 24;
bmi.bmiHeader.biPlanes := 1;
bmi.bmiHeader.biWidth := w;
bmi.bmiHeader.biHeight := h;
bmi.bmiHeader.biSizeImage := bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 3;
bmi.bmiHeader.biClrUsed :=0;
bmi.bmiHeader.biClrImportant:=0;
MemBmp := CreateDIBSection(DC, bmi, DIB_RGB_COLORS, pBits, 0, 0);
MemDC := CreateCompatibleDC(DC);
SelectObject(MemDC, MemBmp);
BitBlt(MemDC, 0, 0, w, h, DC, 0, 0, SRCCOPY);
x := R;
LastX := R;
y := 0;
T := 0;
J:=0;
while J<=3 do
begin
KX := J mod 2*2-1;
KY := J div 2 mod 2*2-1;
SetPixel(BMI, PBits, KX*x+cx, KY*y+cy, 1, color);
SetPixel(BMI, PBits,KX*y+cx, KY*x+cy, 1, color);
Inc(J);
end;
while x>y do
begin
y := y+1;
D := Ceil(Sqrt(R*R-y*y))-Sqrt(R*R-y*y);
if D<T then
begin
x := x-1;
end;
if x<y then
begin
Break;
end;
if (x=y) and (LastX=x) then
begin
Break;
end;
J:=0;
while J<=3 do
begin
KX := J mod 2*2-1;
KY := J div 2 mod 2*2-1;
SetPixel(BMI, PBits, KX * x + cx, KY * y + cy, 1-D, color);
SetPixel(BMI, PBits, KX * y + cx, KY * x + cy, 1-D, color);
if x-1>=y then
begin
SetPixel(BMI, PBits,KX*(x-1)+cx, KY*y+cy, D, color);
SetPixel(BMI, PBits,KX*y+cx, KY*(x-1)+cy, D, color);
end;
Inc(J);
end;
T := D;
LastX := X;
end;
BitBlt(DC, 0, 0, W, H, MemDC, 0, 0, SRCCOPY);
DeleteObject(MemBmp);
DeleteObject(MemDC);
end;
DVM (2008-01-22 23:05:00) [11]
function MixerColors(FromColor, ToColor: TColor; Rate: Single): TColor;
var
ra, rb: byte;
begin
RA := round(Rate * 255);
RB := 255 - RA;
result := (((Lo(FromColor) * RA + Lo(ToColor) * RB) shr 8) or
(((Lo(FromColor shr 8) * RA + Lo(ToColor shr 8) * RB) shr 8) shl 8) or
(((Lo(FromColor shr 16) * RA + Lo(ToColor shr 16) * RB) shr 8) shl 16));
end;
//------------------------------------------------------------------------------
procedure SetPixel(X : Integer; Y : Integer; Alpha : Double; ACanvas: TCanvas);
VAR
T: byte;
begin
ACanvas.Pixels[x,y] := MixerColors(ACanvas.Pen.Color, ACanvas.Pixels[x,y], Alpha);
end;
procedure DrawWuCircle(cx : Integer; cy : Integer; R : Integer; ACanvas: TCanvas);
var
X : Integer;
Y : Integer;
T : Double;
D : Double;
J : Integer;
KX : Integer;
KY : Integer;
LastX : Integer;
begin
x := R;
LastX := R;
y := 0;
T := 0;
J:=0;
while J<=3 do
begin
KX := J mod 2*2-1;
KY := J div 2 mod 2*2-1;
SetPixel(KX*x+cx, KY*y+cy, 1, ACanvas);
SetPixel(KX*y+cx, KY*x+cy, 1, ACanvas);
Inc(J);
end;
while x>y do
begin
y := y+1;
D := Ceil(Sqrt(R*R-y*y))-Sqrt(R*R-y*y);
if D<T then
begin
x := x-1;
end;
if x<y then
begin
Break;
end;
if (x=y) and (LastX=x) then
begin
Break;
end;
J:=0;
while J<=3 do
begin
KX := J mod 2*2-1;
KY := J div 2 mod 2*2-1;
SetPixel(KX*x+cx, KY*y+cy, 1-D, ACanvas);
SetPixel(KX*y+cx, KY*x+cy, 1-D, ACanvas);
if x-1>=y then
begin
SetPixel(KX*(x-1)+cx, KY*y+cy, D, ACanvas);
SetPixel(KX*y+cx, KY*(x-1)+cy, D, ACanvas);
end;
Inc(J);
end;
T := D;
LastX := X;
end;
end;
DVM (2008-01-22 23:01:00) [12]
> homm ©
Ты не забывай, что алгоритм Wu не перебирает все пикселы прямоугольника, в который вписан в эллипс. Берется сравнительно небольшое число точек. Твой пример не подходит совсем.
homm (2008-01-22 22:59:00) [13]> [28] DVM © (22.01.08 22:18)
> Я не думаю, я попробовал.
Ну вот я попробовал:
procedure TForm1.Button2Click(Sender: PObj);
var
BMP: PBitmap;
I, T, J, K: Integer;
DC: HDC;
C: PCanvas;
begin
T := GetTickCount;
DC := GetDC(0);
for i := 0 to 49 do begin
BMP := NewDIBBitmap(1280, 1024, pf32bit);
BitBlt(BMP.Canvas.Handle, 0, 0, 1280, 1024, DC, 0, 0, SRCCOPY);
FillMemory(BMP.DIBBits, 1280*1024*4, $80);
BMP.Draw(DC, 0, 0);
BMP.Free;
end;
ReleaseDC(0, DC);
InvalidateRect(0, nil, false);
ShowMessage(int2str(gettickCount-T));
T := GetTickCount;
DC := GetDC(0);
C := NewCanvas(DC);
for i := 0 to 0 do begin
for j := 0 to 1024-1 do
for k := 0 to 1280-1 do begin
C.Pixels[K,J] := $80808080;
end;
end;
ReleaseDC(0, DC);
C.Free;
InvalidateRect(0, nil, false);
ShowMessage(int2str(gettickCount-T));
end;
Вариант1: 547 мс
Вариант2: 842 мс
А теперь обрати внимание, что первый вариант выполняется в цикле 50 раз, а второй только 1 :) Разница примерно в 80 раз. Уж не знаю, как ты так мерил.
homm (2008-01-22 22:56:00) [14]> [35] DVM © (22.01.08 22:44)
> Слушай, а может рисовать вложенные эллипсы, а между ними
> пространство залить?
Наивный :)
> [36] DVM © (22.01.08 22:50)
Угу.
DVM (2008-01-22 22:50:00) [15]Хотя не, не вложатся они нормально один в другой. Чем больше сплющен эллипс, тем более различаться по толщине будет граница.
DVM (2008-01-22 22:44:00) [16]
> homm ©
Слушай, а может рисовать вложенные эллипсы, а между ними пространство залить?
DVM (2008-01-22 22:43:00) [17]
> Ты кажется говорил об алгоритме с произвольной толщиной
> линии.
Я же наоборот говорил, что кроме него все есть.
homm (2008-01-22 22:41:00) [18]> [32] DVM © (22.01.08 22:35)
> да стандартный метод Wu, основанный на вычислении площади
> трапеции.
>
> вот результат кстати
>
> http://dvmuratov.narod.ru/aa.png
До такого, как я уже сказал, я и сам дошел :) Ты кажется говорил об алгоритме с произвольной толщиной линии.
DVM (2008-01-22 22:35:00) [19]
> homm ©
да стандартный метод Wu, основанный на вычислении площади трапеции.
вот результат кстати
http://dvmuratov.narod.ru/aa.png
homm (2008-01-22 22:34:00) [20]> [30] DVM © (22.01.08 22:28)
> Но впринципе в книгах описан - сделать можно будет.
Проверь, математические ли там алгоритмы, а не то, что я описал?