-
12-04-2008, 08:59 AM #1Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
Lập trình đồ họa | 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 ^_^!View more random threads:
- Tutorial - Tạo một chương trình MFC đơn giản trong VC++ 2008 bằng hình ảnh
- Hướng dẫn khắc phục lỗi không tìm thấy MSVCR80D.dll
- Đồ họa | Từng bước xây dựng cho mình một thư viện đồ họa
- Ở Đâu Sửa Laptop Tại Chỗ Q 5 Chất Lượng
- Hotgirl Trâm Anh lộ clip nóng mới nhất
- Một số hàm thao tác chuỗi hay dùng trong VC++
- Kiểm tra số nguyên tố bằng cách tạo nhiều luồng(Multi Thread)
- GF GameFrameWork đơn giản cho dân gà.
- hồn trương ba, da hàng thịt
- Sử dụng CFileDialog Trong MFC
-
12-04-2008, 09:20 AM #2Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
1.SetPixel and GetPixel :
- Trong GImage cũng có hàm Draw_Pixel, mình chỉ thêm cho đầy đủ thôi!
Mã: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;}
Mã: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;}
- 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:
Mã: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;}
(Ex: DWORD GDrawing_Ind::GetPixel(int x,int y,DWORD &Color) )
-
12-04-2008, 09:33 AM #3Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
À quên, còn phần blending cho pixel nửa chứ!
Thêm hai hàm này vào:
Mã: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)) >> 8; BkColor.Green = ((UpperColor.Green - BkColor.Green)*Transparency + (BkColor.Green << 8)) >> 8; BkColor.Red = ((UpperColor.Red - BkColor.Red)*Transparency + (BkColor.Red << 8)) >> 8; return BkColor;}
- 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) >> 8 (Transparency <= 255)
- Như vậy blending cho SetPixel sẻ chuyển thành:
Mã: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)) >> 8; pColor->Green = ((ColorS.Green - pColor->Green)*Transparency + (pColor->Green << 8)) >> 8; pColor->Red = ((ColorS.Red - pColor->Red)*Transparency + (pColor->Red << 8)) >> 8; } }
Mã: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); } }
-
12-04-2008, 03:28 PM #4Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
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!
Mã: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); }}
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())!
Mã: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); }}
Mã: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; }
-
12-04-2008, 03:32 PM #5Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
RL này. Cậu chỉnh code sao cho dễ nhìn 1 chút đi (nhất là mấy cái SetPixel ở trên đó). Mình đang hoàn thiện tiếp GImage, thứ 7 mới rãnh.
GoodLuck.
-
12-04-2008, 03:38 PM #6Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
Nhìn dễ hơn chưa Z!
-
12-04-2008, 03:54 PM #7Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
Ok rồi!
RL ghép SSE và Bresenham được rồi đó. Ta sẽ chia đoạn thẳng thành 4 khúc (nếu đường thẳng dài hơn 40 pixel). SSE có khả năng tính 4 phép tính float tại một thời điểm do đó mình nghĩ tốc độ tăng ít nhất là 3.5 lần trừ đi một ít sai số khi khởi tạo.
-
12-04-2008, 03:59 PM #8Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
À, nếu ghép thì mình nghĩ nên ghép chung với MMX! MMX giống như SSE thôi nhưng dùng số nguyên (vì code trên toàn dùng số nguyên)! Z có rảnh thì dùng thử nha!
-
13-04-2008, 09:48 AM #9Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
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:
Mã:// Get two rear points of line which inside drawing areaif(GetValidLine(x1,y1,x2,y2,m_pGImage->GetWidth(),m_pGImage->GetHeight()) <= 0) return 0; // Prevent continuous-set by using pointerUINT 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 P2if(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 pointUINT 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);}
- 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!
-
13-04-2008, 04:25 PM #10Junior Member
- Ngày tham gia
- Sep 2015
- Bài viết
- 0
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:
Mã: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}
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)?!
Không chỉ dừng lại ở việc sử dụng nguyên liệu cao cấp, mỗi công trình còn được "chăm sóc" bằng 4 dịch vụ xuất sắc của Tre Nghệ. Từ việc tư vấn, thiết kế đến thi công và hoàn thiện, mọi quy trình đều...
Chất Lượng và Sáng Tạo: Kiến Trúc...