الشبكة العربية لمطوري الألعاب

خبير مدير وسام البهنسي مشاركة 11

السلام عليكم أخي الشمري،
 
آسف للتأخر في الرد، استطعت أخيراً التفرغ واستخراج بعض الكود الذي يقوم بكتابة النصوص على سطوح دايركت ثري دي من أحد مشاريعي.
 
الكود مكتوب بـ C++/CLI و NET. ويستخدم +GDI للرسم كما ذكرت. الكود بسيط ومختصر. من المرفقات:
 
 
وهاهو الكود كاملاً:
 


// مثال عن كود برمجي يقوم برسم النصوص على سطوح دايركت ثري دي
// لغة البرمجة هي دوت نيت مع
// C++/CLI
// ونستخدم مكتبة
// GDI+
// لكتابة النصوص
// تقديم: وسام البهنسي
// الشبكة العربية لمطوري الألعاب
// http://www.agdn-online.com
// رداً على الموضوع:
// http://www.agdn-online.com/communities.aspx?view=posts&threadid=544
 
 


// يقوم هذا الكلاس بإدارة الموارد اللازمة لرسم النصوص العربية
private ref class ArabicTextRenderer
{
public:
 ArabicTextRenderer() :
   m_NativeData(0)
 {
  // أنشئ صورة بأبعاد تتسع لأكبر جملة نصية تتوقع كتابتها بضربة واحدة
  // لاحظ أن هيئة البكسلات مهمة جداً، ويجب أن توافق هيئة السطح في محرك الرسم
  // وذلك لتفادي أي عمليات تحويل بطيئة
  m_Bitmap = gcnew Bitmap(256, 128, PixelFormat::Format32bppRgb); // كل بكسل 32 بت. لا يهمنا قناة ألفا
  m_BitmapData = gcnew BitmapData();
  m_Graphics = Graphics::FromImage(m_Bitmap);
 
  // حدد خواص الكتابة. تنسيق يمين-يسار
  m_Format = gcnew StringFormat();
  m_Format->FormatFlags = StringFormatFlags::NoClip | StringFormatFlags::DirectionRightToLeft;
  m_Format->Alignment = StringAlignment::Near;
  m_Format->HotkeyPrefix = HotkeyPrefix::None;
  m_Format->LineAlignment = StringAlignment::Near;
  m_Format->Trimming = StringTrimming::None;
 
  // معلومات عن كيفية حفظ الصورة في الذاكرة. كي نستطيع قراءتها دون تحويلات مكلفة
  m_Stride = (m_Bitmap->Width * Bitmap::GetPixelFormatSize(m_Bitmap->PixelFormat)) / 8; // عرض الصورة بالبايت
  int iBytesCount = m_Bitmap->Height * m_Stride; // حجم الصورة ككل بالبايت
  m_NativeData = new char[iBytesCount]; // المصفوفة الوسيطة التي ستستلم القيم اللونية للصورة
 
  // تجهيز معلومات الصورة لإجراء القفل
  m_BitmapData->Width = m_Bitmap->Width;
  m_BitmapData->Height = m_Bitmap->Height;
  m_BitmapData->PixelFormat = m_Bitmap->PixelFormat;
  m_BitmapData->Stride = m_Stride;
  m_BitmapData->Scan0 = (IntPtr)m_NativeData;
 }
 
 ~ArabicTextRenderer()
 {
  // حرر الذاكرة التي حجزناها بأنفسنا
  delete [] m_NativeData;
 }
 
 // إجراءات لتسهيل الوصول إلى أعضاء الكلاس
 property Bitmap^ TextBitmap { Bitmap^ get(void) { return m_Bitmap; } }
 property BitmapData^ TextBitmapData { BitmapData^ get(void) { return m_BitmapData; } }
 property Graphics^ TextGraphics { Graphics^ get(void) { return m_Graphics; } }
 property StringFormat^ TextFormat { StringFormat^ get(void) { return m_Format; } }
 property int Stride { int get(void) { return m_Stride; } }
 property void* NativeData { void* get(void) { return m_NativeData; } }
 
private:
 Graphics ^m_Graphics; // جهاز الرسم بجي دي آي بلس
 Bitmap ^m_Bitmap; // الصورة التي سيتم الرسم عليها
 BitmapData ^m_BitmapData; // معلومات القفل والتعبئة
 StringFormat ^m_Format; // خواص الكتابة
 int m_Stride; // عرض الصورة بالبايت. انتبه، قد يكون أكبر من عدد البكسلات مضروباً بحجم كل منها
 void *m_NativeData; // مؤشر إلى القيم اللونية للصورة
};
 

