טיפ ג'אווה 60: שמירת קבצי מפת סיביות בג'אווה

טיפ זה משלים את Java Tip 43, שהדגים את תהליך טעינת קבצי מפת הסיביות ביישומי Java. החודש אני ממשיך בהדרכה כיצד לשמור תמונות בקבצי מפת סיביות של 24 סיביות וצלף קוד באמצעותו ניתן לכתוב קובץ מפת סיביות מאובייקט תמונה.

היכולת ליצור קובץ מפת סיביות פותחת דלתות רבות אם אתה עובד בסביבת Microsoft Windows. בפרויקט האחרון שלי, למשל, נאלצתי לממשק ג'אווה עם Microsoft Access. תוכנית Java איפשרה למשתמש לצייר מפה על המסך. המפה הודפסה אז בדוח של Microsoft Access. מכיוון שג'אווה אינה תומכת ב- OLE, הפיתרון היחיד שלי היה ליצור קובץ מפת סיביות של המפה ולספר לדוח Microsoft Access היכן לקחת אותו. אם אי פעם היית צריך לכתוב יישום כדי לשלוח תמונה ללוח, טיפ זה עשוי לשמש אותך - במיוחד אם מידע זה מועבר ליישום אחר של Windows.

הפורמט של קובץ מפת סיביות

פורמט קובץ מפת הסיביות תומך ב- RLE של 4 סיביות (קידוד באורך ריצה), וכן בקידוד של 8 סיביות ו -24 סיביות. מכיוון שאנחנו מתעסקים רק בפורמט 24 סיביות, בואו נסתכל על מבנה הקובץ.

קובץ מפת הסיביות מחולק לשלושה חלקים. הנחתי אותם בשבילך למטה.

סעיף 1: כותרת קובץ מפת סיביות

כותרת זו מכילה מידע על גודל הסוג והפריסה של קובץ מפת הסיביות. המבנה הוא כדלקמן (נלקח מהגדרת מבנה שפה C):

