מבוא
WebP הוא פורמט תמונה שמשתמש (1) בקידוד של VP8 של מסגרת מפתח כדי לדחוס נתוני תמונה באופן 'lossy' או (2) בקידוד WebP ללא אובדן נתונים. שיטות הקידוד האלה אמורות להפוך את התהליך ליעיל יותר מאשר בפורמטים ישנים יותר, כמו JPEG, GIF ו-PNG. הוא מותאם להעברה מהירה של תמונות ברשת (לדוגמה, לאתרים). לפורמט WebP יש מאפיינים זהים לפורמטים אחרים (פרופיל צבע, מטא-נתונים, אנימציה וכו'). במסמך הזה מתוארים המבנה והפרמטרים של קובץ WebP.
מאגר ה-WebP (כלומר מאגר ה-RIFF ל-WebP) מאפשר תמיכה בתכונות נוספות מעבר למקרה השימוש הבסיסי של WebP (כלומר קובץ שמכיל תמונה אחת בקידוד כפריים מפתח של VP8). הקונטיינר של WebP מספק תמיכה נוספת באפשרויות הבאות:
דחיסת lossless: אפשר לדחוס תמונה ללא אובדן נתונים באמצעות הפורמט WebP Lossless.
מטא-נתונים: יכול להיות שבתמונה יש מטא-נתונים שמאוחסנים בפורמט של קובץ תמונה להחלפה (Exif) או בפורמט של פלטפורמת מטא-נתונים נרחבת (XMP).
שקיפות: תמונה יכולה להיות שקופה, כלומר לכלול ערוץ אלפא.
פרופיל צבע: תמונה עשויה לכלול פרופיל ICC מוטמע כפי שמתואר על ידי International Color Consortium.
אנימציה: תמונה יכולה לכלול כמה פריימים עם השהיות, מה שהופך אותה לאנימציה.
מתן שמות
מומלץ להשתמש בסוגי ה-container הבאים כשמתייחסים ל-WebP:
שם הפורמט של הקונטיינר | WebP |
סיומת שם קובץ | .webp |
סוג MIME | image/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 בסדר little-endian. המשמעות היא שהשדות 'aaaa' (0x61616161) ו-'AAAA' (0x41414141) נחשבים ל-FourCCs שונים.
- בסיס 1
- שדה של מספר שלם ללא סימן שמאחסן ערכים עם סטייה של
-1
. לדוגמה, בשדה כזה הערך 25 יישמר כ-24. - ChunkHeader('ABCD')
- משמש לתיאור הכותרות FourCC ו-Chunk Size של קטעי קוד בודדים, כאשר 'ABCD' הוא ה-FourCC של קטע הקוד. הגודל של הרכיב הזה הוא 8 בייטים.
פורמט קובץ RIFF
פורמט הקובץ WebP מבוסס על פורמט המסמך RIFF (Resource Interchange File Format).
הרכיב הבסיסי של קובץ 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 :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Chunk FourCC: 32 ביט
- קוד ASCII בן ארבעה תווים המשמש לזיהוי מקטעים.
- גודל קבוצת הנתונים: 32 סיביות (uint32)
- הגודל של הקטע בבייטים, לא כולל את השדה הזה, מזהה הקטעים או המילוי.
- המטען הייעודי של הקבוצה: Chunk Size בייטים
- מטען הנתונים. אם הערך של Chunk Size הוא מספר אי-זוגי, מתווסף ביייט אחד של מילוי – שחייב להיות
0
כדי לעמוד בתקן RIFF.
הערה: ב-RIFF יש הסכמה לפיה קטעי FourCC באותיות רישיות גדולות הם קטעים רגילים שחלים על כל פורמט קובץ RIFF, ואילו קטעי FourCC ספציפיים לפורמט קובץ הם באותיות רישיות קטנות. קובצי 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 בייטים, ולכן גודל הקובץ כולו הוא לכל היותר 4GB פחות 2 בייטים.
- 'WEBP': 32 סיביות
- תווי ASCII 'W', 'E', 'B', 'P'.
קובץ WebP חייב להתחיל בכותרת RIFF ב-F4CC 'WEBP'. גודל הקובץ בכותרת הוא הגודל הכולל של המקטעים הבאים, בתוספת 4
בייטים עבור ה-FourCC של 'WEBP'. אסור שהקובץ יכיל נתונים אחרי הנתונים שצוינו בגודל הקובץ. יכול להיות שהקוראים ינתחו קבצים כאלה ומתעלמים מהנתונים הסופיים. מכיוון שהגודל של כל מקטע הוא מספר זוגי, גם הגודל שמצוין בכותרת RIFF הוא מספר זוגי. התוכן של קטעים נפרדים מתואר בקטעים הבאים.
פורמט קובץ פשוט (אובדן נתונים)
צריך להשתמש בפריסה הזו אם התמונה מחייבת קידוד lossy ולא מחייבת שקיפות או תכונות מתקדמות אחרות שמסופקות על ידי הפורמט המורחב. קבצים בפריסה הזו קטנים יותר ותוכנות ישנות יותר תומכות בהם.
פורמט קובץ 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.
שימו לב שהתו הרביעי ב-FourCC של 'VP8 ' הוא רווח ASCII (0x20).
מפרט הפורמט של מקור הנתונים ב-VP8 מתואר במאמר מדריך לפורמט הנתונים ולפענוח של VP8. חשוב לזכור שכותרת הפריים של VP8 מכילה את רוחב הפריים ואת הגובה שלו. ההנחה היא שאלה רוחב הגובה של הקנבס.
במפרט של VP8 מתואר איך לפענח את התמונה לפורמט Y'CbCr. כדי להמיר ל-RGB, צריך להשתמש ב-Recommendation BT.601. יכול להיות שאפליקציות ישתמשו בשיטת המרה אחרת, אבל התוצאות החזוניות עשויות להיות שונות בין מקודדים.
פורמט קובץ פשוט (ללא אובדן נתונים)
הערה: יכול להיות שקוראים ישנים יותר לא תומכים בקבצים בפורמט lossless.
כדאי להשתמש בפריסה הזו אם התמונה דורשת קידוד ללא אובדן נתונים (עם ערוץ שקוף אופציונלי) ואין צורך בתכונות המתקדמות שסופקו על ידי הפורמט המורחב.
פורמט קובץ 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) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: '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: Chunk Size בייטים
- נתוני 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): ביט אחד
- הגדרה של תמונה מונפשת. כדי לשלוט באנימציה, צריך להשתמש בנתונים בקטעי הקוד '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
, המשמעות היא ללא הגבלת זמן.
הקטע הזה חייב להופיע אם הסימון Animation מוגדר בקטע 'VP8X'. אם הדגל Animation לא מוגדר ויש את המקטע הזה, צריך להתעלם ממנו.
מקטע '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)
- משך הזמן להמתנה לפני הצגת המסגרת הבאה, ביחידות של אלפית שנייה. שימו לב שהפירוש של Frame Duration ש-0 (ולרוב <= 10) מוגדר על ידי ההטמעה. בכלים ובדפדפנים רבים מקצים משך זמן מינימלי שדומה ל-GIF.
- שמורים: 6 ביטים
- חייב להיות
0
. הקוראים חייבים להתעלם מהשדה הזה. - שיטת המיזוג (B): 1 ביט
מציינת את מידת המיזוג של פיקסלים שקופים של המסגרת הנוכחית עם פיקסלים תואמים של אזור העריכה הקודם:
0
: שימוש בשילוב אלפא. אחרי שמסירים את המסגרת הקודמת, מבצעים עיבוד (רנדור) של המסגרת הנוכחית על גבי הלוח באמצעות מיזוג אלפא (ראו בהמשך). אם לפריים הנוכחי אין ערוץ אלפא, נניח שערך האלפא הוא 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
מורכב מ:
תת-מקטע אלפא אופציונלי למסגרת.
תת-מקטע ביטstream של הפריים.
רשימה אופציונלית של מקטעים לא ידועים.
הערה: נתוני המסגרת, מטען העבודה של ANMF, מורכבים מקטעים מאוחסים נפרדים, כפי שמתואר בפורמט הקובץ 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 ביטים
הביטים האינפורמטיביים האלה משמשים כדי לסמן את העיבוד מראש שבוצעו במהלך הדחיסה. המקודד יכול להשתמש במידע הזה, למשל, כדי לבצע דיטיר (dither) של הערכים או להחליק את העקומות לפני הצגה.
0
: ללא עיבוד מקדים.1
: הפחתת רמה.
לא חובה להשתמש במידע הזה בקודקים בדרך מסוימת.
- שיטת סינון (F): 2 ביטים
שיטות הסינון שבהן נעשה שימוש מתוארות באופן הבא:
0
: None.1
: מסנן אופקי.2
: מסנן 'תחום עניין'.3
: מסנן גוון מדורג.
הסינון מתבצע לכל פיקסל באמצעות החישובים הבאים.
נניח שערכי האלפא שמקיפים את המיקום הנוכחי של X
מסומנים בתווית:
C | B |
---+---+
A | X |
אנחנו רוצים לחשב את ערך האלפא במיקום X
. בשלב הראשון מתבצע חיזוי בהתאם לשיטת הסינון:
- שיטה
0
: predictor = 0 - שיטה
1
: predictor = A - שיטה
2
: predictor = 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.
- מקור נתונים אלפא: Chunk Size בייטים –
1
מקודד של זרם ביטים של אלפא.
הקטע האופציונלי הזה מכיל נתוני אלפא מקודדים של המסגרת הזו. פריים שמכיל מקטע 'VP8L' לא אמור להכיל את המקטע הזה.
הסיבה: פרטי השקיפות כבר כלולים ב-Chunk 'VP8L'.
נתוני הערוץ אלפא מאוחסנים כנתונים גולמיים לא דחוסים (כששיטת הדחיסה היא '0') או דחוסים בפורמט ללא איבוד נתונים (כששיטת הדחיסה היא '1').
נתונים גולמיים: מורכב מרצף של בייטים באורך = רוחב * גובה, שמכיל את כל ערכי השקיפות של 8 ביט לפי סדר הסריקה.
דחיסת פורמט ללא אובדן נתונים: רצף הבייטים הוא מקור תמונות דחוס (כפי שמתואר בקטע פורמט מקור ביט ללא אובדן נתונים של WebP) של מידות מרומזות: רוחב x גובה. כלומר, ה-image-stream הזה לא מכיל כותרות שמתארות את מידות התמונה.
הסיבה: המאפיינים כבר ידועים ממקורות אחרים, ולכן אחסון שלהם שוב יהיה מיותר וסביר להניח שיגרום לשגיאות.
אחרי שהקוד של מקור התמונה מפוענח לערכים של צבעים (אלפא, אדום, ירוק, כחול, ARGB), בהתאם לתהליך שמתואר במפרט של הפורמט ללא אובדן נתונים, צריך לחלץ את פרטי השקיפות מהערוץ הירוק של ה-quadruplet ARGB.
הסיבה: לערוץ הירוק מותר לבצע שלבי טרנספורמציה נוספים במפרט – בניגוד לשאר הערוצים – שיכולים לשפר את דחיסת הנתונים.
נתיב נתונים (VP8/VP8L)
הרצף הזה מכיל נתוני זרם ביטים דחוסים של פריים יחיד.
מקטע של מקור נתונים יכול להיות (1) מקטע מסוג 'VP8 ', עם 'VP8 ' (שימו לב למרווח המשמעותי בתו הרביעי) כ-FourCC, או (2) מקטע מסוג 'VP8L', עם 'VP8L' כ-FourCC.
הפורמטים של המקטעים 'VP8' ו-'VP8L' מתוארים בסעיפים Simple File Format (Lossy) ו-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 :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- פרופיל צבע: Chunk Size בייטים
- פרופיל 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: Chunk Size בייטים
- מטא-נתונים של תמונה בפורמט 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.
הערה: התו הרביעי ב-FourCC של 'XMP ' הוא רווח ASCII (0x20).
הנחיות נוספות לגבי טיפול במטא-נתונים מופיעות במאמר "הנחיות לטיפול במטא-נתונים" של קבוצת העבודה במטא-נתונים.
קטעי קוד לא ידועים
מקטע RIFF (כפי שמתואר בקטע RIFF File Format) שה-FourCC שלו שונה מכל המקטעים שמתוארים במסמך הזה, נחשב למקטע לא ידוע.
הסיבה: האפשרות לאפשר קטעי נתונים לא מוכרים מאפשרת להרחיב את הפורמט בעתיד, וגם לאחסן נתונים ספציפיים לאפליקציה.
יכול להיות שהקובץ מכיל מקטעים לא ידועים:
- בסוף הקובץ, כפי שמתואר בקטע כותרת מורחבת של קובץ WebP, או
- בסוף 'קטעי ANMF', כפי שמתואר בקטע אנימציה.
הקוראים צריכים להתעלם ממקטעי הנתונים האלה. הכותבים צריכים לשמור אותם בסדר המקורי שלהם (אלא אם הם מתכוונים לשנות את הקטעים האלה באופן ספציפי).
הרכבה של הדפסה על קנבס ממסגרות
כאן נספק סקירה כללית של האופן שבו קורא חייב להרכיב קנבס במקרה של תמונה מונפשת.
התהליך מתחיל ביצירת לוח קנבס לפי המימדים שצוינו ב-chunk של 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 מציין את השדה ב-Chunk '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)