النص الحديث على لينكس: الجزء 1

مرحبًا بك في الجزء الأول من تطبيق Linux Linux Rendering. في كل مقال في هذه السلسلة ، سنقوم بتطوير برنامج C قائم بذاته لتصور شخصية أو تسلسل أحرف. سيقوم كل من هذه البرامج بتنفيذ وظيفة أرى أنها ضرورية لتقديم النص الحديث.

في الجزء الأول ، سنقوم بتكوين FreeType وكتابة عارض رمز بسيط في وحدة التحكم.



هذا هو ما سنكتب. وهنا هو الرمز.

إعداد النظام


  • نظام التشغيل الخاص بي: Ubuntu 18.04.2 LTS (bionic)
  • برنامج التحويل البرمجي C: clang version 6.0.0-1ubuntu2

تثبيت FreeType


على Ubuntu ، تحتاج إلى تثبيت FreeType و libpng.

 $ sudo apt install libfreetype6 libfreetype6-dev $ sudo apt install libpng16-16 libpng-dev 

  • لدي إصدار FreeType 2.8.1-2ubuntu2 ، على الرغم من أنه في وقت كتابة هذا التقرير ، كان أحدث إصدار من FreeType-2.10.1 ، كما أنه يعمل أيضًا.
  • نسخة libpng (1.6.34-1ubuntu0.18.04.2)

عارض وحدة التحكم


إنشاء ملف C ( main.c في حالتي)


 #include <stdio.h> int main() { printf("Hello, world\n"); return 0; } 

 $ clang -Wall -Werror -o main main.c $ ./main Hello, world 

نحن نربط مكتبات FreeType