// إجراء الكتابة على صورة وتحويلها إلى بايتات يمكن إرسالها إلى أي محرك رسم
// البارامتر الأول هو النص المرغوب كتابته
// البارامتر الثاني هو الخط المرغوب للكتابة
// البارامتر الثالث هو كلاس الكتابة بالعربية والذي يجب أن يكون قد تم إنشاؤه من قبل
// البارامتر الأخير هو سطح دايركت ثري الذي ترغب بالرسم عليه
void SetText(String ^text, Font ^font, ArabicTextRenderer^ renderer, IDirect3DSurface9* D3DSurface)
{
 Bitmap ^bmp = renderer->TextBitmap;
 Graphics ^gfx = renderer->TextGraphics;
 
 // أولاً نقوم بمسح محتويات الصورة كي نتخلص من أي بقايا من المرة الماضية
 gfx->Clear(System::Drawing::Color::Transparent);
 
 // يجب علينا أن نقيس النص كي نستطيع توضيعه بشكل صحيح في الصورة
 SizeF sizef = gfx->MeasureString(text, font, PointF(0,0), renderer->TextFormat);
 short width = (short)sizef.Width;
 short height = (short)sizef.Height+2; // ضع هامشاً بمقدار بكسل واحد كارتفاع
 float yo = 1; // الإزاحة من أعلى الصورة
 
 // ارسم النص بدءاً من النقطة المحددة
 // لاحظ أن نقطة الكتابة هي الزاوية العليا اليمنى للنص لأننا نكتب بالعربية،
 // وبالتالي نحن بحاجة إلى وضع هذه النقطة على أقصى يمين الصورة
 gfx->DrawString(text, font, Brushes::White, width , yo, renderer->TextFormat);
 
 // يمكنك رسم إطار حول النص لو أردت التحقق من صحة الحسابات
 //gfx->DrawRectangle(Pens::White, 0, 0, width-1, height-1);
 
 // المستطيل الذي يحدد المنطقة التي نود قفلها من الصورة. عملياً كل الصورة
 Rectangle rc(0,0,bmp->Width,bmp->Height);
 
 // نقل القيم اللونية من الصورة إلى المؤشر الخاص بنا
 // هذه العملية تتم بمجرد قفل بتات الصورة، وذلك وفقاً للبارامترات التي نمررها
 // لإجراء القفل. هذه العملية سريعة نسبياً لأننا نسحب المعلومات بدون أي تحويل.
 bmp->LockBits(rc,ImageLockMode::ReadOnly|ImageLockMode::UserInputBuffer,
               bmp->PixelFormat,renderer->TextBitmapData);
 bmp->UnlockBits(renderer->TextBitmapData);
 
 // الآن لدينا كل ما يلزم لوضع البكسلات في محرك الرسم
 // الإجراء التالي يضع البكسلات في سطح دايركت ثري دي كمثال
 CopyImageToD3DSurface(D3DSurface, renderer->NativeData, renderer->Stride, width, height);
 
 //bmp->Save(L"C:\\صورة.png"); // احفظ الصورة للتحقق
}
 

