مواصفات تقنية WebP لتدفق البيانات بدون فقدان بيانات

جيركي ألاكويجالا، دكتوراه، Google, Inc., 2023-03-09

تجريدي

تنسيق WebP بدون فقدان البيانات هو تنسيق صورة لضغط الصور بتنسيق ARGB بدون فقدان البيانات. يخزن التنسيق غير الناتج قيم البكسل ويعيدها بالضبط، بما في ذلك قيم الألوان لوحدات البكسل الشفافة بالكامل. ويتم استخدام خوارزمية عالمية للضغط التسلسلي (LZ77) وترميز البادئة وذاكرة التخزين المؤقت الملونة لضغط البيانات المجمّعة. تم توضيح سرعات فك الترميز الأسرع من PNG، وضغط أكثر 25٪ مما يمكن تنفيذه باستخدام تنسيق PNG في الوقت الحالي.

1 مقدمة

يصف هذا المستند تمثيل البيانات المضغوطة لصورة WebP بدون فقدان بيانات. إنّه مرجع تفصيلي لتطبيق برنامج الترميز وبرنامج فك الترميز بتنسيق WebP بدون فقدان البيانات.

في هذا المستند، نستخدم على نطاق واسع بنية لغة البرمجة C لوصف مصدر البيانات وافتراض وجود دالة لوحدات بت القراءة، ReadBits(n). تتم قراءة وحدات البايت بالترتيب الطبيعي للتدفق الذي يحتوي عليها، وتتم قراءة وحدات بت كل بايت بترتيب أول وحدة بت. عند قراءة وحدات بت متعددة في وقت واحد، يتم إنشاء العدد الصحيح من البيانات الأصلية بالترتيب الأصلي. وأهم وحدات بت في العدد الصحيح المعروض هي أيضًا أهم وحدات البت في البيانات الأصلية. بالتالي، يجب أن يخبرك البيان

b = ReadBits(2);

تكافئ العبارتين التاليتين:

b = ReadBits(1);
b |= ReadBits(1) << 1;

نفترض أن كل مكون لون، أي ألفا والأحمر والأزرق والأخضر، يتم تمثيله باستخدام بايت 8 بت. نُعرِّف النوع المتجاوب على أنه uint8. يتم تمثيل وحدة بكسل ARGB الكاملة بنوع يسمى uint32، وهو عدد صحيح غير موقع يتكون من 32 بت. في الكود الذي يوضح سلوك عمليات التحويل، يتم تدوين هذه القيم في وحدات البت التالية: ألفا بوحدات البت 31..24، والأحمر في البت 23..16، والأخضر في البت 15..8، والأزرق في البت 7..0. ومع ذلك، فإن استخدامات التنسيق تكون حرة في استخدام تمثيل آخر داخليًا.

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

2 التسمية

تنسيق ARGB
قيمة وحدة بكسل تتكون من قيم ألفا وأحمر وأخضر وأزرق.
صورة ARGB
مصفوفة ثنائية الأبعاد تحتوي على وحدات بكسل ARGB
ذاكرة التخزين المؤقت الملونة
مصفوفة صغيرة بعنوان التجزئة لتخزين الألوان المستخدمة مؤخرًا كي تتمكن من تذكّرها باستخدام رموز أقصر.
صورة فهرسة الألوان
صورة أحادية البعد للألوان يمكن فهرستها باستخدام عدد صحيح صغير (يصل إلى 256 ضمن تنسيق WebP بدون فقدان البيانات).
صورة بتحويل الألوان
صورة ثنائية الأبعاد بدقة فرعية تحتوي على بيانات حول ارتباطات مكوّنات الألوان.
رسم خرائط المسافة
لتغيير المسافات LZ77 بحيث يتم تطبيق أصغر قيم لوحدات البكسل في التقارب الثنائي الأبعاد
صورة إنتروبيا
صورة ذات دقة فرعية ثنائية الأبعاد تشير إلى ترميز القصور الذي يجب استخدامه في مربّع ذي صلة في الصورة، أي أنّ كل وحدة بكسل هي رمز بادئة وصفية.
LZ77
هو خوارزمية لضغط النوافذ المنزلقة تستند إلى القاموس أو تصدر رموزًا أو تصفها على أنّها تسلسلات من الرموز السابقة.
رمز البادئة الوصفية
عدد صحيح صغير (يصل إلى 16 بت) لفهرسة عنصر في جدول البادئة الوصفية.
صورة مؤشّر
صورة ثنائية الأبعاد بدقة فرعية تشير إلى أداة توقّع المكانية المستخدَمة لمربّع معيّن في الصورة.
رمز البادئة
طريقة كلاسيكية لترميز الإنتروبيا حيث يُستخدم عدد أقل من وحدات البت لترميز قصور أكبر.
ترميز البادئة
طريقة لترميز أعداد صحيحة أكبر بالقصور، تُستخدَم لترميز بضع بتات من العدد الصحيح باستخدام رمز قصور، وتحدِّد وحدات البت المتبقية الأولية. ويسمح ذلك بأن تظل أوصاف رموز الإنتروبيا صغيرة نسبيًا حتى عندما يكون نطاق الرموز كبيرًا.
طلب مسح ضوئي
ترتيب معالجة وحدات البكسل (من اليسار إلى اليمين ومن أعلى إلى أسفل)، بدءًا من البكسل في أعلى اليسار. بمجرد اكتمال صف، تابع من العمود الأيسر للصف التالي.

3 عناوين RIFF

وتحتوي بداية الرأس على حاوية RIFF. وهي تتكوّن من وحدات 21 بايت التالية:

  1. السلسلة 'RIFF'.
  2. قيمة صغيرة ذات 32 بت لطول المقطع، وهي الحجم الكامل للمقطع الذي يتحكّم فيه عنوان RIFF. يساوي عادةً هذا حجم حمولة البيانات (حجم الملف مطروحًا منه 8 بايت، أي 4 بايت لمعرّف RIFF و4 بايت لتخزين القيمة نفسها).
  3. السلسلة "WEBP" (اسم حاوية RIFF)
  4. السلسلة 'VP8L' (FourCC لبيانات الصور ذات الترميز بدون فقدان البيانات).
  5. قيمة صغيرة بحجم 32 بت لعدد وحدات البايت في البث بدون فقدان البيانات.
  6. توقيع 1 بايت 0x2f.

تحدد أول 28 بت من تدفق البيانات عرض الصورة وارتفاعها. يتم فك ترميز العرض والارتفاع كأعداد صحيحة 14 بت على النحو التالي:

int image_width = ReadBits(14) + 1;
int image_height = ReadBits(14) + 1;