תג מבנה typedef BITMAPFILEHEADER {UINT bfType; DWORD bfSize; UINT bfReserved1; UINT bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;

הנה תיאור של רכיבי הקוד מהרישום שלעיל:

  • bfType: מציין את סוג הקובץ ותמיד מוגדר ל- BM.
  • bfSize: מציין את גודל הקובץ כולו בתים.
  • bfReserved1: שמורה - יש להגדיר אותה ל -0.
  • bfReserved2: שמורה - יש להגדיר אותה ל -0.
  • bfOffBits: מציין את קיזוז הבתים BitmapFileHeaderמתחילת התמונה.

כאן ראית שהמטרה של כותרת מפת הסיביות היא לזהות את קובץ מפת הסיביות. כל תוכנית שקוראת קבצי מפת סיביות משתמשת בכותרת מפת הסיביות לאימות קבצים.

סעיף 2: כותרת מידע על מפת סיביות

הכותרת הבאה, המכונה כותרת המידע, מכילה את כל המאפיינים של התמונה עצמה.

כך תוכל לציין מידע אודות הממד ופורמט הצבע של מפת סיביות עצמאית (DIB) של Windows 3.0 (ומעלה):

תג מבנה typedef BITMAPINFOHEADER {DWORD biSize; דו רוחב ארוך; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClr חשוב; } BITMAPINFOHEADER;

כל רכיב ברישום הקוד לעיל מתואר להלן:

  • biSize: מציין את מספר הבתים הנדרש על ידי BITMAPINFOHEADERהמבנה.
  • biWidth: מציין את רוחב מפת הסיביות בפיקסלים.
  • biHeight: מציין את גובה מפת הסיביות בפיקסלים.
  • biPlanes: מציין את מספר המטוסים למכשיר היעד. יש להגדיר את החבר הזה ל -1.
  • biBitCount: מציין את מספר הביטים לפיקסל. ערך זה חייב להיות 1, 4, 8 או 24.
  • biCompression: מציין את סוג הדחיסה עבור מפת סיביות דחוסה. בפורמט של 24 סיביות, המשתנה מוגדר ל- 0.
  • biSizeImage: מציין את הגודל בבתים של התמונה. זה חוקי להגדיר חבר זה ל- 0 אם מפת הסיביות BI_RGBבפורמט.
  • biXPelsPerMeter: מציין את הרזולוציה האופקית, בפיקסלים למטר, של מכשיר היעד עבור מפת הסיביות. יישום יכול להשתמש בערך זה כדי לבחור מפת סיביות מקבוצת משאבים המתאימה ביותר למאפייני ההתקן הנוכחי.
  • biYPelsPerMeter: מציין את הרזולוציה האנכית, בפיקסלים למטר, של מכשיר היעד עבור מפת הסיביות.
  • biClrUsed: מציין את מספר אינדקסי הצבעים בטבלת הצבעים המשמשת בפועל את מפת הסיביות. אם biBitCountמוגדר כ- 24, biClrUsedמציין את גודל טבלת הצבעים המפנה המשמשת לייעול הביצועים של לוחות הצבעים של Windows.
  • biClrImportant: מציין את מספר אינדקסי הצבע שנחשבים חשובים להצגת מפת הסיביות. אם ערך זה הוא 0, כל הצבעים חשובים.

כעת הוגדר כל המידע הדרוש ליצירת התמונה.

סעיף 3: תמונה

בפורמט 24 סיביות, כל פיקסל בתמונה מיוצג על ידי סדרה של שלושה בתים של RGB המאוחסנים כ- BRG. כל קו סריקה מרופד לגבול שווה של 4 בתים. כדי לסבך את התהליך עוד מעט, התמונה נשמרת מלמטה למעלה, כלומר קו הסריקה הראשון הוא קו הסריקה האחרון בתמונה. האיור הבא מציג גם כותרות ( BITMAPHEADER) וגם ( BITMAPINFOHEADER) וחלק מהתמונה. כל קטע מתחם על ידי סרגל אנכי:

0000000000 4D42 B536 0002 0000 0000 0036 0000 | 0028 0000000020 0000 0107 0000 00E0 0000 0001 0018 0000 0000000040 0000 B500 0002 0EC4 0000 0EC4 0000 0000 0000000060 0000 0000 0000 | FFFF FFFF FFFF FFFF FFFF 0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF *

עכשיו, לקוד

כעת, כשאנו יודעים הכל על המבנה של קובץ מפת סיביות של 24 סיביות, הנה מה שחיכיתם לו: הקוד לכתיבת קובץ מפת סיביות מאובייקט תמונה.

ייבא java.awt. *; ייבא java.io. *; ייבא java.awt.image. *; מחלקה ציבורית BMPFile מרחיב את הרכיב {// --- קבועים פרטיים פרטיים סטטיים סופיים int BITMAPFILEHEADER_SIZE = 14; פרטי סטטי סופי פרטי BITMAPINFOHEADER_SIZE = 40; // --- הצהרת משתנה פרטית // --- כותרת קובץ Bitmap פרטי bitmapFileHeader [] = בית חדש [14]; בתים פרטיים bfType [] = {'B', 'M'}; פרטי int bfSize = 0; פרטי int bfReserved1 = 0; פרטי int bfReserved2 = 0; פרטי int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- כותרת מידע על מפת סיביות בתים פרטיים bitmapInfoHeader [] = בתים חדשים [40]; פרטי int biSize = BITMAPINFOHEADER_SIZE; פרטי int biWidth = 0; פרטי int biHeight = 0; פרטי int biPlanes = 1; פרטי int biBitCount = 24; פרטי int biCompression = 0; פרטי int biSizeImage = 0x030000;פרטי int biXPelsPerMeter = 0x0; פרטי int biYPelsPerMeter = 0x0; פרטי int biClrUsed = 0; פרטי int biClrImportant = 0; // --- מפת סיביות של נתונים גולמיים מפת סיביות פרטית []; // --- קטע קבצים FileOutputStream fo פרטי; // --- בונה ברירת מחדל ציבורית BMPFile () {} public void saveBitmap (String parFilename, Image parImage, int parWidth, int parHeight) {try {fo = new FileOutputStream (parFilename); שמור (parImage, parWidth, parHeight); fo.close (); } לתפוס (Exception saveEx) {saveEx.printStackTrace (); }} / * * SaveMethod הוא השיטה העיקרית של התהליך. שיטה זו * תקרא לשיטת convertImage להמיר את תמונת הזיכרון למערך בתים; השיטה writeBitmapFileHeader יוצרת וכותבת * כותרת קובץ bitmap; writeBitmapInfoHeader יוצר את כותרת המידע *; ו- writeBitmap כותב את התמונה.* * / שמירת ריק ריק (תמונה parImage, int parWidth, int parHeight) {נסה {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } לתפוס (Exception saveEx) {saveEx.printStackTrace (); }} / * * convertImage ממיר את תמונת הזיכרון לפורמט מפת סיביות (BRG). * זה גם מחשב מידע עבור כותרת המידע על מפת סיביות. * * / private boolean convertImage (תמונה parImage, int parWidth, int parHeight) {int pad; bitmap = int int [parWidth * parHeight]; PixelGrabber pg = PixelGrabber חדש (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); נסה {pg.grabPixels (); } לתפוס (InterruptedException e) {e.printStackTrace (); החזר (שקר); } כרית = (4 - ((parWidth * 3)% 4)) * parHeight; biSizeImage = ((parWidth * parHeight) * 3) + כרית;bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight; להחזיר (נכון); } / * * writeBitmap ממיר את התמונה שהוחזרה מחוטף הפיקסלים לתבנית הנדרשת. זכרו: קווי סריקה הופכים * בקובץ מפת סיביות! * * כל קו סריקה חייב להיות מרופד לגבול שווה של 4 בתים. * / private void writeBitmap () {int size; ערך int; int j; int i; int rowCount; int rowIndex; int lastRowIndex; כרית int; int padCount; בתים rgb [] = בתים חדשים [3]; גודל = (biWidth * biHeight) - 1; כרית = 4 - ((biWidth * 3)% 4); אם (כרית == 4) // <==== כרית תיקון באגים = 0; // <==== שורת תיקון באגים Count = 1; padCount = 0; rowIndex = size - biWidth; lastRowIndex = rowIndex; נסה {עבור (j = 0; j> 8) & 0xFF); rgb [2] = (בתים) ((ערך >> 16) & 0xFF); fo.write (rgb);אם (rowCount == biWidth) {padCount + = pad; עבור (i = 1; i> 8) & 0x00FF); החזר (retValue); } / * * * intToDWord ממיר int למילה כפולה, כאשר ערך ה- return * נשמר במערך של 4 בתים. * * / בתים פרטיים [] intToDWord (int parValue) {byte retValue [] = בית חדש [4]; retValue [0] = (בתים) (parValue & 0x00FF); retValue [1] = (בתים) ((parValue >> 8) & 0x000000FF); retValue [2] = (בתים) ((parValue >> 16) & 0x000000FF); retValue [3] = (בתים) ((parValue >> 24) & 0x000000FF); החזר (retValue); }}0x00FF); retValue [1] = (בתים) ((parValue >> 8) & 0x000000FF); retValue [2] = (בתים) ((parValue >> 16) & 0x000000FF); retValue [3] = (בתים) ((parValue >> 24) & 0x000000FF); החזר (retValue); }}0x00FF); retValue [1] = (בתים) ((parValue >> 8) & 0x000000FF); retValue [2] = (בתים) ((parValue >> 16) & 0x000000FF); retValue [3] = (בתים) ((parValue >> 24) & 0x000000FF); החזר (retValue); }}

סיכום

זה כל מה שיש בזה. אני בטוח שתמצא מחלקה זו שימושית מאוד, שכן נכון ל- JDK 1.1.6 ג'אווה אינה תומכת בשמירת תמונות באף אחד מהפורמטים הפופולריים. JDK 1.2 יציע תמיכה ביצירת תמונות JPEG, אך לא תמיכה במפות סיביות. כך שהשיעור הזה עדיין ימלא פער ב- JDK 1.2.

אם אתה משחק עם השיעור הזה ומוצא דרכים לשפר אותו, הודע לי! הדואר האלקטרוני שלי מופיע למטה, יחד עם הביוגרפיה שלי.

ז'אן פייר דובה הוא יועץ Java עצמאי. הוא הקים את Infocom, שנרשם בשנת 1988. מאז, Infocom פיתחה כמה יישומים מותאמים אישית, החל בייצור, ניהול מסמכים וניהול בקנה מידה גדול של קווי חשמל. יש לו ניסיון רב בתכנות ב- C, Visual Basic, ולאחרונה בג'אווה, שהיא כיום השפה העיקרית בה משתמשת החברה שלו. אחד הפרויקטים האחרונים של Infocom הוא API של דיאגרמה שאמור להיות זמין במהדורת בטא בקרוב.

סיפור זה, "טיפ ג'אווה 60: שמירת קבצי מפת סיביות בג'אווה" פורסם במקור על ידי JavaWorld.