// إجراء تعبئة سطح دايركت ثري دي بقيم لونية من مصفوفة ما. أي محرك رسم يجب أن يقدم إجراءً مماثلاً
// البارامتر الأول هو السطح المرغوب تعبئته
// البارامتر الثاني هو مصفوفة القيم اللونية التي تريد نسخها على السطح
// البارامتر الثالث هو عرض مصفوفة القيم اللونية بالبايت، وليس بالبكسل
// البارامتر الرابع والخامس هما أبعاد مصفوفة القيم اللونية بالبكسل. العرض والارتفاع على الترتيب
void CopyImageToD3DSurface(IDirect3DSurface9* D3DSurface, void* sourcePixels,
                           int stride, int width, int height)
{
 // هناك عدة افتراضات هنا لتبسيط الكود للمتعلم:
 // حجم السطح أكبر من أو يساوي أبعاد مصفوفة القيم اللونية
 // هيئة السطح هي ذاتها هيئة مصفوفة القيم اللونية. بمعنى آخر، لو كانت القيمة
 // اللونية مؤلفة من 4 بايتات، كل منها قناة لونية أحمر، أخضر، أزرق، ألفا
 // فإن سطح دايركت ثري دي يجب أن يكون من الهيئة:
 // D3DFMT_A8R8G8B8 أو D3DFMT_X8R8G8B8
 
 // انسخ القيم اللونية من كامل المصفوفة
 RECT srcRect = {0, 0, height, width};
 
 // انسخ القيم اللونية إلى الزاوية العليا اليسارية من السطح
 RECT destRect = {0, 0, height, width};
 
 // إجراء تعبئة السطح من مصفوفة قيم لونية مقدم من مكتبة دايركت ثري دي الإضافية
 D3DXLoadSurfaceFromMemory(
  D3DSurface, // السطح الذي يتم تعبئته
  NULL, // مصفوفة بجدول الألوان. لا نستخدم هذه الميزة
  &destRect, // المستطيل الذي سيتم تعبئته بالقيم اللونية في الصورة
  sourcePixels, // مصفوفة القيم اللونية التي سيتم النسخ منها
  D3DFMT_X8R8G8B8, // هيئة القيم اللونية. 32 بت وقناة ألفا غير مهمة
  stride, // عرض مصفوفة القيم اللونية بالبايت
  NULL, // مصفوفة بجدول الألوان في مصفوفة القيم اللونية. لا نستخدم هذه الميزة
  &srcRect, // المستطيل الذي يعبر عن مكان النسخ من مصفوفة القيم اللونية
  D3DX_FILTER_NONE, // طريقة الترشيح في حال عدم تطابق الأبعاد. لا نريد أي ترشيح
  0); // اللون المفتاحي. سيتم استبدال جميع القيم اللونية المطابقة لهذا اللون باللون الشفاف
}
 
 
أرجو أن تستفيد من الكود في محركك الخاص. يمكنك ببساطة تحويل التقنيات إلى ++C بحتة مع GDI إن شئت. فقط سيزداد طول الكود قليلاً، لكن المبدأ لن يتغير إطلاقاً.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

مبتدئ  basha ali مشاركة 12

السلام عليكم

لحظة من فضلكم,

وفي 21/ربيع الأول/1430 04:21 م، ظهر شبح ابتسامة على وجه وسام البهنسي وهو يقول:

طبعاً لو كان البرنامج بحاجة إلى إعادة كتابة كاملة فقط لأجل إظهار النصوص على نظام تشغيل مختلف فإننا نتهم المبرمج بسوء تصميم الكود.

بالضبط أخي, مكتبة مثل OpenGL على الرغم من أنها واحدة في مختلف الأنظمة, إلا أن كتابتها في الحقيقة تمت بشكل خاص يماسب كل مظام تشغيل على حدة☺ و لذا من الطبيعي أن تجد مشروعك مدعوم من C++ على ويندوز مثلا دون غيرها. أذكرك أخي في النهاية بـGLAL ، حاول جديا بدراسة هذا البحث و التواصل مع كاتبه, لا سيما أنه من نفس البلد☺

http://doi.wiley.com/10.1002/spe.675

و السلام ختام

خبير  أحمد عبد الغني مشاركة 13

وفي 28/ربيع الأول/1430 12:13 ص، أعرب basha ali عن رأيه بالموقف كالآتي:

أذكرك أخي في النهاية بـGLAL ، حاول جديا بدراسة هذا البحث و التواصل مع كاتبه, لا سيما أنه من نفس البلد☺

http://www3.interscience.wiley.com/journal/110547974/abstract?CRETRY=1&SRETRY=0

لا أفهم أي بلد تقصد 😖
لكن الرابط لا يعمل. يعطيني خطأ في الكوكيز

اللهم انصر أهلنا في فلسطين وآجرنا أن نكون عوناً لهم

مبتدئ  basha ali مشاركة 14

السلام عليكم
أقصد أن الأستاذ / أبو عبد الملك من من نفس بلد الأخ الشمري (أي من المملكة العربية السعودية). عموما هذا هو رابط السيرة الذاتية له:

http://faculty.ksu.edu.sa/AbdulMalik/CV/VITA%20(Arabic).doc

 هو هذا رابط لمكتبة GLAL
http://doi.wiley.com/10.1002/spe.675

بالمناسبة المقالة يتم شراؤها. أنا عندي رابط للمكتبة, و لكن لن أضع وصلة لها هنا إحتراما لسياسة الموقع.