إنّ الدقة التي تبلغ 14 بت لعرض الصورة وارتفاعها تحد من الحد الأقصى لحجم الصورة غير القابلة لفقدان البيانات بتنسيق WebP إلى 16384×16384 بكسل.

البت alpha_is_used هو تلميح فقط، ومن المفترض ألا يؤثر في فك الترميز. يجب تعيينها على 0 عندما تكون جميع قيم ألفا 255 في الصورة و1 في الحالات الأخرى.

int alpha_is_used = ReadBits(1);

رقم version_number هو رمز 3 بت يجب ضبطه على 0. يجب التعامل مع أي قيمة أخرى على أنها خطأ.

int version_number = ReadBits(3);

4 تحويلات

والتحولات هي التلاعب ببيانات الصور بشكل عكسي يمكن أن تقلل القصور الرمزي المتبقي من خلال نمذجة الارتباطات المكانية واللون. يمكنها جعل الضغط النهائي أكثر كثافة.

يمكن أن تمر الصورة بأربعة أنواع من التحويلات. يشير 1 بت إلى وجود تحويل. يُسمح باستخدام كل عملية تحويل مرة واحدة فقط. تُستخدم التحويلات فقط لصورة ARGB ذات المستوى الرئيسي، ولا تحتوي الصور بالدقة الفرعية (صورة تحويل اللون وصورة الإنتروبيا وصورة المتنبئ) على أي تحويلات، ولا حتى البت 0 الذي يشير إلى نهاية التحويلات.

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

while (ReadBits(1)) {  // Transform present.
  // Decode transform type.
  enum TransformType transform_type = ReadBits(2);
  // Decode transform data.
  ...
}

// Decode actual image data (Section 5).

إذا كان هناك تحويل، فإن البتتين التاليتين تحددان نوع التحويل. هناك أربعة أنواع من التحويلات.

enum TransformType {
  PREDICTOR_TRANSFORM             = 0,
  COLOR_TRANSFORM                 = 1,
  SUBTRACT_GREEN_TRANSFORM        = 2,
  COLOR_INDEXING_TRANSFORM        = 3,
};

نوع التحويل متبوعًا ببيانات التحويل. يحتوي بيانات التحويل على المعلومات المطلوبة لتطبيق التحويل العكسي ويعتمد على نوع التحويل. ويتم تطبيق التحويلات العكسية بترتيب عكسي تتم قراءتها من مصدر البيانات، أي المرحلة الأخيرة أولاً.

بعد ذلك، نصف بيانات التحويل لأنواع مختلفة.

4.1 تحويل المتنبئ

ويمكن استخدام تحويل المتنبئ لتقليل القصور عن طريق استغلال حقيقة أن وحدات البكسل المجاورة تكون مرتبطة في كثير من الأحيان. في تحويل المتنبئ، يتم توقع قيمة البكسل الحالية من وحدات البكسل التي تم فك ترميزها بالفعل (بترتيب خط المسح) ولا يتم تشفير سوى القيمة المتبقية (الفعلية - المتوقعة). يحدد المكون الأخضر للبكسل أي من المؤشرات الأربعة عشر التي يتم استخدامها داخل كتلة معينة من صورة ARGB. يُحدِّد وضع التوقّع نوع التوقّع المطلوب استخدامه. نقسم الصورة إلى مربعات، وتستخدم جميع وحدات البكسل في المربع نفس وضع التنبؤ.

تحدد أول 3 وحدات بت من بيانات التنبؤ عرض الكتلة وارتفاعها بعدد وحدات البت.

int size_bits = ReadBits(3) + 2;
int block_width = (1 << size_bits);
int block_height = (1 << size_bits);
#define DIV_ROUND_UP(num, den) (((num) + (den) - 1) / (den))
int transform_width = DIV_ROUND_UP(image_width, 1 << size_bits);

تحتوي بيانات التحويل على وضع التنبؤ لكل كتلة من الصورة. إنها صورة منخفضة الدقة يحدد فيها المكون الأخضر للبكسل أيًا من المؤشرات الأربعة عشر الذي يُستخدم في جميع block_width * block_height بكسلات في كتلة معينة من صورة ARGB. ويتم ترميز هذه الصورة ذات الدقة الفرعية باستخدام الأساليب نفسها الموضحة في الفصل 5.

يُستخدم عدد أعمدة الكتلة transform_width في الفهرسة الثنائية الأبعاد. بالنسبة للبكسل (س، ص)، يمكن للبكسل (س، ص)، حساب عنوان كتلة عامل التصفية المعني حسب:

int block_index = (y >> size_bits) * transform_width +
                  (x >> size_bits);

هناك 14 وضعًا مختلفًا للتنبؤ. في كل وضع تنبؤ، يتم التنبؤ بقيمة البكسل الحالية من وحدة بكسل مجاورة أو أكثر تكون قيمها معروفة بالفعل.

اخترنا وحدات البكسل المجاورة (TL وT وTR وL) للبكسل الحالي (P) على النحو التالي:

O    O    O    O    O    O    O    O    O    O    O
O    O    O    O    O    O    O    O    O    O    O
O    O    O    O    TL   T    TR   O    O    O    O
O    O    O    O    L    P    X    X    X    X    X
X    X    X    X    X    X    X    X    X    X    X
X    X    X    X    X    X    X    X    X    X    X

حيث TL تعني أعلى اليسار، وT تعني أعلى، وTR تعني أعلى اليمين، وL تعني اليسار. في وقت التنبؤ بقيمة P، كان قد تمت معالجة جميع وحدات البكسل O وTL وT وTR وL، ولا يكون وحدات البكسل P وكل X بكسل غير معروف.

بناءً على وحدات البكسل المجاورة السابقة، يتم تعريف أوضاع التوقع المختلفة على النحو التالي.

الوضع القيمة المتنبأ بها لكل قناة من قنوات البكسل الحالية
0 0xff000000 (يمثل اللون الأسود الخالص في ARGB)
1 L
2 T
3 بالليرة التركية
4 TL
5 المتوسط2(متوسط2(L, TR), T)
6 المتوسط2(L, TL)
7 المتوسط2(L, T)
8 المتوسط2(TL، T)
9 المتوسط2(T, TR)
10 متوسط2(متوسط2(L, TL), متوسط2(T, TR))
11 Select(L, T, TL)
12 ClampAddSubtractFull(L, T, TL)
13 ClampAddSubtractHalf(Average2(L, T), TL)

يتم تحديد Average2 على النحو التالي لكل مكوِّن من مكوِّنات ARGB:

uint8 Average2(uint8 a, uint8 b) {
  return (a + b) / 2;
}

يتم تعريف مؤشر الاختيار على النحو التالي:

uint32 Select(uint32 L, uint32 T, uint32 TL) {
  // L = left pixel, T = top pixel, TL = top-left pixel.

  // ARGB component estimates for prediction.
  int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
  int pRed = RED(L) + RED(T) - RED(TL);
  int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
  int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);

  // Manhattan distances to estimates for left and top pixels.
  int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
           abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
  int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
           abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));

  // Return either left or top, the one closer to the prediction.
  if (pL < pT) {
    return L;
  } else {
    return T;
  }
}

يتم تنفيذ الدالتَين ClampAddSubtractFull وClampAddSubtractHalf لكل مكوّن ARGB على النحو التالي:

// Clamp the input value between 0 and 255.
int Clamp(int a) {
  return (a < 0) ? 0 : (a > 255) ? 255 : a;
}
int ClampAddSubtractFull(int a, int b, int c) {
  return Clamp(a + b - c);
}
int ClampAddSubtractHalf(int a, int b) {
  return Clamp(a + (a - b) / 2);
}

هناك قواعد معالجة خاصة لبعض وحدات بكسل الحدود. في حال حدوث تحويل للتوقّعات، بغض النظر عن الوضع [0..13] لهذه وحدات البكسل، تكون القيمة المتوقَّعة للبكسل في أقصى اليسار من الصورة هي 0xff000000، وجميع وحدات البكسل في الصف العلوي هي L-pixel، وجميع وحدات البكسل في العمود الموجود في أقصى اليسار هي T-pixel.

تُعد معالجة TR-pixel مع وحدات البكسل في العمود الموجود في أقصى اليمين طريقة استثنائية. يتم توقع وحدات البكسل في العمود الموجود في أقصى اليمين باستخدام الوضعين [0..13]، تمامًا مثل وحدات البكسل غير الموجودة على الحدود، أما وحدات البكسل في أقصى اليسار في الصف ذاته حيث يتم استخدام وحدة البكسل الحالية بدلاً من ذلك كبكسل TR-pixel.

يتم الحصول على قيمة البكسل النهائية عن طريق إضافة كل قناة من القيمة المتوقعة إلى القيمة المتبقية المشفرة.

void PredictorTransformOutput(uint32 residual, uint32 pred,
                              uint8* alpha, uint8* red,
                              uint8* green, uint8* blue) {
  *alpha = ALPHA(residual) + ALPHA(pred);
  *red = RED(residual) + RED(pred);
  *green = GREEN(residual) + GREEN(pred);
  *blue = BLUE(residual) + BLUE(pred);
}

4.2 تحويل الألوان

الهدف من تحويل اللون هو التزيين بقيم R وG وB لكل بكسل. يحافظ تحويل اللون على القيمة الخضراء كما هي، ويحول القيمة الحمراء (R) بناءً على القيمة الخضراء، ويحول القيمة الزرقاء (B) بناءً على القيمة الخضراء ثم على القيمة الحمراء.

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

typedef struct {
  uint8 green_to_red;
  uint8 green_to_blue;
  uint8 red_to_blue;
} ColorTransformElement;

يتم تحويل اللون الفعلي عن طريق تحديد دلتا تحويل اللون. وتعتمد دلتا تحويل اللون على ColorTransformElement، وهي القيمة نفسها لجميع وحدات البكسل في كتلة معيّنة. يتم طرح الدلتا أثناء تحويل اللون. فإن تحويل اللون العكسي هو حينئذٍ إضافة تلك الدلتا فقط.

يتم تعريف دالة تحويل اللون على النحو التالي:

void ColorTransform(uint8 red, uint8 blue, uint8 green,
                    ColorTransformElement *trans,
                    uint8 *new_red, uint8 *new_blue) {
  // Transformed values of red and blue components
  int tmp_red = red;
  int tmp_blue = blue;

  // Applying the transform is just subtracting the transform deltas
  tmp_red  -= ColorTransformDelta(trans->green_to_red,  green);
  tmp_blue -= ColorTransformDelta(trans->green_to_blue, green);
  tmp_blue -= ColorTransformDelta(trans->red_to_blue, red);

  *new_red = tmp_red & 0xff;
  *new_blue = tmp_blue & 0xff;
}

يتم حساب ColorTransformDelta باستخدام عدد صحيح مكوَّن من 8 بت يحمل علامة، ويمثل رقم نقطة ثابتة 3.5 وقناة لون نموذج أحمر أخضر أزرق موقّعة 8 بت (c) [-128..127]، ويتم تعريفه على النحو التالي:

int8 ColorTransformDelta(int8 t, int8 c) {
  return (t * c) >> 5;
}

مطلوب التحويل من تمثيل 8 بت غير موقَّع (uint8) إلى تمثيل 8 بت موقَّع (int8) قبل استدعاء ColorTransformDelta(). يجب تفسير القيمة المُوقعة على أنها رقم مكمل مكون من 8 بت اثنين (أي: نطاق uint8 [128..255] يتم تعيينه للنطاق [ -128..-1] لقيمة int8 التي تم تحويلها).

ويجب أن يتم الضرب باستخدام مزيد من الدقة (بدقة 16 بت على الأقل). لا يهم هنا خاصية امتداد الإشارة لعملية shift، حيث يتم استخدام أقل 8 بتات فقط من النتيجة، وهناك متغيران في إضافة العلامة وإزاحة غير موقعة يكونان متناسقين مع بعضهما البعض.

الآن، نصف محتويات بيانات تحويل اللون بحيث يمكن لفك الترميز من تطبيق تحويل اللون العكسي واسترداد قيم الأحمر والأزرق الأصلية. وتحتوي أول 3 بتات من بيانات تحويل اللون على عرض وارتفاع كتلة الصورة بعدد وحدات بت، تمامًا مثل تحويل المتنبئ:

int size_bits = ReadBits(3) + 2;
int block_width = 1 << size_bits;
int block_height = 1 << size_bits;

يحتوي الجزء المتبقي من بيانات تحويل اللون على ColorTransformElement مثيل لكل كتلة من الصورة. تتم معاملة كل 'cte' ColorTransformElement على شكل وحدة بكسل في صورة بدرجة دقة فرعية يكون فيها المكوّن ألفا هو 255 والمكون الأحمر cte.red_to_blue والمكون الأخضر cte.green_to_blue والمكون الأزرق cte.green_to_red.

أثناء عملية فك الترميز، يتم فك ترميز ColorTransformElement مثيل من الكتل ويتم تطبيق تحويل الألوان العكسي على قيم ARGB الخاصة بالبكسل. كما ذكرنا سابقًا، إنّ عملية تحويل الألوان العكسية تضيف فقط قيم ColorTransformElement إلى القنوات الحمراء والزرقاء. تُترك القنوات ألفا والخضراء كما هي.

