>>
Site Map
>>
Forums
>>
C/C++/C#
Forum module - topics in forum:
C/C++/C# - Ngôn ngữ lập trình cục mạnh, cho các phần mềm trên máy
[GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa -
[GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
- Mình định sẻ đợi khi nào làm xong GImage rồi mới chuyển sang GDrawing! Nhưng khoảng thời gian này Z hơi bận nên chắc tạm ngưng GImage lại, mình sẻ làm một chút về GDrawing, khi nào Z có thời gian sẻ quay lại hoàn thiện GImage!
- Vì GImage chưa xong nên mình sẻ làm trên struct của mình vậy, nó chỉ khác ở BYTE và DWORD nên sau này chuyển sang GImage của Z không khó khăn lắm! Các bạn có thể Download struct của mình ở đây :
http://www.2shared.com/file/3131263/...GoldenSun.html
- Tất cả code mình post trước đây đều chạy được trên struct này! Nếu bạn nào chạy không được thì cứ hỏi mình!
- Vì mình dùng VS 9.0 nên nếu bạn nào dùng VC++ 6.0 thì phải tạo lại project mới vậy! Tất cả file đều nằm trong folder G2D, khi tạo project mới , các bạn phải copy folder G2D vào trong project rồi mới, add mấy file đó vào rồi mới compile nha!
Hy vọng sẻ có nhiều bạn tham gia ^_^!
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
1.SetPixel and GetPixel :
- Trong GImage cũng có hàm Draw_Pixel, mình chỉ thêm cho đầy đủ thôi!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
void GDrawing_Ind::SetPixel(int x,int y,DWORD Color)
{
if(x < 0 || x >= m_pGImage->GetWidth() ||
y < 0 || y >= m_pGImage->GetHeight()) return;
m_pGImage->GetBits()[m_pGImage->GetWidth()*y + x] = Color;
}
- Ở đây mình có thể thay đổi tí xíu, thay vì so sánh 4 lần thì sẻ chuyển về 2 lần!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
void GDrawing_Ind::SetPixel(int x,int y,DWORD Color)
{
if(UINT(x) < m_pGImage->GetWidth() &&
UINT(y) < m_pGImage->GetHeight())
m_pGImage->GetBits()[m_pGImage->GetWidth()*y + x] = Color;
}
- Nếu x < 0 thì UINT(x) sẻ ra một số rất lớn -> ImgWidth, vì vậy mình có thể dùng cách này để giảm số lần so sánh!
- Tuy nhiên, type cast sẻ làm giản tốc độ nên nhanh hơn không nhiều!
- GetPixel() cũng giống vậy:
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
DWORD GDrawing_Ind::GetPixel(int x,int y)
{
return (UINT(x) < m_pGImage->GetWidth() &&
UINT(y) < m_pGImage->GetHeight()) ?
m_pGImage->GetBits()[m_pGImage->GetWidth()*y + x] : 0;
}
- Ở đây, nếu x,y ngoài image thì sẻ trả về 0, nếu các bạn muốn chính xác hơn thì có thể truyền biến để nhận giá trị trả về vào trong hàm!
(Ex: DWORD GDrawing_Ind::GetPixel(int x,int y,DWORD &Color) )
À quên, còn phần blending cho pixel nửa chứ!
Thêm hai hàm này vào:
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
struct SColor32
{
BYTE Blue,Green,Red,Alpha;
};
SColor32 GImage_Ind::MixColors(SColor32 Color1, SColor32 Color2)
{
DWORD Cl = ((*(DWORD*)&Color1 >> 1) & 0xFF7F7F7F) + ((*(DWORD*)&Color2 >> 1) & 0xFF7F7F7F); // C = C1/2 + C2/2
return *(SColor32*)&Cl;
}
SColor32 GImage_Ind::AlphaBlend(SColor32 BkColor,SColor32 UpperColor,BYTE Transparency)
{
BkColor.Blue = ((UpperColor.Blue - BkColor.Blue)*Transparency + (BkColor.Blue <<

) >> 8;
BkColor.Green = ((UpperColor.Green - BkColor.Green)*Transparency + (BkColor.Green <<

) >> 8;
BkColor.Red = ((UpperColor.Red - BkColor.Red)*Transparency + (BkColor.Red <<

) >> 8;
return BkColor;
}
- MixColor() là AlphaBlend() với Transparency = 0.5!
- Công thức Alpha blend có dạng như sau :
+ Color = UpperColor*Transparency + BkColor*(1 - Transparency) (Transparency <= 1)
+ Hay : Color = ((UpperColor - BkColor)*Transparency + (BkColor <<

>> 8 (Transparency <= 255)
- Như vậy blending cho SetPixel sẻ chuyển thành:
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
inline void GDrawing_Ind::SetPixel(int x,int y,DWORD Color,BYTE Transparency)
{
if(UINT(x) < m_pGImage->GetWidth() && UINT(y) < m_pGImage->GetHeight())
{
SColor32* pColor = (SColor32*)&m_pGImage->GetBits()[m_pGImage->GetWidth()*y + x]; // A little faster than direct access
SColor32 ColorS = *(SColor32*)&Color;
pColor->Blue = ((ColorS.Blue - pColor->Blue)*Transparency + (pColor->Blue <<

) >> 8;
pColor->Green = ((ColorS.Green - pColor->Green)*Transparency + (pColor->Green <<

) >> 8;
pColor->Red = ((ColorS.Red - pColor->Red)*Transparency + (pColor->Red <<

) >> 8;
}
}
Hay:
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
inline void GDrawing_Ind::SetPixel(int x,int y,DWORD Color,BYTE Transparency)
{
if(UINT(x) < m_pGImage->GetWidth() && UINT(y) < m_pGImage->GetHeight())
{
SColor32* pColor = (SColor32*)&m_pGImage->GetBits()[m_pGImage->GetWidth()*y + x]; // A little faster than direct access
SColor32 ColorS = *(SColor32*)&Color;
*pColor = m_pGImage->AlphaBlend(*pColor,ColorS,Transparency);
}
}
Tạm thời chỉ có hai phần này thôi, tối về mình sẻ post tiếp phần line()!
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
2.Line :
Bây giờ là tới phần vẻ đường thẳng!
Mình sẻ dùng thuật toán bresenham để vẻ!
Theo một số sách về graphics thì như sau!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
void Line(int x1,int y1,int x2,int y2,DWORD Color)
{
int Dx,Dy,p,Const1,Const2;
int x,y;
Dx = x2 - x1;
Dy = y2 - y1;
p = (Dy << 1) - Dx;
Const1 = Dy << 1;
Const2 = (Dy - Dx) << 1;
x = x1;
y = y1;
SetPixel(x,y,Color);
for(int i = 1;i < x2;++i)
{
if(p < 0) p += Const1;
else
{
p += Const2;
++y;
}
++x;
SetPixel(x,y,Color);
}
}
Phần thuật toán bresenham các bạn có thể search trên mạng, rất rõ ràng và đầy đủ! Vì vậy mình không nói nhiều đến nó!
Nhưng theo code ở trên như vầy thì chưa tốt lắm! Vì chúng ta có thể thấy, khi p >= 0 thì mới tăng y lên mà thôi, còn lại ở mỗi bước lặp đều tăng x lên 1!
Do đó chúng ta có thể chuyển sang dùng pointer (thay vì dùng SetPixel())!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
void Line(int x1,int y1,int x2,int y2,DWORD Color)
{
int Dx,Dy,p,Const1,Const2;
int x,y;
Dx = x2 - x1;
Dy = y2 - y1;
p = (Dy << 1) - Dx;
Const1 = Dy << 1;
Const2 = (Dy - Dx) << 1;
x = x1;
y = y1;
DWORD* Ptr = m_pGImage->GetBits();
// SetPixel(x,y,Color);
*Ptr = Color;
for(int i = 1;i < x2;++i)
{
if(p < 0) p += Const1;
else
{
p += Const2;
Ptr += m_pGImage->GetWidth();
// ++y;
}
++x;
*Ptr = Color;
// SetPixel(x,y,Color);
}
}
Phần code ở trên chỉ vẻ được line trong 1/4 của đường tròn mà thôi! Đây là code đầy đủ của hàm Line():
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
inline BOOL GDrawing_Ind::Line(int x1,int y1,int x2,int y2,DWORD Color)
{
// Using Bresenham algorithm
// Get two rear points of line which inside drawing area
if(GetValidLine(x1,y1,x2,y2,m_pGImage->GetWidth(),m_pGImage->GetHeight()) <= 0) return 0;
if(y1 > y2)
{
// Be careful with same memory block
x1 = x1^x2;
x2 = x2^x1;
x1 = x1^x2;
y1 = y1^y2;
y2 = y2^y1;
y1 = y1^y2;
}
UINT Dx = ((x1 - x2 < 0) ? x2 - x1 : x1 - x2) + 1;
UINT Dy = y2 - y1 + 1;
// Vertical line
UINT ImgWidth = m_pGImage->GetWidth();
DWORD* Ptr = &m_pGImage->GetBits()[y1*ImgWidth + x1];
if(x1 == x2)
{
for(UINT i = 0;i < Dy;++i)
{
*Ptr = Color;
Ptr += ImgWidth;
}
return 1;
}
// Horizontal line
if(y1 == y2)
{
if(x2 < x1)
{
x1 = x1^x2;
x2 = x2^x1;
x1 = x1^x2;
Ptr = &m_pGImage->GetBits()[ImgWidth*y1 + x1];
}
for(UINT i = 0;i < Dx;++i) *Ptr++ = Color;
return 2;
}
// Diagonal line
if(Dx == Dy)
{
if(x1 < x2)
{
for(UINT i = 0;i < Dx;++i)
{
*Ptr = Color;
++Ptr += ImgWidth;
}
}
else
{
for(UINT i = 0;i < Dx;++i)
{
*Ptr = Color;
--Ptr += ImgWidth;
}
}
return 3;
}
// X - Major line
if(Dx > Dy)
{
// Prepare for drawing
int P = (Dy << 1) - Dx;
int Offset1 = Dy << 1;
int Offset2 = (Dy - Dx) << 1;
if(x1 < x2)
{
DWORD* pEndPtr = Ptr + Dy*ImgWidth + Dx;
for(;Ptr < pEndPtr;++Ptr)
{
*Ptr = Color;
if(P < 0) P += Offset1;
else
{
P += Offset2;
Ptr += ImgWidth;
}
}
}
else
{
DWORD* pEndPtr = Ptr + (Dy - 1)*ImgWidth + x2 - x1;
for(;Ptr < pEndPtr;--Ptr)
{
*Ptr = Color;
if(P < 0) P += Offset1;
else
{
P += Offset2;
Ptr += ImgWidth;
}
}
for(;Ptr > pEndPtr;--Ptr) *Ptr = Color;
}
}
else // Y - Major line
{
// Prepare for drawing
int P = (Dx << 1) - Dy;
int Offset1 = Dx << 1;
int Offset2 = (Dx - Dy) << 1;
int x = x1,y = y1;
if(x1 < x2)
{
DWORD* pEndPtr = Ptr + (Dy - 1)*ImgWidth + Dx;
for(;Ptr < pEndPtr;Ptr += ImgWidth)
{
*Ptr = Color;
if(P < 0) P += Offset1;
else
{
P += Offset2;
Ptr++;
}
}
}
else
{
DWORD* pEndPtr = Ptr + Dy*ImgWidth - Dx;
for(;Ptr < pEndPtr;Ptr += ImgWidth)
{
*Ptr = Color;
if(P < 0) P += Offset1;
else
{
P += Offset2;
--Ptr;
}
}
}
}
return 4;
}
Nếu muốn dùng thêm blending thì thay "*Ptr = Color;" bằng hàm blend tương ứng như trong SetPixel()
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họ
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
3.Line with antialiasing :
- Bây giờ là tới phần AA cho line!
- Mình sẻ dùng thuật toán WU Antialiasing!
- Việc dùng AA là xác định mỗi row của line có bao nhiêu pixel! Khi đả xác định được số pixel thì chỉ việc lấy 255 chia cho số pixel!
- Trong hình trên thì row đầu tiên có 8 pixel -> 255/8 ~ 32 -> mỗi lần set một pixel thì Transparency sẻ giảm đi 32!
- Do đó, theo hình trên thì pixel thứ 1 sẻ có Transparency là 255, pixel thứ 2 sẻ có transparency là (255 - 32)! Việc giảm Transparency sẻ liên tục đến khi tọa độ y của pixel tăng lên 1 (nghĩa là sang row kế) thì Transparency sẻ được reset về 255! Và cứ thế!
- Nhưng như vậy thì nhìn line sẻ bị hỏng, do đó người ta thêm một line thứ 2 bên dưới hoặc trên line ban đầu (để bù lỗ ^_^)! Kết quả là sẻ có line được AA như hình bên dưới!
- Các bạn có thể xem về WU Antialiasing trên mạng! Kỹ thuật AA trên là cách mình làm thôi, còn WU thì hơi khác hơn tí xíu! Kỹ thuật AA ở trên có thể áp dụng cho tất cả các line, circle, ellipse, bezier,... nói chung là tất cả các line có chuyển tọa độ y của pixel!
- Phần code của WU như sau:
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
// Get two rear points of line which inside drawing area
if(GetValidLine(x1,y1,x2,y2,m_pGImage->GetWidth(),m_pGImage->GetHeight()) <= 0) return 0;
// Prevent continuous-set by using pointer
UINT ImgWidth = m_pGImage->GetWidth();
UINT ImgHeight = m_pGImage->GetHeight();
if(x1 == 0) ++x1;
else if(x1 == ImgWidth - 1) --x1;
if(x2 == 0) ++x2;
else if(x2 == ImgWidth - 1) --x2;
if(y1 == 0) ++y1;
else if(y1 == ImgHeight - 1) --y1;
if(y2 == 0) ++y2;
else if(y2 == ImgHeight - 1) --y2;
// Reposition P1 on top of P2
if(y1 > y2)
{
// Be careful with same memory block
x1 = x1^x2;
x2 = x2^x1;
x1 = x1^x2;
y1 = y1^y2;
y2 = y2^y1;
y1 = y1^y2;
}
// Get distance of two point
UINT dx = ((x1 - x2 < 0) ? x2 - x1 : x1 - x2) + 1;
UINT dy = y2 - y1 + 1;
SColor32 ColorS = *(SColor32*)&Color;
SColor32* Ptr = (SColor32*)&m_pGImage->GetBits()[y1*ImgWidth + x1];
BYTE Transparency,PaperTransparency;
UINT TRatio = 0;
*Ptr = ColorS;
UINT Ratio = (dy << 16)/dx;
int PrevY2 = y1;
SColor32* Ptr2;
while(--dx)
{
TRatio += Ratio;
y2 = y1 + (TRatio >> 16);
++Ptr;
if(PrevY2 < y2)
{
Ptr += ImgWidth;
PrevY2 = y2;
}
Ptr2 = Ptr + ImgWidth;
Transparency = TRatio >> 8;
PaperTransparency = ~Transparency;
// Original
*Ptr = m_pGImage->AlphaBlend(*Ptr,ColorS,PaperTransparency);
Ptr2 = Ptr + ImgWidth;
*Ptr2 = m_pGImage->AlphaBlend(*Ptr2,ColorS,Transparency);
}
- Phần code trên là cho x - Major line (dx > dy, x1 < x2)
- Muốn viết các trường hợp còn lại thì chỉ cần đảo các var!
- Tồi về mình sẻ post tiếp một cách vẻ line và AA mới, nhanh hơn cả WU!
Mặc định Re: [GRAPHICS] Từng bước xây dựng cho mình một thư vi
Mặc định Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
3 - 2 Line with antialiasing :
- Trước khi nói về AA, mình sẻ nói về thuật toán vẻ line mới (tạm gọi là RLight Line vậy ^_^)!
- Trước tiên xem cái hình này đả :
- Như trên hình, việc vẻ một line là set từng pixel theo chiều ngang (x,y) (với X Major line)! Đến một lúc nào đó thì tăng y lên 1 (x,y + 1)! Nghĩa là nếu chúng ta biết khi nào cần tăng y lên thì việc vẻ Line xem như xong!
- Thuật toán bresenham dùng công thức đường thẳng để tính các offset, còn cách của mình là dựa trên ratio giửa dx và dy!
- Lấy vd, chúng ta sẻ vẻ một line từ 0x0 tới 9x3 -> dy/dx = (3 - 0 + 1)/(9 - 0 + 1) = 0.4
- Vẻ tay sẻ như sau:
Khi x = 0 -> y = 1*0.4 = 0.4 -> P(0,0)
Khi x = 1 -> y = 2*0.4 = 0.8 -> P(1,0)
Khi x = 2 -> y = 3*0.4 = 1.2 -> P(2,1)
Khi x = 3 -> y = 4*0.4 = 1.6 -> P(3,1)
Khi x = 4 -> y = 5*0.4 = 2.0 -> P(4,1)
Khi x = 5 -> y = 6*0.4 = 2.4 -> P(5,2)
Khi x = 6 -> y = 7*0.4 = 2.8 -> P(6,2)
Khi x = 7 -> y = 8*0.4 = 3.2 -> P(7,3)
Khi x = 8 -> y = 9*0.4 = 3.6 -> P(8,3)
Khi x = 9 -> y = 10*0.4 = 4.0 -> P(9,3)
- Kết quả sẻ như hình sau:
- Nhưng nếu nhân kiểu này thì chết chắc!
- Vì chúng ta đả biết ratio nên -> biết khi nào sẻ tăng y! Đặc IncWr là var để tăng offset, mỗi lần tăng x, chúng ta sẻ cộng Ratio vào IncWr, nếu IncWr > 1 có nghĩa là y sẻ tăng lên 1! Reset lại IncWr bằng cách trư đi 1!
- Kết quả sẻ như hình sau:
- Còn đây là code:
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
inline BOOL GDrawing_Ind::LineRl(int x1,int y1,int x2,int y2,DWORD Color)
{
#pragma region LineRl
// Using RLight Line algorithm
// Get two rear points of line which inside drawing area
if(GetValidLine(x1,y1,x2,y2,m_pGImage->GetWidth(),m_pGImage->GetHeight()) <= 0) return -1;
// Reposition P1 on top of P2
if(y1 > y2)
{
// Be careful with same memory block
x1 = x1^x2;
x2 = x2^x1;
x1 = x1^x2;
y1 = y1^y2;
y2 = y2^y1;
y1 = y1^y2;
}
// Get distance of two point
UINT dx = ((x1 - x2 < 0) ? x2 - x1 : x1 - x2) + 1;
UINT dy = y2 - y1 + 1;
// Prepare for drawing
UINT ImgWidth = m_pGImage->GetWidth();
DWORD* Ptr = &m_pGImage->GetBits()[y1*ImgWidth + x1];
// Vertical line
if(x1 == x2)
{
for(UINT i = 0;i < dy;i++)
{
*Ptr = Color;
Ptr += ImgWidth;
}
return 1;
}
if(y1 == y2) // Horizontal line
{
if(x2 < x1)
{
x1 = x1^x2;
x2 = x2^x1;
x1 = x1^x2;
Ptr = &m_pGImage->GetBits()[m_pGImage->GetWidth()*y1 + x1];
}
for(UINT i = 0;i < dx;i++) *Ptr++ = Color;
return 2;
}
if(dx == dy) // Diagonal line
{
if(x1 < x2)
{
for(UINT i = 0;i < dx;i++)
{
*Ptr = Color;
Ptr += ImgWidth + 1;
}
}
else
{
for(UINT i = 0;i < dx;i++)
{
*Ptr = Color;
Ptr += ImgWidth - 1;
}
}
return 3;
}
// X - major line
UINT StepSize = 65536; // 2^16
if(dx > dy)
{
UINT StepWr = (dy << 16)/dx,IncWr = 0;
if(x1 < x2)
{
DWORD* pEndPtr = Ptr + (dy - 1)*ImgWidth + dx;
for(;Ptr < pEndPtr;++Ptr)
{
IncWr += StepWr;
if(IncWr > StepSize)
{
IncWr -= StepSize;
Ptr += ImgWidth;
}
*Ptr = Color;
}
}
else
{
DWORD* pEndPtr = Ptr + (dy - 1)*ImgWidth + x2 - x1;
for(;Ptr < pEndPtr;--Ptr)
{
IncWr += StepWr;
if(IncWr > StepSize)
{
IncWr -= StepSize;
Ptr += ImgWidth;
}
*Ptr = Color;
}
for(;Ptr >= pEndPtr;--Ptr) *Ptr = Color;
}
}
else // Y - major line
{
UINT StepHr = (dx << 16)/dy,IncHr = 0;
if(x1 < x2)
{
DWORD* pEndPtr = Ptr + (dy - 1)*ImgWidth + dx;
for(;Ptr < pEndPtr;Ptr += ImgWidth)
{
IncHr += StepHr;
if(IncHr > StepSize)
{
IncHr -= StepSize;
++Ptr;
}
*Ptr = Color;
}
}
else
{
DWORD* pEndPtr = Ptr + dy*ImgWidth - dx;
for(;Ptr < pEndPtr;Ptr += ImgWidth)
{
IncHr += StepHr;
if(IncHr > StepSize)
{
IncHr -= StepSize;
--Ptr;
}
*Ptr = Color;
}
}
}
return 1;
#pragma endregion
}
- Test thử so với bresenham xem sao!
Line(1,1,400,4) : vẻ 1 000 000 line và cộng dồn trung bình 10 lần!
+ Dùng bresenham : 1.558410 (s)
+ Dùng RLight : 1.274334 (s)
Line(1,1,4,400) : vẻ 1 000 000 line và cộng dồn trung bình 10 lần!
+ Dùng Bresenham : 5.762071 (s)
+ Dùng RLight : 5.755710 (s)
Line(1,1,100,4) : vẻ 1 000 000 line và cộng dồn trung bình 10 lần!
+ Bresenham : 0.570991 (s)
+ RLight : 0.515729 (s)
- Ủa, vậy nhanh hơn bresenham sao ta ???! Test lại với line có độ dài nhỏ xem sao!
Line(1,1,50,4) : vẻ 1 000 000 line và cộng dồn trung bình 10 lần!
+ Bresenham : 0.367095 (s)
+ RLight : 0.388135 (s)
Line(1,1,25,4) : vẻ 1 000 000 line và cộng dồn trung bình 10 lần!
+ Bresenham : 0.208329 (s)
+ RLight : 0.243836 (s)
- Như vậy là vẻ mấy line nhỏ thì thuật toán của mình chậm hơn bresenham, nhưng khi vẻ mấy line dài hơn thì thuật toán của mình lại nhanh hơn (không tin các bạn cứ chép code về test thử)!
- Có bạn nào biết vì sao vẻ mấy line dài thì bresenham chạy chậm hơn ko ^_^ (đơn giản lắm)?!
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họ
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
3 - 2 Line With Antialiasing :
- Phần AA này sẻ sử dụng LineRl ở trên!
- Vì cách tính pixel của LineRl dựa trên ratio giửa dx và dy, khi chuyển sang dùng StepSize < 1 thì IncWr cũng sẻ là Transparency của pixel đó trên một row!
- Như trong hình :
- Pixel đầu tiên sẻ có Transparency là 0.4, pixel 2 là 0.8! Và như vậy, việc dùng AA cho LineRl rất đơn giản, lấy IncWr làm Transparency là xem như xong!
- Đây là code :
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
while(dx--)
{
IncWr += StepWr;
if(IncWr > StepSize)
{
IncWr -= StepSize;
Ptr += ImgWidth;
}
Transparency = IncWr >> 8;
Ptr2 = Ptr + ImgWidth;
Ptr2->Blue = ((ColorS.Blue - Ptr2->Blue)*Transparency + (Ptr2->Blue <<

) >> 8;
Ptr2->Green = ((ColorS.Green - Ptr2->Green)*Transparency + (Ptr2->Green <<

) >> 8;
Ptr2->Red = ((ColorS.Red - Ptr2->Red)*Transparency + (Ptr2->Red <<

) >> 8;
Transparency = ~Transparency;
Ptr->Blue = ((ColorS.Blue - Ptr->Blue)*Transparency + (Ptr->Blue <<

) >> 8;
Ptr->Green = ((ColorS.Green - Ptr->Green)*Transparency + (Ptr->Green <<

) >> 8;
Ptr->Red = ((ColorS.Red - Ptr->Red)*Transparency + (Ptr->Red <<

) >> 8;
++Ptr;
}
- Y hệt như LineRl, chỉ khác ở chổ thêm Transparency và dùng AlphaBlend!
- Test thử xem sao:
LineWithAntiAliasing(1,1,400,20,0xff)
Vẻ 100 000 line và tính trung bình 10 lần!
+ Wu : 1.109941 (s)
+ Rl : 1.097067 (s)
LineWithAntiAliasing(1,1,20,400,0xff)
Vẻ 100 000 line và tính trung bình 10 lần!
+ Wu : 2.183134 (s)
+ Rl : 2.083289 (s)
- Hình như là không nhanh hơn nhiều lắm...! Nhưng mà hông sao, nhanh hơn là tốt rồi ^_^!
- Mình vẫn thích dùng AA của mình hơn, nhanh hơn không nhiều nhưng code đơn giản hơn!
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họ
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
4. Rectangle
- Phần này cũng khá đơn giản nên mình chỉ post code thôi, cho pointer chạy tới chạy lui là được ^_^!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
inline bool GDrawing_Ind::Rectangle(int x,int y,UINT Width,UINT Height,DWORD Color)
{
// Get valid rect to draw
if(!GetValidRect(x,y,Width,Height,m_pGImage->GetWidth(),m_pGImage->GetHeight()))
return 0; // Outside rect
// Begin draw
UINT ImgWidth = m_pGImage->GetWidth();
UINT Offset = ImgWidth - Width;
DWORD* Ptr = &m_pGImage->GetBits()[ImgWidth*y + x];
for(UINT i = 0;i < Height;++i)
{
for(UINT j = 0;j < Width;++j) *Ptr++ = Color;
Ptr += Offset;
}
return 1;
}
- Nếu muốn dùng blending thì sửa lại chổ "*Ptr++ = Color" là được!

e: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa
e: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
- Ủa, hình như nntn post nhằm chổ thì phải! Bạn post vào phần hỏi đáp chắc sẻ có người giúp, post ở đây thì mình... bó tay...!!
- Đi tiếp thôi! Hôm trước định post luôn nhưng hình dd lúc đó bị gì nên không post được!
5. Circle outline :
- Mình định thêm phần Gradient vào rectangle luôn, nhưng đả chia ra rồi nên đến phần GDrawingGradient sẻ làm luôn!
- Tới phần Circle, chắc chắn sẻ dùng MidPoint, mình chưa tìm ra cách nào để "trị" tên này nên dùng nó luôn vậy!
- Mấy công thức rườm ra mình bỏ đi luôn cho rồi, nếu bạn nào muốn biết rỏ cách làm của thuật giải thì... như mọi khi, search trên google vậy (hông biết có ai nổi điên lên hông ta ...!)
- Như trên hình thì người ta (trong đó có mình ^_^) sẻ dùng exp F(x,y) = x^2 + y^2 - R^2 để tính!
+ Nếu F(x,y) < 0 thì có nghĩa là (x,y) nằm trong circle, sẻ lấy pixel ngay dưới pixel hiện tại!
+ Nếu F(x,y) = 0 thì (x,y) nằm trên circle -> khỏi phải nói...!
+ Nếu F(x,y) > 0 -> (x,y) ngoài circle -> lấy pixel ngay trên pixel hiện tại ( (x,y) chứ ai nửa...!))
- Cách tính mấy cái đó gõ ra chắc chết, vì vậy bạn nào muốn hiểu rỏ thì chịu khó... search vậy!
- Ở đây mình sẻ nêu cách để optimize nó thôi!
- Nguyên mẫu code như sau:
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
int i = 0;
int j = Radius;
int p = 1 - Radius; // Midpoint Algorithm
SetPixel(x,y + j,Color);
SetPixel(x,y - j,Color);
SetPixel(x + j,y,Color);
SetPixel(x - j,y,Color);
while(i++ <= j)
{
if(p < 0) p += 3 + (i << 1);
else p += 5 + ((i- --j) << 1);
SetPixel(x + i,y + j,Color);
SetPixel(x + i,y - j,Color);
SetPixel(x - i,y + j,Color);
SetPixel(x - i,y - j,Color);
SetPixel(x + j,y + i,Color);
SetPixel(x - j,y + i,Color);
SetPixel(x + j,y - i,Color);
SetPixel(x - j,y - i,Color);
}
- Test thử xem sao:
Circle(100,100,50,0xff) : vẻ 100 000 circle, trung bình 10 lần
+ 1.371378 (s) (vầy là chết...T_T !)
- Với tính của mình thì đời nào chịu để yên cho nó như vầy!
- Chúng ta thấy rằng, mỗi khi p >= 0 thì mới --y mà thôi, nghĩa là chúng ta hoàn toàn có thể dùng pointer ở đây!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
while(i++ < j)
{
if(p < 0) p += 1 + (i << 1);
else
{
p += 1 + ((i- --j) << 1);
// Inc offset
pBitsPtr2--;
pBitsPtr3--;
pBitsPtr6++;
pBitsPtr7++;
pBitsPtr4 += ImgWidth;
pBitsPtr5 += ImgWidth;
pBitsPtr1 -= ImgWidth;
pBitsPtr8 -= ImgWidth;
}
// Inc offset
++pBitsPtr1;
++pBitsPtr4;
--pBitsPtr5;
--pBitsPtr8;
pBitsPtr2 += ImgWidth;
pBitsPtr7 += ImgWidth;
pBitsPtr3 -= ImgWidth;
pBitsPtr6 -= ImgWidth;
// Set 8 pixels
if(UINT(x + i) < ImgWidth)
{
if(UINT(y + j) < ImgHeight) *pBitsPtr1 = Color;
if(UINT(y - j) < ImgHeight) *pBitsPtr4 = Color;
}
if(UINT(x - i) < ImgWidth)
{
if(UINT(y + j) < ImgHeight) *pBitsPtr8 = Color;
if(UINT(y - j) < ImgHeight) *pBitsPtr5 = Color;
}
if(UINT(x + j) < ImgWidth)
{
if(UINT(y + i) < ImgHeight) *pBitsPtr2 = Color;
if(UINT(y - i) < ImgHeight) *pBitsPtr3 = Color;
}
if(UINT(x - j) < ImgWidth)
{
if(UINT(y + i) < ImgHeight) *pBitsPtr7 = Color;
if(UINT(y - i) < ImgHeight) *pBitsPtr6 = Color;
}
}
- Chuyển sang pointer thì không khác gì mấy, chỉ bị rắt rối với mấy cái pixel nằm ngoài mà thôi!
- Test lại xem :
Circle(100,100,50,0xff) (vẻ tại pos = (100,100), radius = 50
100 000 circle, tính trung bình 10 lần
- 0.401037 (s) (vầy tốt hơn nhiều rồi ... ^_^)

- Tạm thời "Stop" cái GDrawing lại tí xíu, sang GImage làm thêm Rotate cho image cái đả, lâu rồi chẳn post gì cho GImage nhìn thiệt là thê thảm...!
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
- Mấy hôm nay thấy cũng hơi chán nên chẳng thèm post gì! Bây giờ mình sẻ post tiếp phần vẻ circle bằng thuật toán khác!
5 - 1 Circle Outline using RLight algorithm :
- Vẻ circle bằng midpoint thì chắc ai cũng biết, bây giờ mình sẻ giới thiệu cách vẻ circle mới!
http://img242.imageshack.us/my.php?image=cir2dk7.jpg
- Dựa trên công thức x*x + y*y = R*R, tất cả các pixel nào có tọa độ thỏa công thức trên sẻ nằm trên đường tròn!
- Nhưng tọa độ của mỗi pixel là số nguyên nên sẻ có sai số ở đây! VD (10.5,16.4) -> (10,16) hay làm theo midpoint sẻ là (11,16)!
- Vì đường tròn có tính đối xứng nên thay vì tính tọa độ của tất cả các pixel trên đường tròn thì chúng ta chỉ cần tính 1/8 đường tròn và lấy đối xứng!
- Một trong những cách tính tọa độ pixel dễ nhất và chậm nhất là!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
while(x++ < y)
{
i = x*x + y*y;
if(i >= (R + 1)*(R + 1))
{
--y;
}
Set8Pixel(x,y);
}
+ Có nghĩa là cho vòng lặp chạy đến khi x >= y (hết 1/8 vòng tròn), nếu i >= (R + 1)*(R + 1) có nghĩa là pixel đang xét nằm ngoài đường tròn -> giảm y, nếu không -> có nghĩ là pixel nằm trong phạm vi (R - 1) đến (R + 1)!
+ Vì sao phải là (R + 1) ?! Đó là vì tọa độ pixel là số nguyên -> độ chênh sẻ là 1 do việc làm tròn số!
+ Nhưng làm như cách trên sẻ rất chậm!
+ Như trong hình, chỉ cần chúng ta xác định được khi nào cần giảm y là mọi việc sẻ xong! Nhưng bằng cách nào???! MidPoint dùng công thức đường tròn để tính ra giá trị tăng trong mỗi lần lặp, nhưng khuyết điểm của nó là không thể lưu lại tỉ lệ với R -> Antialiasing gặp khó khăn!
+ Việc xác định khi nào sẻ giảm y thực sự không khó! Vì chúng ta có độ chênh lệch là 1 -> với chiều dài là R thì độ chênh là
(R + 1)*(R + 1) - R*R = 2*R + 1
+ Từ x*x + y*y, chúng ta có thể dùng một biến Inc để tăng offset, mỗi lần tăng là 2*x + 1! (x + 1)*(x + 1) - x*x = 2*x + 1!
+ Chúng ta sẻ tăng Inc trong mỗi lần lặp, đến khi Inc >= 2*R + 1 -> Pixel ở ngoài đường tròn thì giảm y!
+ Như vậy mọi việc đả xong!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
inline void GDrawing::CircleOutlineRl(int x,int y,UINT Radius,DWORD Color)
{
#pragma region CircleOutlineRl
// Using RLight algorithm
int BoundVl = Radius,Inc = 0;
UINT i = 0,j = Radius,R2 = Radius*Radius;
UINT ImgWidth = m_pGImage->GetWidth(),ImgHeight = m_pGImage->GetHeight();
// Init pointer
DWORD* Ptr1 = &m_pGImage->GetBits()[(y + Radius)*ImgWidth + x];
DWORD* Ptr2 = &m_pGImage->GetBits()[y*ImgWidth + x + Radius],*Ptr3 = Ptr2;
DWORD* Ptr4 = &m_pGImage->GetBits()[(y - Radius)*ImgWidth + x];
// Set 4 rear pixels
if(UINT(x) < ImgWidth && UINT(y + j) < ImgHeight) *Ptr1 = Color;
if(UINT(x) < ImgWidth && UINT(y - j) < ImgHeight) *Ptr4 = Color;
if(UINT(x + j) < ImgWidth && UINT(y) < ImgHeight) *Ptr2 = Color;
if(UINT(x - j) < ImgWidth && UINT(y) < ImgHeight) *(Ptr2 - (j << 1)) = Color;
// Set remain cir parts
while(i < j)
{
// Inc offset
Inc += (i++ << 1) + 1;
// Inc pointer
++Ptr1;
Ptr2 += ImgWidth;
Ptr3 -= ImgWidth;
++Ptr4;
// Check whether current pixel inside cir
if(Inc < BoundVl)
{
// 1 and 4
if(UINT(x + i) < ImgWidth)
{
if(UINT(y + j) < ImgHeight) *Ptr1 = Color;
if(UINT(y - j) < ImgHeight) *Ptr4 = Color;
}
// 5 and 8
if(UINT(x - i) < ImgWidth)
{
if(UINT(y - j) < ImgHeight) *(Ptr4 - (i << 1)) = Color;
if(UINT(y + j) < ImgHeight) *(Ptr1 - (i << 1)) = Color;
}
// 2 and 3
if(UINT(x + j) < ImgWidth)
{
if(UINT(y + i) < ImgHeight) *Ptr2 = Color;
if(UINT(y - i) < ImgHeight) *Ptr3 = Color;
}
// 6 and 7
if(UINT(x - j) < ImgWidth)
{
if(UINT(y - i) < ImgHeight) *(Ptr3 - (j << 1)) = Color;
if(UINT(y + i) < ImgHeight) *(Ptr2 - (j << 1)) = Color;
}
}
else
{
// Next line
--j;
// Inc pointer
Ptr1 -= ImgWidth;
--Ptr2;
--Ptr3;
Ptr4 += ImgWidth;
Inc = i*i + j*j - R2;
if(Inc < BoundVl)
{
// 1 and 4
if(UINT(x + i) < ImgWidth)
{
if(UINT(y + j) < ImgHeight) *Ptr1 = Color;
if(UINT(y - j) < ImgHeight) *Ptr4 = Color;
}
// 5 and 8
if(UINT(x - i) < ImgWidth)
{
if(UINT(y - j) < ImgHeight) *(Ptr4 - (i << 1)) = Color;
if(UINT(y + j) < ImgHeight) *(Ptr1 - (i << 1)) = Color;
}
// 2 and 3
if(UINT(x + j) < ImgWidth)
{
if(UINT(y + i) < ImgHeight) *Ptr2 = Color;
if(UINT(y - i) < ImgHeight) *Ptr3 = Color;
}
// 6 and 7
if(UINT(x - j) < ImgWidth)
{
if(UINT(y - i) < ImgHeight) *(Ptr3 - (j << 1)) = Color;
if(UINT(y + i) < ImgHeight) *(Ptr2 - (j << 1)) = Color;
}
}
}
}
#pragma endregion
}
- Test thử xem!
Vẻ 100000 circle(Radius = 50), và cộng dồn trung bình 10 lần!
+ MidPoint : 4.540756 (s)
+ RLight : 4.540211 (s)
+ Chẳng khác gì so với MidPoint! Các bạn có thể nói là vô lý vì bên trong vòng lặp có dùng phép nhân (Inc = i*i + j*j - R2)! Dù vậy,do sử dụng một cache line và một biến (MidPoint dùng 2 biến), nên cách của mình vẩn ngang với MidPoint!
+ Nhưng nếu chỉ ngang với MidPoint thì mình chẳng viết nó ra làm chi! Mình viết cái circle này là cho circle antialiasing, vì cách của mình có lưu lại tỉ lệ với R trong Inc -> AA rất dễ..., còn MidPoint thì không!!!
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họ
Re: [GRAPHICS] Từng bước xây dựng cho mình một thư viện đồ họa - GDrawing
6. Circle Solid :
- Khi đả có thể vẻ được đường tròn rồi thì việc fill nó không khó! Chỉ việc SetPixel từ (x - i) đền (x + i) là được!
- Vì chúng ta có tọa độ i và j, nên mọi chuyện trở nên dễ dàng! Nhưng nếu chúng ta fill không đúng chổ sẻ xảy ra trường hợp bị thừa do overlap các line!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
while(i < j)
{
if(Inc < BoundVl)
{
// Set line ở đây! (1)
}
else
{
// Hay ở đây! (2)
}
- Xem kỹ lại thì chắc ai cũng nhận ra là phải fill ở chổ thứ 2! Đó là do i sẻ được tăng liên tục nhưng j thì chỉ giảm khi (Inc >= BoundVl), do đó nếu đặc ở (1) thì chúng ta đả fill dư (n - 1) line ( n = số pixel trên 1 row của 1/8 cir)!
- Do đó, tóm lại là đặc code để fill cir ở (2) là được!
Cplusplus Code: | Lựa chọn code | Ẩn/Hiện code |
for(int k = i - 1;k <= x + i;++k)
{
SetPixel(k,j + 1,Color);
}
- Ở đây, tọa độ y sẻ là (j - 1), đó là do khi (Inc >= BoundVl) thì j đả được giảm đi 1 (--j), tức là nó sẻ nhảy sang line ở trên, trong khi chúng ta lại muốn fill line bên dướí nên phải cộng cho 1!
- Ở đây mình dùng SetPixel(), nếu bạn nào muốn chạy nhanh thì dùng pointer (nhanh hơn ít nhất 2 lần)! Nhưng dùng pointer sẻ gặp rắc rối ở vấn đề định vị pointer đó nha, các bạn thử sẻ biết ... ^_^!
+ Còn cái này là dùng AA cho circle! (cách mình làm hơi ăn gian, có ai nghĩ ra các gì hay cho AA với cái này ko??!)