---- المشرفون: تم حذف ما يشير لقرصنة المكتبة ----

لم أقصد القرصنة، عموما سأبقي على السطر الذي وضعتموه بالأعلى إحتراما لسياستكم و تأكيدا عليها.

محترف مشرف عبد اللطيف حاجي علي مشاركة 15

وفي 25 آذار 2009 03:24 ص، قال basha ali متحمساً:

بالمناسبة المقالة يتم شراؤها. أنا عندي رابط للمكتبة, و لكن لن أضع وصلة لها هنا إحتراما لسياسة الموقع.
أشكر لك التزامك بسياسة الموقع. لكن أعتقد أن عرض المنتجات المقرصنة هو بحد ذاته مخالف لسياسة الموقع. وإن لم تضع رابطاً مباشراً لها.
أطلب من الإدارة توضيح ذلك ضمن شروط الموقع.

عبد اللطيف حاجي علي
مبرمج
In|Framez

موهوب  عبدالله الشمّري هذه المشاركة مميزة مشاركة 16

السلام عليكم ,
 
- بارك الله فيك أخي وسام , كود رائع وشرح واضح أيضا , قمت بتحويل الكود إلى OpenGL ( باستخدام ++C طبعا ) , الكود يعمل بشكل رائع 😋
 





كنت واجهت صعوبة مع الدالة lock , والكائن m_BitmapData , لكن الحمد لله , تم حل الاشكال .
 
البرنامج بالمرفقات .. ليخضع للتقييم :-) ,  على العموم , استخدمت الدالة glDrawPixels , التي تقوم بالرسم على Buffer مباشرة , قد لاتكون هذه الطريقة هي الأفضل , يوجد طريقة أخرى وهي الرسم على سطح الاكساء , أعتقد هذا أسرع ؟
شكرا مرة أخرى أخي وسام على مشاركتك لنا بهذا الكود الرائع 😄  .
 



في 29/ربيع الأول/1430 01:24 ص، عقد basha ali حاجبيه بتفكير وقال:

أقصد أن الأستاذ / أبو عبد الملك من من نفس بلد الأخ الشمري (أي من المملكة العربية السعودية). عموما هذا هو رابط السيرة الذاتية له:

ومن نفس الكلّية أيضا 😏  , قد أحاول الحصول على المكتبة منه ان استطعت مقابلته  ( لم أقابله أبدا :( ) , هو الان في السلك الاداري تقريبا ,ولكن أعتقد أننا قادرون على تطوير مكتبة مشابهة ... حيث مشروع تطوير المكتبة لازال قائم طبعا ..

--
طالب - تخصص نظم معلومات .
--

مفصول عمر سمير  مشاركة 17

كيف احول الكود  للكتابة باللغة العربية  من  C++ TO C# XNA

وجدت صعوبة في تغير متغيرات  الكود المكتوب  بلغة C++  وتحويلها الى  XNA 

لأنني اكره هذه اللغة  ++C    لانها  معقدة  ولا أحب  ان اتعلمها  ابداً   للصعوبة برمجتها  كثيراً   وعدم  مقدرتي  لها ذاتياً  .

هل  تكملون معروفكم  وتبرمجها  على لغة  XNA   نفسها

OSF متخصص محترف

مبتدئ  حسام زكريا مشاركة 18

السلام عليكم ورحمة الله وبركاته:

أحببت أن أطلعكم على مكتبة مجانية تقوم بحل مشكلة قلب الحروف من أجل لغات اليمين لليسار (العربي والعبري) وحل مشكلة تغيير أشكال الحروف (العربي) اسمها FriBidi تجدها على موقع fribidi.org

هناك مكتبة أصغر منها اسمها miniBidi تقوم بنفس العمل تقريباً

يمكنك الحصول على معلومات أكثر حول Arabic localization من موقع  www.arabeyes.org

Husam Zakaria
Game Programmer

مبتدئ  Waleed Mohamed مشاركة 19

sorry for writing in English i have  no arabic keyboard
I worked with Sakher (harf) for a while and they did develop a very nice liberary that use their own fonts 
it make use of reshape and reordring for the text i will try to contact them and see how it could work 
aboziad

مبتدئ  Waleed Mohamed مشاركة 20

I did found a nice open source library that is opensource it is created by IBM for this it is called ICU
you can use it for reordering and shaping