void InverseTransform(uint8 red, uint8 green, uint8 blue,
                      ColorTransformElement *trans,
                      uint8 *new_red, uint8 *new_blue) {
  // Transformed values of red and blue components
  int tmp_red = red;
  int tmp_blue = blue;

  // Applying the inverse transform is just adding the
  // color transform deltas
  tmp_red  += ColorTransformDelta(trans->green_to_red, green);
  tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
  tmp_blue +=
      ColorTransformDelta(trans->red_to_blue, tmp_red & 0xff);

  *new_red = tmp_red & 0xff;
  *new_blue = tmp_blue & 0xff;
}

4.3 طرح التحويل الأخضر

يطرح التحويل الأخضر القيم الخضراء من قيم الأحمر والأزرق لكل بكسل. عند إجراء هذا التحويل، يحتاج برنامج فك الترميز إلى إضافة القيمة الخضراء إلى كل من القيمتين الأحمر والأزرق. لا توجد بيانات مرتبطة بهذا التحويل. ويطبق برنامج فك الترميز التحويل العكسي على النحو التالي:

void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
  *red  = (*red  + green) & 0xff;
  *blue = (*blue + green) & 0xff;
}

هذا التحويل متكرر، حيث يمكن نمذجته باستخدام تحويل اللون، ولكن نظرًا لعدم وجود بيانات إضافية هنا، يمكن ترميز التحويل الأخضر باستخدام وحدات بت أقل من تحويل اللون الكامل.

4.4 تحويل فهرسة الألوان

إذا لم يكن هناك العديد من قيم البكسل الفريدة، قد يكون من الأفضل إنشاء مصفوفة فهرس الألوان واستبدال قيم البكسل بفهارس الصفيفة. ويحقق تحويل فهرسة الألوان ذلك. (في سياق WebP بدون فقدان البيانات، لا نسمي هذه العملية بالتحديد تحويل لوحة الألوان لأنّ هناك مفهومًا مشابهًا ولكن أكثر ديناميكية في ترميز WebP بدون فقدان البيانات: ذاكرة التخزين المؤقت الملونة).

تتحقّق فهرسة الألوان من عدد قيم ARGB الفريدة في الصورة. إذا كان هذا الرقم أقل من الحد الأدنى (256)، يتم إنشاء مصفوفة من قيم ARGB هذه، والتي يتم استخدامها بعد ذلك لاستبدال قيم البكسل بالفهرس المقابل: يتم استبدال القناة الخضراء لوحدات البكسل بالفهرس، ويتم تعيين جميع قيم ألفا على 255، ويتم ضبط جميع قيم الأحمر والأزرق على 0.

تحتوي بيانات التحويل على حجم جدول الألوان وإدخالات في جدول الألوان. يقرأ برنامج فك الترميز بيانات التحويل الخاصة بفهرسة الألوان على النحو التالي:

// 8-bit value for the color table size
int color_table_size = ReadBits(8) + 1;

يتم تخزين جدول الألوان باستخدام تنسيق تخزين الصور نفسه. يمكن الحصول على جدول الألوان من خلال قراءة صورة، بدون عنوان RIFF وحجم الصورة وعمليات التحويل، على افتراض ارتفاع بكسل واحد وعرض color_table_size. يتم دائمًا ترميز جدول الألوان لتقليل قصور الصورة. تحتوي دلتا ألوان لوحة الألوان عادةً على قصور أقل بكثير من الألوان فيها، مما يؤدي إلى توفير كبير في الصور الأصغر. خلال عملية فك الترميز، يمكن الحصول على كل لون نهائي في جدول الألوان عن طريق إضافة قيم مكونات اللون السابقة من خلال كل مكوّن من مكونات ARGB بشكل منفصل وتخزين أقل 8 وحدات بت من النتيجة.

يتمثل التحويل العكسي للصورة في استبدال قيم البكسل (وهي فهارس لجدول الألوان) بقيم جدول الألوان الفعلية. تتم الفهرسة بالاستناد إلى المكون الأخضر للون ARGB.

// Inverse transform
argb = color_table[GREEN(argb)];

إذا كان الفهرس يساوي color_table_size أو أكبر منه، يجب ضبط قيمة اللون argb على 0x00000000 (أسود شفاف).

عندما يكون جدول الألوان صغيرًا (يساوي أو أقل من 16 لونًا)، يتم تجميع عدة وحدات بكسل في بكسل واحد. يعمل تجميع وحدات البكسل على تجميع وحدات بكسل متعددة (2 أو 4 أو 8) بكسل في بكسل واحد، مما يقلل عرض الصورة على التوالي. يسمح تجميع وحدات البكسل بترميز إنتروبيا للتوزيع المشترك بشكل أكثر كفاءة لوحدات البكسل المجاورة ويقدم بعض المزايا الشبيهة بالترميز الحسابي لرمز القصور، ولكن لا يمكن استخدامه إلا عند وجود 16 قيمة فريدة أو أقل.

color_table_size يحدد عدد وحدات البكسل التي يتم دمجها:

int width_bits;
if (color_table_size <= 2) {
  width_bits = 3;
} else if (color_table_size <= 4) {
  width_bits = 2;
} else if (color_table_size <= 16) {
  width_bits = 1;
} else {
  width_bits = 0;
}

قيمة width_bits هي 0 أو 1 أو 2 أو 3. تشير القيمة 0 إلى عدم عدم إتمام جمع وحدات البكسل للصورة. تشير القيمة 1 إلى أنّه يتم دمج بكسلَين، وأنّ نطاق كل بكسل [0..15]. تشير القيمة 2 إلى أنّه تم دمج أربع وحدات بكسل، وأنّ نطاق كل بكسل [0..3]. تشير القيمة 3 إلى أنه تم دمج ثماني بكسلات وأن لكل بكسل نطاق [0..1]، أي قيمة ثنائية.

يتم تجميع القيم في المكون الأخضر على النحو التالي:

  • width_bits = 1: لكل قيمة x، حيث يتم وضع x ÷ 0 (mod 2)، يتم وضع قيمة خضراء عند x في 4 وحدات بت أقل أهمية من القيمة الخضراء عند x / 2، والقيمة الخضراء عند x + 1 موضوعة في أهم 4 وحدات بت من القيمة الخضراء عند x / 2.
  • width_bits = 2: لكل قيمة x، حيث يتم وضع x ÷ 0 (النموذج 4)، يتم وضع قيمة خضراء عند x في البتتين الأقل أهمية من القيمة الخضراء عند x / 4، والقيم الخضراء عند x + 1 حتى x + 3 يتم وضعها بالترتيب إلى البتات الأكثر أهمية من القيمة الخضراء x / 4.
  • width_bits = 3: لكل قيمة x، حيث يتم وضع x ÷ 0 (mod 8)، يتم وضع قيمة خضراء عند x في أقل وحدة بت من القيمة الخضراء عند x / 8، ويتم وضع القيم الخضراء في x + 1 إلى x + 7 بترتيب في وحدات البت الأكثر أهمية من القيمة الخضراء x / 8.

