מבוא
WebP הוא פורמט תמונה שמשתמש ב-(i) בקידוד תמונות המפתח VP8 כדי לדחוס נתוני תמונה באופן איבוד נתונים או (ii) קידוד ללא אובדן מידע של WebP. האלה סכימות קידוד אמורות להיות יעילות יותר מפורמטים ישנים יותר, כגון JPEG, GIF ו-PNG. הוא מותאם להעברת תמונות מהירה ברשת ( לדוגמה, באתרים). לפורמט WebP יש מאפיינים זהים לפורמטים אחרים (פרופיל צבע, מטא-נתונים, אנימציה וכו'). במסמך הזה מתוארים המבנה והפרמטרים של קובץ WebP.
מאגר ה-WebP (כלומר מאגר ה-RIFF של WebP) מאפשר תמיכה בתכונות נוספות מעבר למקרה השימוש הבסיסי של WebP (כלומר, קובץ שמכיל תמונה אחת בקידוד כפריים מפתח של VP8). הקונטיינר של WebP מספק תמיכה נוספת באפשרויות הבאות:
דחיסה ללא אובדן נתונים: ניתן לדחוס תמונה ללא אובדן נתונים, באמצעות הפונקציה פורמט WebP Lossless.
מטא-נתונים: יכול להיות שהמטא-נתונים של התמונה מאוחסנים בקובץ תמונה שניתן להחלפה פורמט (Exif) או פורמט Extensible Metadata Platform (XMP).
שקיפות: לתמונה יכולה להיות שקיפות, כלומר ערוץ אלפא.
פרופיל צבע: יכול להיות שתמונה תכלול פרופיל ICC מוטמע, כפי שמתואר על ידי International Color Consortium.
אנימציה: תמונה יכולה לכלול כמה פריימים עם השהיות ביניהם והופך אותו לאנימציה.
מתן שמות
מומלץ להשתמש בסוגי ה-container הבאים כשמתייחסים ל-WebP:
שם הפורמט של הקונטיינר | WebP |
סיומת שם הקובץ | .webp |
סוג MIME | תמונה/WebP |
מזהה סוג אחיד (UTI) | org.webmproject.webp |
מונחים ויסודות
מילות המפתח "חייבים", "אסור", "נדרש", "לא", "לא", "צריך", 'לא מומלץ', 'מומלץ', 'לא מומלץ', 'מאי' ו'אופציונלי' כאן המסמך מפורש כפי שמתואר ב-BCP 14 RFC 2119 RFC 8174 מתי ורק כשהם מופיעים באותיות גדולות, כפי שמוצג כאן.
קובץ WebP מכיל תמונת סטילס (כלומר, מטריצה מקודדת של פיקסלים) או אנימציה. היא יכולה גם לכלול שקיפות מידע, פרופיל צבע ומטא-נתונים. אנחנו מתייחסים למטריצה של הפיקסלים בתור הקנבס של התמונה.
מספור הביטים בדיאגרמות מקטעי נתונים מתחיל ב-0
עבור הביט המשמעותי ביותר
('MSB 0'), כפי שמתואר ב-RFC 1166.
בהמשך מופיעים מונחים נוספים שמופיעים במסמך הזה:
- קריאה/כתיבה
- קוד שקורא קובצי WebP נקרא קורא, ואילו קוד כשכותבים אותו, הוא מופיע בתור כותב.
- uint16
- מספר שלם ללא סימן באורך 16 ביט, בסדר קטן-גדול (little-endian).
- uint24
- מספר שלם ב-24 סיביות, ב-little-endian, ללא סימן.
- uint32
- מספר שלם לא חתום מסוג 32-bit.
- FourCC
- קוד בן ארבעה תווים (FourCC) הוא uint32 שנוצר על ידי שרשור של ארבע תווי ASCII בסדר קטן מאוד. פירוש הדבר הוא 'אאא' (0x61616161) וגם 'AAAA' (0x41414141) נחשבים ל-FourCCs שונים.
- בסיס 1
- שדה של מספר שלם ללא סימן שמאחסן ערכים עם סטייה של
-1
. לדוגמה, בשדה כזה הערך 25 יישמר כ-24. - ChunkHeader('ABCD')
- משמש לתיאור הכותרת FourCC ו-Chunk size של מקטעי נתונים בודדים, כאשר 'ABCD' הוא ה-FourCC של המקטע. הגודל של הרכיב הזה הוא 8 בייטים.
פורמט קובץ RIFF
פורמט הקובץ WebP מבוסס על RIFF (פורמט הקובץ של החלפת משאבים) פורמט המסמך.
הרכיב הבסיסי של קובץ RIFF הוא מקטע. הוא מורכב מ:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Chunk FourCC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Chunk Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: Chunk Payload :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Lunk ארבעהCC: 32 סיביות
- קוד ASCII בן ארבעה תווים המשמש לזיהוי קטעים.
- גודל המקטע: 32 ביטים (uint32)
- הגודל של הקטע בבייטים, לא כולל את השדה הזה, מזהה הקטעים או המילוי.
- מטען ייעודי (payload) של מקטע: גודל מקטע בייטים
- מטען הנתונים. אם הערך של Chunk Size הוא מספר אי-זוגי, מתווסף ביייט אחד של מילוי – שחייב להיות
0
כדי לעמוד בתקן RIFF.
הערה: ב-RIFF יש מוסכמה שלפיה ארבע מקטעים לפי אותיות רישיות בלבד הם סטנדרטיים מקטעים שחלים על כל פורמט קובץ RIFF, ו-FourCCs ספציפיים לקובץ כתובים באותיות קטנות. קובצי WebP לא פועלים לפי המוסכמה הזו.
כותרת של קובץ WebP
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'R' | 'I' | 'F' | 'F' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'W' | 'E' | 'B' | 'P' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- 'RIFF': 32 סיביות
- תווי ASCII 'R', 'I', 'F', 'F'.
- גודל הקובץ: 32 סיביות (uint32)
- גודל הקובץ בבייטים, החל מהיסט 8. הערך המקסימלי של בשדה הזה הוא 2^32 פחות 10 בייט, ולכן גודל הקובץ כולו הוא הכי הרבה GiB 4 פחות 2 בייטים.
- 'WEBP': 32 סיביות
- תווי ASCII: 'W', 'E', 'B', 'P'.
קובץ WebP חייב להתחיל בכותרת RIFF עם ה-FourCC 'WEBP'. גודל הקובץ
בכותרת הוא הגודל הכולל של המקטעים שאחריהם 4
בייטים עבור
'WEBP' ארבעה כתוביות. אסור לכלול בקובץ נתונים אחרי הנתונים
מצוין באמצעות גודל הקובץ. הקוראים יכולים לנתח קבצים כאלה, תוך התעלמות מהסימנים
. מאחר שגודל כל מקטע הוא שווה, הגודל שניתן על ידי הכותרת RIFF הוא
אפילו יותר. התוכן של קטעים נפרדים מתואר בקטעים הבאים.
פורמט קובץ פשוט (אובדן נתונים)
יש להשתמש בפריסה הזו אם התמונה מחייבת קידוד איטי ולא דורשים שקיפות או תכונות מתקדמות אחרות שמסופקות על ידי הפורמט המורחב. קבצים בפריסה הזו הם קטנים יותר ונתמכים על ידי תוכנות ישנות יותר.
פורמט קובץ WebP פשוט (עם אובדן נתונים):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| WebP file header (12 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: 'VP8 ' Chunk :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'VP8' מקטע:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8 ') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: VP8 data :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- נתוני VP8: Chunk Size בייטים
- נתוני מקור ביט של VP8.
שימו לב שהתו הרביעי ב-'VP8' FoCC הוא רווח ASCII (0x20).
מפרט הפורמט של סיביות VP8 מתואר בפורמט נתונים VP8 מדריך לפענוח הקוד. חשוב לזכור שכותרת הפריים של VP8 מכילה את רוחב הפריים ואת הגובה שלו. ההנחה היא שאלה רוחב הגובה של הקנבס.
במפרט של VP8 מתואר איך לפענח את התמונה לפורמט Y'CbCr. כדי לבצע המרה ל-RGB, צריך להשתמש בהמלצה BT.601. יכול להיות שאפליקציות ישתמשו בשיטת המרה אחרת, אבל התוצאות החזוניות עשויות להיות שונות בין מקודדים.
פורמט קובץ פשוט (ללא אובדן)
הערה: יכול להיות שקוראים ישנים לא תומכים בקבצים בפורמט Lossless.
יש להשתמש בפריסה הזו אם לתמונה נדרש קידוד Lossless (עם ערוץ שקיפות אופציונלי) ולא מחייב שימוש בתכונות מתקדמות בפורמט המורחב.
פורמט קובץ פשוט WebP (lossless):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| WebP file header (12 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: 'VP8L' Chunk :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
מקטע 'VP8L':
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8L') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: VP8L data :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- נתוני VP8L: בייטים של גודל מקטע
- נתוני bitstream של VP8L.
המפרט הנוכחי של מקור הנתונים (bitstream) של VP8L זמין במאמר פורמט מקור הנתונים (bitstream) ללא אובדן נתונים של WebP. שימו לב שהכותרת VP8L מכיל את הרוחב והגובה של התמונה מסוג VP8L. ההנחה היא שאלה רוחב הגובה של הקנבס.
פורמט קובץ מורחב
הערה: יכול להיות שקוראים ישנים יותר לא יתמכו בקבצים בפורמט המורחב.
קובץ בפורמט מורחב כולל את הרכיבים הבאים:
מקטע 'VP8X' עם מידע על התכונות שבהן נעשה שימוש בקובץ.
מאפיין ICCP אופציונלי מקטע עם פרופיל צבעים.
מקטע 'ANIM' אופציונלי עם נתוני בקרה של אנימציה.
נתוני תמונה.
מקטע 'EXIF' אופציונלי עם מטא-נתונים של Exif.
מקטע 'XMP' אופציונלי עם מטא-נתונים של XMP.
רשימה אופציונלית של קטעי קוד לא מוכרים.
בתמונת סטילס, נתוני התמונה מורכבים ממסגרת אחת, עד:
קטע משנה של אלפא אופציונלי.
בתמונה מונפשת, נתוני התמונה מורכבים מכמה פריימים. פרטים נוספים על פריימים זמינים בקטע אנימציה.
כל המקטעים שדרושים לשחזור ולתיקון צבע הם 'VP8X' 'ICCP', 'ANIM', 'ANMF', 'ALPH', 'VP8' ו-'VP8L', חייבים להופיע בסדר שתואר קודם לכן. הקוראים אמורים להיכשל כשקטעים שנדרשים לשחזור ולתיקון הצבע לא מסודרים.
ייתכן שמטא-נתונים ומקטעים לא ידועים יופיעו מחוץ הזמנה.
הסיבה: הקטעים הנדרשים לשחזור צריכים להופיע קודם בקובץ כדי לאפשר לקורא להתחיל לפענח תמונה לפני שהוא מקבל את כל הנתונים. אפליקציה יכולה להפיק תועלת משינוי הסדר של המטא-נתונים מקטעי נתונים מותאמים אישית שיתאימו להטמעה.
כותרת של קובץ WebP מורחב:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| WebP file header (12 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8X') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv|I|L|E|X|A|R| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Canvas Width Minus One | ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... Canvas Height Minus One |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- שמור (Rsv): 2 סיביות
- חייב להיות
0
. הקוראים חייבים להתעלם מהשדה הזה. - פרופיל ICC (I): ביט אחד
- הערך מוגדר אם הקובץ מכיל מקטע ICCP.
- אלפא (L): ביט אחד
- הגדרה אם מסגרות בתמונה מכילות מידע על שקיפות ('אלפא').
- מטא-נתונים של תצוגת Exif (E): ביט אחד
- מגדירים אם הקובץ מכיל מטא-נתונים של תצוגת Exif.
- מטא-נתונים של XMP (X): ביט אחד
- מגדירים אם הקובץ מכיל מטא-נתונים של XMP.
- אנימציה (A): 1 ביט
- הגדרה של תמונה עם אנימציה. נתונים ב-ANIM ו-ANMF הגושים צריכים להיות שמשמש לשליטה באנימציה.
- שמור (R): ביט אחד
- חייב להיות
0
. הקוראים חייבים להתעלם מהשדה הזה. - שמור: 24 סיביות
- חייב להיות
0
. הקוראים חייבים להתעלם מהשדה הזה. - רוחב קנבס מינוס אחד: 24 סיביות רוחב
- שמתחיל ב-1 של לוח הציור בפיקסלים.
רוחב הקנבס בפועל הוא
1 + Canvas Width Minus One
. - גובה ההדפסה על קנבס מינוס אחד: 24 ביט
- גובה מבוסס-1 של אזור העריכה בפיקסלים.
גובה הקנבס בפועל הוא
1 + Canvas Height Minus One
.
המוצר של רוחב הקנבס וגובה הקנבס צריך להיות 2^32 - 1
לכל היותר.
יכול להיות שבמפרטים עתידיים יתווספו עוד שדות. יש להתעלם משדות לא ידועים.
Animation
אנימציה נשלטת על ידי קטעי קוד מסוג 'ANIM' ו-'ANMF'.
'ANIM' מקטע:
בתמונה מונפשת, המקטע הזה מכיל את הפרמטרים הגלובליים של אנימציה.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ANIM') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Background Color |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Loop Count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- צבע הרקע: 32 ביט (uint32)
- צבע ברירת המחדל של צבע הרקע של קנבס ב[כחול, ירוק, אדום, אלפא]
סדר בייטים. הצבע הזה עשוי להופיע כדי למלא את השטח הלא מנוצל של הקנבס
סביב המסגרות, וגם את הפיקסלים השקופים של הפריים הראשון.
צבע הרקע משמש גם כאשר שיטת ההשלכה היא
1
.
הערה:
צבע הרקע יכול להכיל ערך אלפא לא אטום, גם אם הדגל Alpha בקטע 'VP8X' לא מוגדר.
אפליקציות צפייה צריכות להתייחס לערך של צבע הרקע כנקודת עזר, ולא חייבות להשתמש בו.
הלוח נמחק בתחילת כל לולאה. יכול להיות שצבע הרקע יהיה להשגת התוצר הזה.
- מספר הלולאות: 16 ביט (uint16)
- מספר הפעמים להפעיל את האנימציה בלופ. אם הערך הוא
0
, המשמעות היא היא אין סוף.
המקטע הזה חייב להופיע אם הדגל אנימציה מופיע לצד 'VP8X' המקטע הוגדר. אם הדגל אנימציה לא מוגדר והמקטע הזה קיים, הוא חייב להיות המערכת מתעלמת ממנו.
מקטע 'ANMF':
בתמונות אנימציה, הקטע הזה מכיל מידע על פריים יחיד. אם הדגל אנימציה לא מוגדר, המקטע הזה לא אמור להופיע.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ANMF') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame X | ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... Frame Y | Frame Width Minus One ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... | Frame Height Minus One |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame Duration | Reserved |B|D|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: Frame Data :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- מסגרת X: 24 ביט (uint24)
- הקואורדינטה X של הפינה השמאלית העליונה של המסגרת היא
Frame X * 2
. - מסגרת Y: 24 ביטים (uint24)
- הקואורדינטה Y של הפינה השמאלית העליונה של המסגרת היא
Frame Y * 2
. - רוחב המסגרת מינוס אחד: 24 ביט (uint24)
- רוחב המסגרת שמתחיל ב-1.
רוחב המסגרת הוא
1 + Frame Width Minus One
. - גובה הפריים מינוס אחד: 24 ביט (uint24)
- גובה הפריים מבוסס 1.
גובה המסגרת הוא
1 + Frame Height Minus One
. - משך המסגרת: 24 ביטים (uint24)
- הזמן שצריך להמתין לפני הצגת הפריים הבא, ביחידות של אלפית שנייה. שימו לב שהפרשנות של משך המסגרת של 0 (ולרוב <= 10) היא מוגדר על ידי ההטמעה. כלים ודפדפנים רבים מקצים ערך מינימלי בדומה ל-GIF.
- שמור: 6 סיביות
- חייב להיות
0
. הקוראים חייבים להתעלם מהשדה הזה. - שיטת מיזוג (B): ביט אחד
מציין את מידת המיזוג של פיקסלים שקופים בפריים הנוכחי עם פיקסלים תואמים בקנבס הקודם:
0
: שימוש במיזוג אלפא. אחרי שמסירים את המסגרת הקודמת, מבצעים עיבוד (render) של המסגרת הנוכחית על גבי הלוח באמצעות מיזוג אלפא (ראו בהמשך). אם למסגרת הנוכחית אין ערוץ אלפא, נניח שערך האלפא הוא 255, החלפת למעשה את המלבן.1
: לא למזג. אחרי שהמסגרת הקודמת נפטרת, צריך לעבד את המסגרת הנוכחית באזור העריכה על ידי החלפת המלבן שמכוסה על ידי המסגרת הנוכחית.
- שיטת השלכה (D): ביט אחד
מציין איך הפריים הנוכחי יטופל אחרי שהוא יוצג (לפני עיבוד הפריימים הבא) על הלוח:
0
: אין להשליך. משאירים את הקנבס כמו שהוא.1
: Dispose לצבע הרקע. ממלאים את המלבן בבד הציור שמכוסה על ידי הפריים הנוכחי בצבע הרקע שצוין בקטע 'ANIM'.
הערות:
ביטול הפריים חל רק על ריבוע הפריים, כלומר על הריבוע שמוגדר על ידי Frame X, Frame Y, frame width ו-frame height. הוא יכול לכסות את כל שטח ההדפסה או לא.
מיזוג אלפא:
מאחר שכל אחד מהערוצים R, G, B ו-A הוא באורך 8 ביט, וערוצי ה-RGB לא מוגדרים כמכפילים באלפא, הנוסחה למיזוג של 'dst' עם 'src' היא:
blend.A = src.A + dst.A * (1 - src.A / 255) if blend.A = 0 then blend.RGB = 0 else blend.RGB = (src.RGB * src.A + dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
רצוי לבצע מיזוג אלפא במרחב צבע לינארי, תוך התחשבות בפרופיל הצבע של התמונה. אם פרופיל הצבעים הוא לא קיים, ויש להניח שה-RGB סטנדרטי (sRGB). (שימו לב שגם sRGB צריך לעבור ליניאריזציה בגלל ערך גמא של כ-2.2).
- נתוני הפריים: Chunk Size בייטים –
16
כולל:
קטע משנה של אלפא אופציונלי לפריים.
קטע משנה של מקור נתונים (bitstream) של המסגרת.
רשימה אופציונלית של קטעי קוד לא מוכרים.
הערה: הפורמט 'ANMF' מטען ייעודי (payload), נתוני פריים, מורכב מקטעים מרופדים, כפי שמתואר על ידי פורמט הקובץ RIFF.
אלפא
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ALPH') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv| P | F | C | Alpha Bitstream... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- שמור (Rsv): 2 סיביות
- חייב להיות
0
. הקוראים חייבים להתעלם מהשדה הזה. - עיבוד מראש (P): 2 ביטים
הביטים האינפורמטיביים האלה משמשים כדי לסמן את העיבוד מראש בוצעה במהלך הדחיסה. המפענח יכול להשתמש במידע הזה לדוגמה, לשנות את הערכים או להחליק את ההדרגתיות לפני התצוגה.
0
: ללא עיבוד מראש.1
: הפחתת רמה.
לא חובה להשתמש במידע הזה בקודקים בדרך מסוימת.
- שיטת סינון (F): 2 ביט
שיטות הסינון הבאות מתוארות כך:
0
: ללא.1
: מסנן אופקי.2
: מסנן אנכי.3
: מסנן הדרגתי.
הסינון מתבצע לכל פיקסל באמצעות החישובים הבאים.
נניח שערכי האלפא שמקיפים את המיקום הנוכחי של X
מסומנים בתווית:
C | B |
---+---+
A | X |
אנחנו מנסים לחשב את ערך האלפא במיקום X
. בשלב הראשון מתבצע חיזוי בהתאם לשיטת הסינון:
- שיטה
0
: חיזוי = 0 - שיטה
1
: חיזוי = A - שיטה
2
: חיזוי = B - שיטה
3
: predictor = clip(A + B - C)
כאשר clip(v)
שווה ל-:
- 0 אם v < 0,
- 255 אם v > 255, או
- v אחרת
הערך הסופי נגזר מהוספת הערך הלא דחוס X
לחזאי, ושימוש באריתמטיקה של modulo-256 כדי לעטוף את הטווח [256..511] בטווח [0..255]:
alpha = (predictor + X) % 256
יש מקרים מיוחדים למיקומי הפיקסלים הימני והעליון. עבור לדוגמה, הערך בפינה הימנית העליונה במיקום (0, 0) משתמש ב-0 כערך החיזוי. אחרת:
- בשיטות סינון אופקי או הדרגתי, הפיקסלים הימניים ביותר הם המיקום (0, y) נחזה על סמך המיקום (0, y-1) שלמעלה.
- בשיטות סינון אנכיות או הדרגתיות, הפיקסלים העליונים ביותר מוגדרים המיקום (x, 0) חזוי על סמך המיקום (x-1, 0) שבצד ימין.
- שיטת דחיסה (C): 2 ביטים
שיטת הדחיסה שבה נעשה שימוש:
0
: ללא דחיסת נתונים.1
: דחוס באמצעות פורמט WebP Lossless.
- Alphabitstream של אלפא: גודל מקטע בייטים –
1
מקודד של זרם ביטים של אלפא.
המקטע האופציונלי הזה מכיל נתוני אלפא מקודדים למסגרת הזו. מסגרת שמכיל את הערך 'VP8L' המקטע לא אמור להכיל את המקטע הזה.
הסיבה: פרטי השקיפות כבר כלולים ב-Chunk 'VP8L'.
נתוני ערוץ האלפא מאוחסנים כנתונים גולמיים לא דחוסים (כאשר שיטת הדחיסה היא '0') או דחוסה באמצעות הפורמט ללא אובדן מידע (כששיטת הדחיסה היא '1').
נתונים גולמיים: מורכב מרצף של בייטים באורך = רוחב * גובה, שמכיל את כל ערכי השקיפות של 8 ביט לפי סדר הסריקה.
דחיסת פורמט ללא אובדן: הרצף של הבייטים הוא דחוס image-stream (כפי שמתואר במאמר "WebP Lossless Bitstream Format") של מידות מרומזות, רוחב x גובה. כלומר, ה-image-stream הזה לא מכיל כותרות שמתארות את מידות התמונה.
הסיבה: המאפיינים כבר ידועים ממקורות אחרים, ולכן אחסון שלהם שוב יהיה מיותר וסביר להניח שיגרום לשגיאות.
אחרי שקודק של מקור התמונה לערכים של צבעים (אלפא, אדום, ירוק, כחול – ARGB), בהתאם לתהליך שמתואר במפרט הפורמט ללא אובדן נתונים, צריך לחלץ את פרטי השקיפות מהערוץ הירוק של ה-quadruplet ARGB.
הסיבה: לערוץ הירוק מותר לבצע שלבי טרנספורמציה נוספים במפרט – בניגוד לשאר הערוצים – שיכולים לשפר את דחיסת הנתונים.
Bitstream (VP8/VP8L)
הרצף הזה מכיל נתוני זרם ביטים דחוסים של פריים יחיד.
מקטע Bitstream יכול להיות (i) 'VP8' קבוצת משתמשים, באמצעות 'VP8' (שימו לב ל רווח משמעותי בתווים רביעיים) כ-FourCC, או (ii) 'VP8L' Chenk, באמצעות 'VP8L' כ-FourCC.
הפורמטים של 'VP8' ו-VP8L מקטעי נתונים כפי שמתואר במקטעים פורמט קובץ פשוט (לוסי) ו-Simple File Format (Lossless) בהתאמה.
פרופיל צבע
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ICCP') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: Color Profile :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- פרופיל צבעים: גודל מקטע בייטים
- פרופיל ICC.
המקטע הזה חייב להופיע לפני נתוני התמונה.
אמורה להיות מקטע אחד כזה לכל היותר. אם יש עוד קטעים כאלה, הקוראים יכולים להתעלם מכל הקטעים חוץ מהקטע הראשון. פרטים נוספים זמינים במפרט ICC.
אם המקטע הזה לא נמצא, ההנחה היא שיש להשתמש ב-sRGB.
מטא-נתונים
ניתן לאחסן מטא נתונים ב-EXIF או 'XMP' גושים.
צריך להיות מקסימום מקטע אחד מכל סוג ('EXIF' ו-'XMP'). אם יש יותר מקטעים כאלה, הקוראים עשויים להתעלם מכל המקטעים מלבד הראשון.
המקטעים מוגדרים כך:
'EXIF' מקטע:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('EXIF') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: Exif Metadata :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- מטא-נתונים של תצוגת Exif: גודל מקטע בייטים
- המטא-נתונים של התמונה בפורמט Exif.
מקטע 'XMP':
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('XMP ') |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: XMP Metadata :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- מטא-נתונים של XMP: בייטים בגודל מקטע
- מטא-נתונים של תמונה בפורמט XMP.
שימו לב שהתו הרביעי ב-'XMP ' FoCC הוא רווח ASCII (0x20).
הנחיות נוספות לגבי טיפול במטא-נתונים זמינות בקטע "הנחיות לטיפול במטא-נתונים" של קבוצת העבודה בנושא מטא-נתונים".
גושים לא ידועים
מקטע RIFF (מתואר בקטע RIFF File Format) שה-FourCC שלו שונה מכל המקטעים שמתוארים במסמך הזה, נחשב למקטע לא ידוע.
הסיבה: האפשרות לאפשר קטעי נתונים לא מוכרים מאפשרת להרחיב את הפורמט בעתיד, וגם לאחסן נתונים ספציפיים לאפליקציה.
קובץ יכול להכיל קטעים לא מוכרים:
- בסוף הקובץ, כפי שמתואר בקובץ ה-WebP המורחב, כותרת, או
- בסוף 'ANMF' מקטעי נתונים, כפי שמתואר הקטע אנימציה.
הקוראים צריכים להתעלם ממקטעי הנתונים האלה. הכותבים צריכים לשמור אותם ההזמנה המקורית (אלא אם הם מתכוונים לשנות את המקטעים האלה באופן ספציפי).
הרכבת קנבס ממסגרות
כאן נספק סקירה כללית של האופן שבו קורא חייב להרכיב קנבס במקרה של תמונה מונפשת.
התהליך מתחיל ביצירת קנבס באמצעות המימדים שצוינו
'VP8X' מקטע, רוחב של Canvas Width Minus One + 1
פיקסלים על גובה של Canvas Height Minus
One + 1
פיקסלים. השדה Loop Count
מהקטע ANIM קובע כמה פעמים תהליך האנימציה יחזור על עצמו. הערך הזה הוא Loop Count - 1
עבור ערכים של Loop Count
שאינם אפס, או אינסוף אם הערך של Loop Count
הוא אפס.
בתחילת כל חזרה של לולאה, אזור העריכה ממולא באמצעות צבע רקע מ-'ANIM' מקטע נתונים או צבע שמוגדר על ידי האפליקציה.
קטעי 'ANMF' מכילים פריימים נפרדים שמופיעים בסדר התצוגה. לפני עיבוד הגרפי של כל מסגרת, המערכת מחילה את Disposal method
של המסגרת הקודמת.
העיבוד של המסגרת המקודדת מתחיל בקואורדינטות הקרטזיות (2 *
Frame X
, 2 * Frame Y
), תוך שימוש בפינה הימנית העליונה של הלוח כמקור.
Frame Width Minus One + 1
פיקסלים ברוחב ו-Frame Height Minus One + 1
פיקסלים
גובה נעבדים על קנבס באמצעות Blending method
.
הקנבס מוצג למשך Frame Duration
אלפיות השנייה. הבעיה נמשכת עד
כל הפריימים שניתנו על ידי 'ANMF' הוצגו מקטעים. לאחר מכן מתחילה חזרה חדשה של הלולאה, או שהלוח נשאר במצב הסופי שלו אם כל החזרות הסתיימו.
הקוד המדומה הבא מדגים את תהליך הרינדור. הסימון המשמעות של VP8X.field היא השדה בתוך 'VP8X' מקטע עם תיאור זהה.
VP8X.flags.hasAnimation MUST be TRUE
canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
background color ANIM.background_color or
application-defined color.
loop_count ← ANIM.loopCount
dispose_method ← Dispose to background color
if loop_count == 0:
loop_count = ∞
frame_params ← nil
next chunk in image_data is ANMF MUST be TRUE
for loop = 0..loop_count - 1
clear canvas to ANIM.background_color or application-defined color
until eof or non-ANMF chunk
frame_params.frameX = Frame X
frame_params.frameY = Frame Y
frame_params.frameWidth = Frame Width Minus One + 1
frame_params.frameHeight = Frame Height Minus One + 1
frame_params.frameDuration = Frame Duration
frame_right = frame_params.frameX + frame_params.frameWidth
frame_bottom = frame_params.frameY + frame_params.frameHeight
VP8X.canvasWidth >= frame_right MUST be TRUE
VP8X.canvasHeight >= frame_bottom MUST be TRUE
for subchunk in 'Frame Data':
if subchunk.tag == "ALPH":
alpha subchunks not found in 'Frame Data' earlier MUST be
TRUE
frame_params.alpha = alpha_data
else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
bitstream subchunks not found in 'Frame Data' earlier MUST
be TRUE
frame_params.bitstream = bitstream_data
apply dispose_method.
render frame with frame_params.alpha and frame_params.bitstream
on canvas with top-left corner at (frame_params.frameX,
frame_params.frameY), using Blending method
frame_params.blendingMethod.
canvas contains the decoded image.
Show the contents of the canvas for
frame_params.frameDuration * 1 ms.
dispose_method = frame_params.disposeMethod
פריסות קבצים לדוגמה
תמונה עם אלפא בקידוד עם אובדן נתונים עשויה להיראות כך:
RIFF/WEBP
+- VP8X (descriptions of features used)
+- ALPH (alpha bitstream)
+- VP8 (bitstream)
תמונה בקידוד ללא אובדן נתונים עשויה להיראות כך:
RIFF/WEBP
+- VP8X (descriptions of features used)
+- VP8L (lossless bitstream)
+- XYZW (unknown chunk)
תמונה ללא אובדן מידע עם פרופיל ICC ומטא-נתונים של XMP עשויה ייראה כך:
RIFF/WEBP
+- VP8X (descriptions of features used)
+- ICCP (color profile)
+- VP8L (lossless bitstream)
+- XMP (metadata)
תמונה מונפשת עם מטא-נתונים של תצוגת Exif עשויה להיראות כך:
RIFF/WEBP
+- VP8X (descriptions of features used)
+- ANIM (global animation parameters)
+- ANMF (frame1 parameters + data)
+- ANMF (frame2 parameters + data)
+- ANMF (frame3 parameters + data)
+- ANMF (frame4 parameters + data)
+- EXIF (metadata)