كيفية كتابة مكون تحليل الصورة في دلفي
باعتبارها أداة تطوير RAD قوية، تتمتع دلفي دائمًا بمزاياها الفريدة في تطوير برامج التطبيقات. وتنعكس هذه الميزة أيضًا في تطوير البرامج المتعلقة بالصور. إذا كنت تريد وضع صورة على سطح المكتب، ما عليك سوى وضع عنصر تحكم الصورة على سطح المكتب، وبعد ذلك يمكنك تحميل الصور بشكل تعسفي بتنسيقات BMP وWMF وEMF وغيرها من التنسيقات من خلال خاصية الصورة الخاصة به. إذا كنت تريد أيضًا إضافة دعم لـ JPEG، فأنت تحتاج فقط إلى إضافة وحدة JPEG. حتى بعد تحميل ملف JPEG في الصورة، ستقوم دلفي تلقائيًا بإضافة وحدة JPEG. كل شيء بسيط جدًا للقيام به. لقد تم تغليف التنسيقات الأساسية في VCL، فكيف تقوم دلفي بتنفيذ الدعم لتنسيقات الصور مثل JPEG؟
في الواقع، من السهل رؤية عملية التنفيذ من TPicture، والتي يمكن فهمها على أنها حاوية لجميع كائنات الصورة.
على سبيل المثال، يوجد السطرين التاليين من التعليمات البرمجية في JPEG.pas:
TPicture.RegisterFileFormat('jpeg', sJPEGImageFile, TJPEGImage);
TPicture.RegisterFileFormat('jpg', sJPEGImageFile, TJPEGImage);
(sJPEGImageFile = 'ملف صورة JPEG'، راجع JConsts.pas)
ماذا يعني ذلك؟ يمكن فهمه على أنه فئة تسجل TJPEGImage كملف صورة بلاحقتين: jpeg وjpg.
الجوهر هو حفظ اللاحقة ووصف الصورة وفئة تحليل الصور المحددة والمعلومات الأخرى في FileFormats.
راجع الكود التالي للحصول على التفاصيل:
var FileFormats: TFileFormatsList = nil;
فئة PROcedure TPicture.RegisterFileFormat(const AExtension,
وصف: سلسلة؛ AGraphicClass: TGraphicClass)؛
يبدأ
GetFileFormats.Add(AExtension, ADescription, 0, AGraphicClass);
نهاية؛
وظيفة GetFileFormats: TFileFormatsList؛
يبدأ
إذا FileFormats = لا شيء ثم FileFormats := TFileFormatsList.Create؛
النتيجة := تنسيقات الملفات؛
نهاية؛
يدعم TPicture أربعة تنسيقات صور بشكل افتراضي لأنه تمت إضافتها في مُنشئ TFileFormatsList.
منشئ TFileFormatsList.Create؛
يبدأ
إنشاء موروث؛
Add('wmf', SVMetafiles, 0, TMetafile);
Add('emf', SVEnhMetafiles, 0, TMetafile);
Add('ico', SVIcons, 0, TIcon);
Add('bmp', SVBitmaps, 0, TBitmap);
نهاية؛
من خلال المعلومات المحفوظة في FileFormats، يقوم عنصر التحكم OpenPictureDialog تلقائيًا بإنشاء قائمة بأنواع الملفات المدعومة.
فكيف تكتب فئات تحليل الصور هذه؟
TGraphic هي الفئة الأساسية لكائنات TBitmap وTIcon وTMetafile. وبالمثل، يجب أيضًا اشتقاق فئة تحليل الصور هنا من TGraphic، حيث يمكنها توفير الكثير من العمل باستخدام الكثير من التعليمات البرمجية التي تم تغليفها في VCL.
لتنفيذ الوظائف الأساسية، تحتاج عمومًا إلى زيادة التحميل على ثلاثة أعضاء فقط:
TXXXImage = فئة(TGraphic)
محمي
الإجراء Draw(ACanvas: TCanvas; const Rect: TRect override;// ارسم الصورة على القماش
عام
الإجراء LoadFromStream(Stream: TStream override);
الإجراء SaveToStream(Stream: TStream override);
نهاية؛
نظرًا لأن TGraphic.LoadFromFile/TGraphic.SaveToFile قد قام بالفعل بتنفيذ وظيفة قراءة البيانات من اسم الملف إلى الدفق/كتابة البيانات في الدفق إلى الملف المقابل، ليست هناك حاجة لزيادة تحميله دون احتياجات خاصة. يتم استخدام رسم العضو بشكل طبيعي لرسم الصور على اللوحة القماشية نظرًا لتغليف TCanvas المثالي لـ GDI، ليست هناك حاجة للنظر في عملية كيفية رسم الصور إلى النموذج باستخدام GDI. كل ما تبقى هو كتابة الكود الخاص بجزء تحليل الصورة.
لنأخذ تنسيق RAS كمثال لمزيد من المناقشة.
لا يتم استخدام TGraphic كفئة أساسية هنا، ولكن يتم استخدام TBitmap، مما يحفظ عملية تنفيذ Draw ويحتاج فقط إلى تنفيذ عملية التحويل إلى صورة نقطية في LoadFromStream.
يكتب
TRASGraphic = فئة (TBitmap)
عام
الإجراء LoadFromStream(Stream: TStream);
الإجراء SaveToStream(Stream: TStream override);
نهاية؛
// حدد نوع السجل الذي يصف رأس ملف RAS
TRASHheader = سجل معبأ
السحر، // مارك
العرض، // العرض
الارتفاع // مرتفع
العمق، // عمق اللون
الطول، // طول بيانات الصورة، قد يساوي 0
RasType، //نوع التنسيق
MapType، // نوع اللوحة
MapLength: Cardinal؛ // طول بيانات اللوحة
نهاية؛
// من الضروري جدًا تحديد نوع السجل المستخدم لوصف رأس ملف RAS
ثابت
// تحديد الثوابت التي تمثل جميع أنواع RAS
RT_OLD = 0;
RT_STANDARD = 1؛
RT_BYTE_ENCODE = 2؛
RT_FORMAT_RGB = 3؛
RT_FORMAT_TIFF = 4؛
RT_FORMAT_IFF = 5؛
RT_EXPERIMENTAL = $FFFF؛
// تحديد الثوابت التي تمثل أنواع اللوحة
RMT_NONE = 0; // لا توجد بيانات لوحة الألوان
RMT_EQUAL_RGB = 1;
RMT_RAW = 2;
{إذا كان تنسيق RAS هو RT_OLD، فقد يكون طول البيانات 0}
وظيفة SwapLong(قيمة ثابتة: الكاردينال): الكاردينال؛
asm
BSWAP EAX//تعليمات تبادل بايت المكالمة
نهاية؛
// طرح استثناء، المعلمة هي معلومات الاستثناء المحددة
الإجراء RasError(const ErrorString: String);
يبدأ
رفع EInvalidGraphic.Create(ErrorString);
نهاية؛
{ما يلي هو الكود الخاص بجزء التنفيذ. }
الإجراء TRASGraphic.LoadFromStream(Stream: TStream);
فار
الرأس: TRASHader؛
الصف 8: بايت؛
Row24: PRGBTriple؛
Row32: PRGBQuad؛
PMap: ببايت؛
ص: عدد صحيح؛
أنا: عدد صحيح؛
قراءة الخريطة: منطقية؛
بال: TMaxLogPalette؛
R، G، B: مجموعة [0..255] من البايت؛
كولور بايت: بايت؛
يبدأ
مع تيار القيام به
يبدأ
ReadBuffer(Header, SizeOf(Header)); // اقرأ بيانات رأس الملف في السجل Header
مع رأس القيام به
يبدأ
العرض := SwapLong(Width);
الارتفاع := SwapLong(Height);
العمق := SwapLong(Depth);
الطول := SwapLong(Length);
RASType := SwapLong(RASType);
MapType := SwapLong(MapType);
MapLength := SwapLong(MapLength);
نهاية؛
// نظرًا لترتيب قراءة البيانات، تحتاج إلى استدعاء SwapLong أعلاه لتغيير الترتيب.
إذا (Header.Magic = $956AA659) و
(Header.Width<>0) و (Header.Height<>0) و
(Header.Depth في [1,8,24,32]) و(Header.RasType في [RT_OLD,RT_STANDARD,RT_BYTE_ENCODED,RT_FORMAT_RGB]) ثم
يبدأ
Width := Header.Width;
الارتفاع := Header.Height;
MapReaded := خطأ؛
رأس الحالة.عمق
1:PixelFormat := pf1Bit;
8:
يبدأ
PixelFormat := pf8Bit;
رأس الحالة.نوع الخريطة
RMT_NONE:
يبدأ
Pal.palVersion:=300$;
Pal.palNumEntries:=256;
لأني := 0 إلى 255 افعل
يبدأ
Pal.palPalEntry[I].peRed:=I;
Pal.palPalEntry[I].peGreen:=I;
Pal.palPalEntry[I].peBlue:=I;
Pal.palPalEntry[I].peFlags:=0;
نهاية؛
Palette := CreatePalette(PLogPalette(@Pal)^);
// عندما يكون عمق ألوان الصورة 8 بت ولا توجد معلومات لوحة الألوان، قم بإنشاء لوحة تدرج رمادي 8 بت
نهاية؛
RMT_EQUAL_RGB:
يبدأ
إذا (Header.MapLength = 3*256) إذن
يبدأ
Pal.palVersion:=300$;
Pal.palNumEntries:=256;
ReadBuffer(R,256);
ReadBuffer(G,256);
ReadBuffer(B,256);
لأني := 0 إلى 255 افعل
يبدأ
Pal.palPalEntry[I].peRed:=R[I];
Pal.palPalEntry[I].peGreen:=G[I];
Pal.palPalEntry[I].peBlue:=B[I];
Pal.palPalEntry[I].peFlags:=0;
نهاية؛
Palette := CreatePalette(PLogPalette(@Pal)^);
// اقرأ معلومات اللوحة في الملف
// بالنسبة لعمليات لوحة API ذات الصلة، يرجى التحقق من MSDN
نهاية
آخر
RasError("طول اللوحة خاطئ!");
MapReaded := صحيح؛
نهاية؛
RMT_RAW:
يبدأ
RasError("تنسيق الملف غير مدعوم!");
نهاية؛
نهاية؛
نهاية؛
24:PixelFormat := pf24Bit;
32:
يبدأ
PixelFormat := pf32Bit;
//
نهاية؛
نهاية؛
إذا (ليس MapReaded) و(Header.MapLength>0) إذن
يبدأ
الموضع := الموضع + Header.MapLength؛
نهاية؛
// إذا لم يكن طول اللوحة 0 ولم تتم قراءة المعلومات ذات الصلة بشكل صحيح، فتخطي هذه القطعة من البيانات
رأس الحالة.عمق
8:
يبدأ
إذا كان Header.RasType = RT_BYTE_ENCODED إذن
يبدأ
// ترميز
// يرجى التحقق بنفسك من المعلومات المتعلقة بتشفير وفك تشفير ضغط RLE.
RasError('تنسيق الضغط غير مدعوم!');
نهاية
آخر
يبدأ
لـ Y := 0 إلى الارتفاع-1
يبدأ
Row8:=ScanLine[Y];
ReadBuffer(Row8^,Width);
إذا (تعديل العرض 2) = 1 إذن
يبدأ
الموضع := الموضع + 1؛
نهاية؛
نهاية؛
نهاية؛
النهاية؛{نهاية 8 بت}
أربعة وعشرون:
يبدأ
رأس الحالة.RasType
RT_OLD،
RT_STANDARD:
يبدأ
لـ Y := 0 إلى الارتفاع-1
يبدأ
Row24:=ScanLine[Y];
ReadBuffer(Row24^,Width*3);
إذا (تعديل العرض 2) = 1 إذن
يبدأ
الموضع := الموضع + 1؛
نهاية؛
نهاية؛
نهاية؛
RT_BYTE_ENCODE:
يبدأ
// ترميز
// يرجى التحقق بنفسك من المعلومات المتعلقة بتشفير وفك تشفير ضغط RLE.
RasError('تنسيق الضغط غير مدعوم!');
نهاية؛
RT_FORMAT_RGB:
يبدأ
لـ Y := 0 إلى الارتفاع-1
يبدأ
Row24:=ScanLine[Y];
ReadBuffer(Row24^,Width*3);
لأني := 0 إلى العرض-1
يبدأ
ColorByte := Row24^.rgbtRed;
Row24^.rgbtRed := Row24^.rgbtBlue;
Row24^.rgbtBlue := ColorByte;
شركة (صف 24)؛
نهاية؛
// عندما يكون بتنسيق RT_FORMAT_RGB، احصل على البيانات بواسطة RGB، وهنا تحتاج إلى تبادل قيم R وB
إذا (تعديل العرض 2) = 1 إذن
يبدأ
الموضع := الموضع + 1؛
نهاية؛
نهاية؛
النهاية؛{نهاية RT_FORMAT_RGB}
آخر
RasError("تنسيق الملف غير مدعوم!");
نهاية؛
النهاية؛{نهاية 24 بت}
32:
يبدأ
رأس الحالة.RasType
RT_OLD،
RT_STANDARD:
يبدأ
لـ Y := 0 إلى الارتفاع-1
يبدأ
Row32:=ScanLine[Y];
ReadBuffer(Row32^,Width*4);
لأني := 0 إلى العرض-1
يبدأ
ColorByte := Row32^.rgbReserved;
Row32^.rgbReserved := Row32^.rgbBlue;
Row32^.rgbBlue := Row32^.rgbGreen;
Row32^.rgbGreen:= Row32^.rgbRed;
Row32^.rgbRed := ColorByte;
شركة (صف 32)؛
نهاية؛
// عند استخدام لون 32 بت، تحتاج إلى ضبط ترتيب البيانات بعد القراءة.
نهاية؛
نهاية؛
RT_BYTE_ENCODE:
يبدأ
// ترميز
// يرجى التحقق بنفسك من المعلومات المتعلقة بتشفير وفك تشفير ضغط RLE.
RasError('تنسيق الضغط غير مدعوم!');
نهاية؛
RT_FORMAT_RGB:
يبدأ
بالنسبة لـ Y := 0 إلى الارتفاع-1 افعل
يبدأ
Row32:=ScanLine[Y];
ReadBuffer(Row32^,Width*4);
لأني := 0 إلى العرض-1
يبدأ
ColorByte := Row32^.rgbBlue;
Row32^.rgbBlue := Row32^.rgbReserved;
Row32^.rgbReserved := ColorByte;
ColorByte := Row32^.rgbGreen;
Row32^.rgbGreen:= Row32^.rgbRed;
Row32^.rgbRed := ColorByte;
شركة (صف 32)؛
نهاية؛
// تم دمج رموز تعديل الطلب وتبادل قيم R و B هنا.
نهاية؛
النهاية؛{نهاية RT_FORMAT_RGB}
آخر
RasError("تنسيق الملف غير مدعوم!");
النهاية؛{نهاية 32 بت}
نهاية؛
آخر
يبدأ
FreeImage;
RasError("تنسيق الملف غير مدعوم!");
نهاية؛
نهاية؛
نهاية
آخر
RasError("تنسيق الملف غير مدعوم!");
نهاية؛ {انتهى بـ}
نهاية؛
{يظهر الكود التالي عدة مرات في الكود أعلاه:
إذا (تعديل العرض 2) = 1 إذن
يبدأ
الموضع := الموضع + 1؛
نهاية؛
وذلك لأن البيانات الموجودة في كل صف يجب أن تكون محاذاة للكلمات، أي أنه يجب تسجيل البيانات الموجودة في كل صف بعدد زوجي من البايتات. عندما يتم تسجيل معلومات اللون لكل بكسل في بايت واحد (8 بت) أو 3 بايت (24 بت) ويكون عدد وحدات البكسل في كل صف رقمًا فرديًا، فيجب تعبئة بايت واحد. لذلك يتم تخطي بايت واحد هنا.
في الكود خلف
إذا (تعديل العرض 2) = 1 إذن
يبدأ
فيل بايت:=0;
Stream.Write(FillByte,1);
نهاية؛
ويستند أيضا على نفس المبدأ. }
الإجراء TRASGraphic.SaveToStream(Stream: TStream);
فار
الرأس: TRASHader؛
الصف 8: بايت؛
Row24: PRGBTriple؛
Row32: PRGBQuad؛
فيلبايت: بايت؛
ص: عدد صحيح؛
أنا: عدد صحيح؛
بال: TMaxLogPalette؛
R، G، B: مجموعة [0..255] من البايت؛
يبدأ
Header.Magic := $956AA659;
Header.Width := SwapLong(Width);
Header.Height := SwapLong(Height);
Header.RasType := SwapLong(RT_STANDARD);
إذا (PixelFormat = pf1bit) أو (PixelFormat = pf4bit) إذن
تنسيق البكسل:=pf8bit
وإلا إذا كان (PixelFormat <> pf8bit) و(PixelFormat <> pf24bit) و(PixelFormat <> pf32bit) فعندئذ
تنسيق البكسل:=pf24bit;
حالة PixelFormat
PF8bit:
يبدأ
Header.Length := SwapLong(Height*(Width+(Width mod 2)));
Header.Depth := SwapLong(8);
Header.MapType := SwapLong(RMT_EQUAL_RGB);
Header.MapLength := SwapLong(3*256);
Stream.WriteBuffer(Header,SizeOf(Header));
GetPaletteEntries(Palette, 0, 256, Pal.palPalEntry);
لأني := 0 إلى 255 افعل
يبدأ
R[I]:=Pal.palPalEntry[I].peRed;
G[I]:=Pal.palPalEntry[I].peGreen;
B[I]:=Pal.palPalEntry[I].peBlue;
نهاية؛
// بالنسبة لعمليات لوحة API ذات الصلة، يرجى التحقق من MSDN
Stream.WriteBuffer(R,256);
Stream.WriteBuffer(G,256);
Stream.WriteBuffer(B,256);
لـ Y := 0 إلى الارتفاع-1
يبدأ
Row8 := ScanLine[Y];
Stream.WriteBuffer(Row8^,Width);
إذا (وضع العرض 2) = 1 إذن
يبدأ
فيل بايت:=0;
Stream.Write(FillByte,1);
نهاية؛
نهاية؛
نهاية؛
pf32bit:
يبدأ
Header.Length := SwapLong(Height*Width*4);
Header.Depth := SwapLong(32);
Header.MapType := SwapLong(RMT_NONE);
Header.MapLength := 0;
Stream.WriteBuffer(Header,SizeOf(Header));
لـ Y := 0 إلى الارتفاع-1
يبدأ
Row32 := ScanLine[Y];
لأني := 0 إلى العرض-1
يبدأ
Stream.WriteBuffer(Row32.rgbReserved,1);
Stream.WriteBuffer(Row32^,3);
شركة (صف 32)؛
نهاية؛
نهاية؛
نهاية؛
آخر
يبدأ
Header.Length := SwapLong(Height*Width*3);
Header.Depth := SwapLong(24);
Header.MapType := SwapLong(RMT_NONE);
Header.MapLength := 0;
Stream.WriteBuffer(Header,SizeOf(Header));
لـ Y := 0 إلى الارتفاع-1
يبدأ
Row24 := ScanLine[Y];
Stream.WriteBuffer(Row24^,Width*3);
إذا (وضع العرض 2) = 1 إذن
يبدأ
فيل بايت:=0;
Stream.Write(FillByte,1);
نهاية؛
نهاية؛
نهاية؛
نهاية؛
// SaveToStream هي في الأساس عملية عكسية لـ LoadFromStream.
نهاية؛
التهيئة
TPicture.RegisterFileFormat('RAS', 'Sun RAS', TRASGraphic);
وضع اللمسات النهائية
TPicture.UnregisterGraphicClass(TRASGraphic);
باستخدام هذه الأسطر القليلة من التعليمات البرمجية، يتم إكمال مكون تحليل الصورة بالكامل.