بعد قراءة هذا التحويل، تم أخذ عينة فرعية من image_width في width_bits. يؤثر هذا على حجم التحويلات اللاحقة. يمكن حساب الحجم الجديد باستخدام DIV_ROUND_UP، على النحو المحدّد سابقًا.

image_width = DIV_ROUND_UP(image_width, 1 << width_bits);

5 بيانات الصور

بيانات الصورة هي مصفوفة من قيم البكسل بترتيب سطر المسح الضوئي.

5.1 أدوار بيانات الصور

ونحن نستخدم بيانات الصور في خمسة أدوار مختلفة:

  1. صورة ARGB: لتخزين وحدات البكسل الفعلية للصورة.
  2. صورة إنتروبيا: لتخزين رموز البادئة الوصفية (راجِع "فك ترميز رموز البادئات الوصفية").
  3. صورة المتنبئ: لتخزين البيانات الوصفية لتحويل المتنبئ (يمكنك الاطلاع على "تحويل أداة التوقع").
  4. صورة تحويل اللون: تم إنشاؤها باستخدام قيم ColorTransformElement (تم تحديدها في "Color Transform") لكتلات مختلفة من الصورة.
  5. صورة فهرسة الألوان: مصفوفة بحجم color_table_size (يصل إلى 256 قيمة ARGB) تخزّن البيانات الوصفية لتحويل فهرسة الألوان (يمكنك الاطّلاع على "تحويل فهرسة الألوان").

5.2 ترميز بيانات الصور

ويكون تشفير بيانات الصورة مستقلاً عن دورها.

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

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

يتم ترميز كل بكسل باستخدام إحدى الطرق الثلاث المحتملة:

  1. القيم الحرفية المرمّزة بالبادئة: يتم ترميز كل قناة (الأخضر والأحمر والأزرق وألفا) بشكل مستقل.
  2. مرجع LZ77 السابق: يتم نسخ تسلسل وحدات بكسل من مكان آخر في الصورة.
  3. رمز ذاكرة التخزين المؤقت للألوان: استخدام رمز تجزئة ضربي قصير (فهرس ذاكرة التخزين المؤقت للألوان) للون الذي ظهر مؤخرًا

وتصف الأقسام الفرعية التالية كلاً من هذه العناصر بالتفصيل.

5.2.1 الأحرف الحرفية المشفرة بالبادئة

يتم تخزين وحدة البكسل كقيم مشفرة بالبادئة للأخضر والأحمر والأزرق وألفا (بهذا الترتيب). راجع الفقرة 6.2.3 للاطّلاع على التفاصيل.

5.2.2 LZ77 مرجع سابق

المراجع السابقة هي صفوف من الطول ورمز المسافة:

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

يتم تخزين قيم الطول والمسافة باستخدام ترميز بادئة LZ77.

من خلال ترميز بادئة LZ77، يتم تقسيم قيم الأعداد الصحيحة الكبيرة إلى جزأين، وهما رمز البادئة ووحدات البت الإضافية. يتم تخزين رمز البادئة باستخدام رمز القصور، بينما يتم تخزين وحدات البت الإضافية كما هي (بدون رمز قصور).

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

يوضح الجدول التالي أكواد البادئة والبت الإضافية المستخدمة لتخزين نطاقات القيم المختلفة.

نطاق القيمة رمز البادئة وحدات بت إضافية
1 0 0
2 1 0
3 2 0
4 3 0
5..6 4 1
7..8 5 1
9..12 6 2
13..16 7 2
... ... ...
3072..4096 23 10
... ... ...
524289..786432 38 18
786433..1048576 39 18

يكون الكود الزائف للحصول على قيمة (الطول أو المسافة) من رمز البادئة على النحو التالي:

if (prefix_code < 4) {
  return prefix_code + 1;
}
int extra_bits = (prefix_code - 2) >> 1;
int offset = (2 + (prefix_code & 1)) << extra_bits;
return offset + ReadBits(extra_bits) + 1;
رسم خرائط المسافات

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

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

أصغر رموز مسافة [1..120] هي رموز خاصة ومخصصة لحي قريب من البكسل الحالي. يتكون هذا الحي من 120 بكسل:

  • وحدات البكسل التي تزيد عن 1 إلى 7 صفوف فوق وحدة البكسل الحالية ويصل حجمها إلى 8 أعمدة إلى اليسار أو ما يصل إلى 7 أعمدة على يمين البكسل الحالي. [إجمالي وحدات البكسل هذه = 7 * (8 + 1 + 7) = 112].
  • وحدات البكسل الموجودة في نفس صف وحدة البكسل الحالية والتي يصل طولها إلى 8 أعمدة إلى يسار البكسل الحالي. [8 وحدات بكسل من هذا النوع].

يكون التعيين بين رمز المسافة distance_code وإزاحة وحدة البكسل المجاورة (xi, yi) على النحو التالي:

(0, 1),  (1, 0),  (1, 1),  (-1, 1), (0, 2),  (2, 0),  (1, 2),
(-1, 2), (2, 1),  (-2, 1), (2, 2),  (-2, 2), (0, 3),  (3, 0),
(1, 3),  (-1, 3), (3, 1),  (-3, 1), (2, 3),  (-2, 3), (3, 2),
(-3, 2), (0, 4),  (4, 0),  (1, 4),  (-1, 4), (4, 1),  (-4, 1),
(3, 3),  (-3, 3), (2, 4),  (-2, 4), (4, 2),  (-4, 2), (0, 5),
(3, 4),  (-3, 4), (4, 3),  (-4, 3), (5, 0),  (1, 5),  (-1, 5),
(5, 1),  (-5, 1), (2, 5),  (-2, 5), (5, 2),  (-5, 2), (4, 4),
(-4, 4), (3, 5),  (-3, 5), (5, 3),  (-5, 3), (0, 6),  (6, 0),
(1, 6),  (-1, 6), (6, 1),  (-6, 1), (2, 6),  (-2, 6), (6, 2),
(-6, 2), (4, 5),  (-4, 5), (5, 4),  (-5, 4), (3, 6),  (-3, 6),
(6, 3),  (-6, 3), (0, 7),  (7, 0),  (1, 7),  (-1, 7), (5, 5),
(-5, 5), (7, 1),  (-7, 1), (4, 6),  (-4, 6), (6, 4),  (-6, 4),
(2, 7),  (-2, 7), (7, 2),  (-7, 2), (3, 7),  (-3, 7), (7, 3),
(-7, 3), (5, 6),  (-5, 6), (6, 5),  (-6, 5), (8, 0),  (4, 7),
(-4, 7), (7, 4),  (-7, 4), (8, 1),  (8, 2),  (6, 6),  (-6, 6),
(8, 3),  (5, 7),  (-5, 7), (7, 5),  (-7, 5), (8, 4),  (6, 7),
(-6, 7), (7, 6),  (-7, 6), (8, 5),  (7, 7),  (-7, 7), (8, 6),
(8, 7)