للبحث عن مسار التضمين (أي الدلائل التي يمر بها المترجم عند البحث عن الملفات في #include ) لـ FreeType ، قم بتشغيل:

 $ pkg-config --cflags freetype2 -I/usr/include/freetype2 -I/usr/include/libpng16 

يحتوي السطر -I/usr/include/freetype2 -I/usr/include/libpng16 على أعلام تجميع ضرورية لتمكين FreeType في برنامج C.

 #include <stdio.h> #include <freetype2/ft2build.h> #include FT_FREETYPE_H int main() { printf("Hello, world\n"); return 0; } 

 $ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ main.c $ ./main Hello, world 

نحن طباعة نسخة من FreeType


بداخل main() بتهيئة FreeType باستخدام FT_Init_FreeType(&ft) وتحقق من الأخطاء (ترجع وظائف FreeType 0 إذا نجحت).

(من الآن فصاعدًا ، جميع الوظائف التي سأستخدمها مأخوذة من مساعدة واجهة برمجة تطبيقات FreeType ).

 FT_Library ft; FT_Error err = FT_Init_FreeType(&ft); if (err != 0) { printf("Failed to initialize FreeType\n"); exit(EXIT_FAILURE); } 

ثم باستخدام FT_Library_Version نحصل على رقم الإصدار.

 FT_Int major, minor, patch; FT_Library_Version(ft, &major, &minor, &patch); printf("FreeType's version is %d.%d.%d\n", major, minor, patch); 

إذا تم تجميعها باستخدام الأمر الأخير ، فسيظهر خطأ رابط:

 /tmp/main-d41304.o: In function `main': main.c:(.text+0x14): undefined reference to `FT_Init_FreeType' main.c:(.text+0x54): undefined reference to `FT_Library_Version' clang: error: linker command failed with exit code 1 (use -v to see invocation) 

لإصلاح ذلك ، أضف -lfreetype .

 $ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ -lfreetype \ main.c $ ./main FreeType's version is 2.8.1 

تحميل الخط


الخطوة الأولى لعرض الحرف هي تنزيل ملف خط. أنا أستخدم أوبونتو مونو .

لفهم الفرق الدقيق بين بنية وجه الخط وعائلة الخطوط والخطوط الفردية ، راجع وثائق FreeType .

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

  FT_Face face; err = FT_New_Face(ft, "./UbuntuMono.ttf", 0, &face); if (err != 0) { printf("Failed to load face\n"); exit(EXIT_FAILURE); } 

ضبط حجم بكسل للوجه


باستخدام هذا الإرشادات ، نقول لـ FreeType العرض والارتفاع المطلوبين للأحرف المعروضة.

إذا تجاوزت الصفر للعرض ، فسوف يفسر FreeType هذا كـ "مثل الآخرين" ، في هذه الحالة 32 بكسل. يمكن استخدام هذا لعرض حرف ، على سبيل المثال ، بعرض 10 بكسل وارتفاع 16 بكسل.

قد تفشل هذه العملية على خط بحجم ثابت ، كما في حالة رمز تعبيري.

 err = FT_Set_Pixel_Sizes(face, 0, 32); if (err != 0) { printf("Failed to set pixel size\n"); exit(EXIT_FAILURE); } 

الحصول على مؤشر للشخصية


بادئ ذي بدء ، العودة إلى وثائق FreeType وإنشاء اصطلاح تسمية. الرمز ليس هو نفس الصورة الرمزية . الحرف هو ما يقوله char ، والصورة الرمزية هي صورة مرتبطة بطريقة ما بتلك الشخصية. هذه العلاقة معقدة إلى حد ما لأن char يمكن أن تتوافق مع العديد من الحروف الرسومية: مثل اللهجات. يمكن أن تتوافق الصورة الرمزية مع العديد من الأحرف: أي الحروف المركبة ، حيث -> يتم تمثيلها كصورة واحدة.

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

  FT_UInt glyph_index = FT_Get_Char_Index(face, 'a'); 

تحميل الصورة الرمزية من الوجه


بعد تلقي glyph_index ، يمكننا تحميل الصورة الرمزية المقابلة من وجهنا.

في إصدار مستقبلي ، سنناقش بالتفصيل أعلام التنزيل المختلفة وكيف تسمح لك باستخدام ميزات مثل التلميح وخطوط الصورة النقطية.

 FT_Int32 load_flags = FT_LOAD_DEFAULT; err = FT_Load_Glyph(face, glyph_index, load_flags); if (err != 0) { printf("Failed to load glyph\n"); exit(EXIT_FAILURE); } 

عرض حرف رسومي في الحاوية (فتحة الصورة الرمزية)


الآن يمكننا أخيرًا عرض الصورة الرمزية الخاصة بنا في الحاوية (الفتحة) المحددة في face->glyph .

سنناقش أيضًا تقديم العلامات في المستقبل ، لأنها تتيح لنا استخدام عرض LCD (أو البكسل الفرعي) وإظهار الحواف الرمادية.

 FT_Int32 render_flags = FT_RENDER_MODE_NORMAL; err = FT_Render_Glyph(face->glyph, render_flags); if (err != 0) { printf("Failed to render the glyph\n"); exit(EXIT_FAILURE); } 

إخراج الحرف إلى وحدة التحكم


يمكن الحصول على الصورة النقطية face->glyph->bitmap.buffer الرمزية المقدمة من face->glyph->bitmap.buffer الصورة الرمزية face->glyph->bitmap.buffer ، حيث يتم تقديمها كصفيف من قيم char غير الموقعة ، وبالتالي تتراوح قيمها من 0 إلى 255.

يتم إرجاع المخزن المؤقت كصفيف أحادي البعد ، ولكن هو صورة ثنائية الأبعاد. للوصول إلى الصف الأول من العمود j-th ، نحسب column * row_width + row ، كما هو الحال في bitmap.buffer[i * face->glyph->bitmap.pitch + j] .

يمكنك أن ترى أنه عند الوصول إلى الصفيف ، استخدمنا bitmap.width في حلقة و bitmap.pitch ، لأن طول كل سطر من وحدات البكسل يساوي bitmap.width ، لكن "عرض" المخزن المؤقت هو bitmap.pitch .

في التعليمة البرمجية التالية ، يتم فرز جميع الصفوف والأعمدة ، ويتم رسم أحرف مختلفة حسب سطوع البكسل.

 for (size_t i = 0; i < face->glyph->bitmap.rows; i++) { for (size_t j = 0; j < face->glyph->bitmap.width; j++) { unsigned char pixel_brightness = face->glyph->bitmap.buffer[i * face->glyph->bitmap.pitch + j]; if (pixel_brightness > 169) { printf("*"); } else if (pixel_brightness > 84) { printf("."); } else { printf(" "); } } printf("\n"); } 

إخراج وحدة التحكم.

 $ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ -lfreetype \ main.c && ./main FreeType's version is 2.8.1 .*****. .********. .********* . ***. *** *** .******** *********** .**. *** *** *** *** *** ***. *** .*********** *********** .*******.. 

→ يمكن رؤية الرمز الكامل هنا

استنتاج


لقد أنشأنا عارض الشخصيات الأساسية في وحدة التحكم. يمكن توسيع هذا المثال (وسوف) لتجسيد الأحرف في مواد OpenGL لدعم رموز تعبيرية ، تجسيد البكسل الفرعي ، الحروف المركبة ، والمزيد. في الجزء التالي ، سنتحدث عن تجانس البكسل الفرعي لشاشة LCD مقارنة بظلال اللون الرمادي وإيجابياتهم وسلبياتهم.

اراك قريبا

Source: https://habr.com/ru/post/ar461497/


All Articles