على سبيل المثال، يشير رمز المسافة 1 إلى إزاحة (0, 1) للبكسل المجاور، أي البكسل فوق البكسل الحالي (الفرق بـ 0 بكسل في الاتجاه X والفرق البالغ 1 بكسل في الاتجاه Y). وبالمثل، يشير رمز المسافة 3 إلى البكسل في أعلى يسار الشاشة.

يمكن لفك الترميز تحويل رمز المسافة distance_code إلى مسافة dist بترتيب سطر المسح الضوئي على النحو التالي:

(xi, yi) = distance_map[distance_code - 1]
dist = xi + yi * image_width
if (dist < 1) {
  dist = 1
}

حيث distance_map هو التعيين المذكور أعلاه، وimage_width هو عرض الصورة بالبكسل.

5.2.3 ترميز ذاكرة التخزين المؤقت بالألوان

تخزن ذاكرة التخزين المؤقت للألوان مجموعة من الألوان التي تم استخدامها مؤخرًا في الصورة.

الأسباب: بهذه الطريقة، يمكن أحيانًا الإشارة إلى الألوان المستخدَمة مؤخرًا بشكل أكثر فعالية من إصدارها باستخدام الطريقتين الأخريين (على النحو الموضّح في 5.2.1 و5.2.2).

يتم تخزين رموز ذاكرة التخزين المؤقت الملونة على النحو التالي. أولاً، توجد قيمة مكونة من 1 بت تشير إلى ما إذا تم استخدام ذاكرة التخزين المؤقت الملونة أم لا. إذا كان هذا البت 0، فلا توجد رموز ذاكرة تخزين مؤقت للألوان، ولا يتم نقلها في رمز البادئة الذي يفك ترميز الرموز الخضراء ورموز بادئة الطول. ومع ذلك، إذا كان هذا البت هو 1، فستتم قراءة حجم ذاكرة التخزين المؤقت الملونة تاليًا:

int color_cache_code_bits = ReadBits(4);
int color_cache_size = 1 << color_cache_code_bits;

يحدّد color_cache_code_bits حجم ذاكرة التخزين المؤقت الملونة (1 << color_cache_code_bits). نطاق القيم المسموح بها للسمة color_cache_code_bits هو [1..11]. يجب أن تشير برامج فك الترميز المتوافقة إلى تدفق بيانات تالف للقيم الأخرى.

ذاكرة التخزين المؤقت الملونة هي مصفوفة بحجم color_cache_size. ويخزن كل إدخال لونًا ARGB واحد يتم البحث عن الألوان من خلال فهرستها بواسطة (0x1e35a7bd * color) >> (32 - color_cache_code_bits). يتم إجراء عملية بحث واحدة فقط في ذاكرة التخزين المؤقت الملونة؛ وليس هناك حل تعارض.

في بداية فك ترميز صورة أو ترميزها، يتم تعيين جميع الإدخالات في جميع قيم ذاكرة التخزين المؤقت للألوان على صفر. يتم تحويل رمز التخزين المؤقت للون إلى هذا اللون في وقت فك الترميز. يتم الحفاظ على حالة ذاكرة التخزين المؤقت الملونة من خلال إدخال كل بكسل في ذاكرة التخزين المؤقت بالترتيب الذي تظهر به في ساحة المشاركات، سواء تم إنتاجه من خلال الإشارة إلى الوراء أم بقيم حرفية.

رمز إنتروبيا 6

6.1 نظرة عامة

يتم ترميز معظم البيانات باستخدام رمز البادئة الأساسي. وبالتالي، يتم إرسال الرموز عن طريق إرسال أطوال رموز البادئة، على عكس رموز البادئة الفعلية.

وعلى وجه الخصوص، يستخدم التنسيق ترميز بادئة المتغير المكاني. بعبارة أخرى، يمكن أن تستخدم كتل مختلفة من الصورة رموز إنتروبيا مختلفة.

السبب: قد يكون للمناطق المختلفة من الصورة خصائص مختلفة. وبالتالي، فإن السماح لها باستخدام رموز إنتروبيا مختلفة يوفر مزيدًا من المرونة وربما ضغطًا أفضل.

6.2 التفاصيل

تتألف بيانات الصورة المشفرة من عدة أجزاء:

  1. فك الترميز وإنشاء رموز البادئة.
  2. رموز بادئة Meta.
  3. بيانات الصورة المشفرة بالإنتروبيا.

بالنسبة إلى أي بكسل (x, y)، هناك مجموعة من خمسة رموز بادئة مرتبطة به. هذه الرموز هي (بترتيب تدفق البيانات):

  • رمز البادئة رقم 1: يتم استخدامه مع القناة الخضراء وطول المرجع السابق وذاكرة التخزين المؤقت بالألوان.
  • رمز البادئة رقم 2 و3 و4: يُستخدَم على التوالي للقنوات الحمراء والزرقاء وقنوات ألفا.
  • رمز البادئة رقم 5: يُستخدَم للإشارة إلى مسافة الرجوع إلى الخلف.

ومن هنا، نشير إلى هذه المجموعة باعتبارها مجموعة رموز بادئة.

6.2.1 فك ترميز رموز البادئات وإنشائها

يصف هذا القسم كيفية قراءة أطوال رمز البادئة من مصدر بيانات البت.

يمكن ترميز أطوال رمز البادئة بطريقتين. يتم تحديد الطريقة المستخدمة بواسطة قيمة 1 بت.

  • إذا كان هذا البت 1، فهو رمز طول رمز بسيط.
  • إذا كان هذا البت 0، سيكون رمز طول الرمز العادي.

وفي كلتا الحالتين، قد تكون هناك أطوال رموز غير مستخدمة لا تزال جزءًا من البث. قد يكون هذا الإجراء غير فعال، ولكن يُسمح به من خلال التنسيق. يجب أن تكون الشجرة الموضّحة شجرة ثنائية كاملة. تعتبر العقدة ذات الورقة الفردية شجرة ثنائية كاملة ويمكن ترميزها باستخدام رمز طول الرمز البسيط أو رمز طول الرمز العادي. عند ترميز عقدة ذات ورقة شجر واحدة باستخدام الرمز العادي لطول الرمز، يكون طول الرمز بالكامل باستثناء واحد هو الأصفار، ويتم تمييز قيمة العقدة الفردية بالطول 1، حتى في حال عدم استهلاك أي وحدات بت عند استخدام شجرة العقدة الفردية هذه.

رمز بسيط لطول الرمز

وتُستخدَم هذه الصيغة في حالة خاصة عندما يكون رمز بادئة واحد أو رمزَين فقط في النطاق [0..255] مع طول الرمز 1. جميع أطوال رموز البادئة الأخرى عبارة عن أصفار ضمنيًا.

يشير البت الأول إلى عدد الرموز:

int num_symbols = ReadBits(1) + 1;

فيما يلي القيم الرمزية.

يتم ترميز هذا الرمز الأول باستخدام 1 أو 8 بت، استنادًا إلى قيمة is_first_8bits. النطاق هو [0..1] أو [0..255]، على التوالي. الرمز الثاني، إن وجد، يفترض دائمًا أنه في النطاق [0..255] ويتم ترميزه باستخدام 8 بت.

int is_first_8bits = ReadBits(1);
symbol0 = ReadBits(1 + 7 * is_first_8bits);
code_lengths[symbol0] = 1;
if (num_symbols == 2) {
  symbol1 = ReadBits(8);
  code_lengths[symbol1] = 1;
}

يجب أن يكون الرمزين مختلفين. يُسمح بالرموز المكررة، ولكنها غير فعالة.

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

الرمز العادي لطول الرمز

يصل طول الرمز البرمجي لرمز البادئة إلى 8 بت وتتم قراءته على النحو التالي. أولاً، تُحدِّد السمة num_code_lengths عدد أطوال الرموز.

int num_code_lengths = 4 + ReadBits(4);

ويتمّ ترميز أطوال الرمز بحد ذاتها باستخدام رموز البادئة، ويجب قراءة أطوال الرموز ذات المستوى الأدنى، code_length_code_lengths أولاً. باقي هذه القيم code_length_code_lengths (وفقًا للترتيب في kCodeLengthCodeOrder) عبارة عن أصفار.

int kCodeLengthCodes = 19;
int kCodeLengthCodeOrder[kCodeLengthCodes] = {
  17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
};
int code_length_code_lengths[kCodeLengthCodes] = { 0 };  // All zeros
for (i = 0; i < num_code_lengths; ++i) {
  code_length_code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
}

بعد ذلك، إذا تم ضبط ReadBits(1) == 0 على الحد الأقصى لعدد رموز القراءة المختلفة (max_symbol) لكل نوع من أنواع الرموز (A وR وG وB والمسافة) على حجم الأحرف الأبجدية:

  • G القناة: 256 + 24 + color_cache_size
  • القيم الحرفية الأخرى (A وR وB): 256
  • رمز المسافة: 40

وبخلاف ذلك، يتم تعريفه على أنه:

int length_nbits = 2 + 2 * ReadBits(3);
int max_symbol = 2 + ReadBits(length_nbits);

إذا كان max_symbol أكبر من حجم الحروف الأبجدية لنوع الرمز، يكون مصدر البيانات غير صالح.

يتم بعد ذلك إنشاء جدول بادئة من code_length_code_lengths واستخدامه لقراءة ما يصل إلى max_symbol من أطوال الرموز.

  • يشير الرمز [0..15] إلى أطوال الرمز الحرفية.
    • تعني القيمة 0 أنه لم يتم ترميز أي رموز.
    • تشير القيم [1..15] إلى طول البت للرمز المعني.
  • يكرر الرمز 16 القيمة غير الصفرية السابقة [3..6] مرات، أي 3 + ReadBits(2) مرة. إذا تم استخدام التعليمة البرمجية 16 قبل إصدار قيمة غير صفرية، فسيتم تكرار القيمة 8.
  • ينبعث الرمز 17 من سلسلة أصفار بطول [3..10]، أي 3 + ReadBits(3) مرة.
  • ينبعث الرمز 18 من سلسلة أصفار بطول [11..138]، أي 11 + ReadBits(7) مرة.

بمجرد قراءة أطوال التعليمات البرمجية، يتم تشكيل كود بادئة لكل نوع رمز (A، وR، وG، وB، والمسافة) باستخدام أحجام الأبجدية الخاصة بها.

يجب أن يرمز الرمز العادي لطول الرمز إلى شجرة قرارات كاملة، أي أنّ مجموع 2 ^ (-length) لكل الرموز غير الصفرية يجب أن يكون واحدًا بالضبط. ومع ذلك، هناك استثناء واحد لهذه القاعدة، وهو شجرة العقد المفردة، حيث يتم تمييز قيمة العقدة الطرفية بالقيمة 1 والقيم الأخرى تكون 0s.

6.2.2 فك ترميز رموز البادئات الوصفية

كما أشرنا سابقًا، يسمح التنسيق باستخدام رموز بادئة مختلفة لكتلات مختلفة من الصورة. رموز البادئة الوصفية هي فهارس تحدّد رموز البادئات التي يجب استخدامها في أجزاء مختلفة من الصورة.

لا يمكن استخدام رموز البادئة الوصفية إلا عند استخدام الصورة في دور صورة ARGB.

هناك احتمالان لرموز البادئة الوصفية، تتم الإشارة إليها بقيمة 1 بت:

  • إذا كان هذا البت صفرًا، فسيتم استخدام رمز بادئة وصفية واحد فقط في كل مكان في الصورة. ولا يتم تخزين المزيد من البيانات.
  • إذا كان هذا البت واحدًا، ستستخدم الصورة رموز بادئة وصفية متعددة. يتم تخزين رموز البادئات الوصفية هذه كصورة قصور (على النحو الموضّح أدناه).

تحدد المكونات الحمراء والخضراء لوحدة البكسل رمز بادئة وصفية 16 بت يُستخدم في كتلة معينة من صورة ARGB.

صورة إنتروبيا

تحدّد صورة القصور رموز البادئة المستخدمة في أجزاء مختلفة من الصورة.

تحتوي أول 3 وحدات بت على القيمة prefix_bits. تُستمَد أبعاد صورة القصور من prefix_bits:

int prefix_bits = ReadBits(3) + 2;
int prefix_image_width =
    DIV_ROUND_UP(image_width, 1 << prefix_bits);
int prefix_image_height =
    DIV_ROUND_UP(image_height, 1 << prefix_bits);

حيث يتم تحديد DIV_ROUND_UP على النحو المحدّد في وقت سابق.

تحتوي وحدات البت التالية على صورة قصور بها العرض prefix_image_width والارتفاع prefix_image_height.

تفسير رموز البادئة الوصفية

يمكن الحصول على عدد مجموعات رموز البادئة في صورة ARGB من خلال البحث عن أكبر رمز بادئة وصفية في الصورة القصور:

int num_prefix_groups = max(entropy image) + 1;

حيث يشير max(entropy image) إلى أكبر رمز بادئة مخزَّن في صورة القصور.

ونظرًا لاحتواء كل مجموعة رموز بادئة على خمسة رموز بادئة، يكون العدد الإجمالي لرموز البادئة كما يلي:

int num_prefix_codes = 5 * num_prefix_groups;

بناءً على وحدة البكسل (x, y) في صورة ARGB، يمكننا الحصول على رموز البادئة المناظرة لاستخدامها على النحو التالي:

int position =
    (y >> prefix_bits) * prefix_image_width + (x >> prefix_bits);
int meta_prefix_code = (entropy_image[position] >> 8) & 0xffff;
PrefixCodeGroup prefix_group = prefix_code_groups[meta_prefix_code];

حيث افترضنا وجود بنية PrefixCodeGroup التي تمثّل مجموعة من خمسة رموز بادئة. أيضًا، prefix_code_groups هي مصفوفة من PrefixCodeGroup (بحجم num_prefix_groups).

بعد ذلك، يستخدم برنامج فك الترميز مجموعة رموز البادئة prefix_group لفك ترميز البكسل (x, y)، كما هو موضّح في القسم "فك ترميز بيانات الصور المشفرة بالإنتروبيا".

6.2.3 فك ترميز بيانات الصور المشفرة بالإنتروبيا

بالنسبة إلى الموضع الحالي (x وy) في الصورة، يحدِّد برنامج فك الترميز أولاً مجموعة رموز البادئة المتناظرة (كما هو موضّح في القسم الأخير). باستخدام مجموعة رموز البادئة، تتم قراءة وحدة البكسل وفك ترميزها على النحو التالي.

بعد ذلك، اقرأ الرمز S من مصدر البيانات باستخدام رمز البادئة رقم 1. يُرجى العِلم أنّ S هو أي عدد صحيح في النطاق من 0 إلى (256 + 24 + color_cache_size- 1).

يعتمد تفسير حرف S على قيمته:

  1. إذا كانت S < 256
    1. استخدم S كمكون أخضر.
    2. اقرأ اللون الأحمر من مصدر بيانات البث باستخدام رمز البادئة رقم 2.
    3. اقرأ اللون الأزرق من مصدر بيانات البث باستخدام رمز البادئة رقم 3.
    4. قراءة ألفا من مصدر بيانات البث باستخدام رمز البادئة رقم 4
  2. إذا كانت S >= 256 & S < 256 + 24
    1. استخدم S - 256 كرمز بادئة طول.
    2. اقرأ المزيد من وحدات البت لمعرفة الطول من تدفق البيانات.
    3. حدد طول المرجع الخلفي L من رمز بادئة الطول وقراءة وحدات البت الإضافية.
    4. اقرأ رمز بادئة المسافة من مصدر بيانات البث باستخدام رمز البادئة رقم 5.
    5. قراءة وحدات بت إضافية لمعرفة المسافة من مصدر البيانات.
    6. حدد مسافة المرجع العكسي D من رمز بادئة المسافة وقراءة وحدات البت الإضافية.
    7. انسخ وحدات البكسل L (بترتيب سطر المسح) من تسلسل وحدات البكسل بدءًا من الموضع الحالي مطروحًا منه وحدات البكسل D.
  3. إذا كانت S >= 256 + 24
    1. استخدم S - (256 + 24) كفهرس في ذاكرة التخزين المؤقت للألوان.
    2. احصل على لون ARGB من ذاكرة التخزين المؤقت للألوان في هذا الفهرس.

7 الهيكل العام للتنسيق

وفي ما يلي عرض بالتنسيق في "صيغة باكوس ناور المعززة" (ABNF) RFC 5234 RFC 7405. وهي لا تشمل جميع التفاصيل. يتم ترميز نهاية الصورة (EOI) ضمنيًا فقط في عدد البكسلات (image_width * image_height).

يُرجى العِلم أنّ *element تعني أنّه يمكن تكرار element 0 مرة أو أكثر. 5element تعني أن element يتم تكرارها 5 مرات بالضبط. تمثل %b قيمة ثنائية.

7.1 البنية الأساسية

format        = RIFF-header image-header image-stream
RIFF-header   = %s"RIFF" 4OCTET %s"WEBPVP8L" 4OCTET
image-header  = %x2F image-size alpha-is-used version
image-size    = 14BIT 14BIT ; width - 1, height - 1
alpha-is-used = 1BIT
version       = 3BIT ; 0
image-stream  = optional-transform spatially-coded-image

7.2 بنية التحوّلات

optional-transform   =  (%b1 transform optional-transform) / %b0
transform            =  predictor-tx / color-tx / subtract-green-tx
transform            =/ color-indexing-tx

predictor-tx         =  %b00 predictor-image
predictor-image      =  3BIT ; sub-pixel code
                        entropy-coded-image

color-tx             =  %b01 color-image
color-image          =  3BIT ; sub-pixel code
                        entropy-coded-image

subtract-green-tx    =  %b10

color-indexing-tx    =  %b11 color-indexing-image
color-indexing-image =  8BIT ; color count
                        entropy-coded-image

7.3 بنية بيانات الصورة

spatially-coded-image =  color-cache-info meta-prefix data
entropy-coded-image   =  color-cache-info data

color-cache-info      =  %b0
color-cache-info      =/ (%b1 4BIT) ; 1 followed by color cache size

meta-prefix           =  %b0 / (%b1 entropy-image)

data                  =  prefix-codes lz77-coded-image
entropy-image         =  3BIT ; subsample value
                         entropy-coded-image

prefix-codes          =  prefix-code-group *prefix-codes
prefix-code-group     =
    5prefix-code ; See "Interpretation of Meta Prefix Codes" to
                 ; understand what each of these five prefix
                 ; codes are for.

prefix-code           =  simple-prefix-code / normal-prefix-code
simple-prefix-code    =  ; see "Simple Code Length Code" for details
normal-prefix-code    =  ; see "Normal Code Length Code" for details

lz77-coded-image      =
    *((argb-pixel / lz77-copy / color-cache-code) lz77-coded-image)

في ما يلي مثال على التسلسل المحتمل:

RIFF-header image-size %b1 subtract-green-tx
%b1 predictor-tx %b0 color-cache-info
%b0 prefix-codes lz77-coded-image