ওয়েবপি লসলেস বিটস্ট্রিমের জন্য স্পেসিফিকেশন

Jyrki Alakuijala, Ph.D., Google, Inc., 2023-03-09

সারসংক্ষেপ

WebP লসলেস হলো ARGB ইমেজের লসলেস কম্প্রেশনের জন্য একটি ইমেজ ফরম্যাট। এই লসলেস ফরম্যাটটি পিক্সেলের মান হুবহু সংরক্ষণ ও পুনরুদ্ধার করে, যার মধ্যে সম্পূর্ণ স্বচ্ছ পিক্সেলের রঙের মানও অন্তর্ভুক্ত। বাল্ক ডেটা কম্প্রেস করার জন্য সিকোয়েনশিয়াল ডেটা কম্প্রেশনের একটি সার্বজনীন অ্যালগরিদম (LZ77), প্রিফিক্স কোডিং এবং একটি কালার ক্যাশে ব্যবহার করা হয়। PNG-এর চেয়ে দ্রুততর ডিকোডিং গতি প্রদর্শন করা হয়েছে, এবং সেইসাথে বর্তমান PNG ফরম্যাট ব্যবহার করে যা অর্জন করা যায় তার চেয়ে ২৫% বেশি নিবিড় কম্প্রেশনও সম্ভব।

১ ভূমিকা

এই ডকুমেন্টটিতে একটি WebP লসলেস ইমেজের সংকুচিত ডেটা উপস্থাপনার বর্ণনা দেওয়া হয়েছে। এটি WebP লসলেস এনকোডার এবং ডিকোডার বাস্তবায়নের জন্য একটি বিশদ রেফারেন্স হিসাবে তৈরি করা হয়েছে।

এই ডকুমেন্টে, আমরা বিটস্ট্রিম বর্ণনা করার জন্য ব্যাপকভাবে C প্রোগ্রামিং ভাষার সিনট্যাক্স ব্যবহার করেছি এবং বিট পড়ার জন্য ReadBits(n) একটি ফাংশনের অস্তিত্ব ধরে নিয়েছি। বাইটগুলো যে স্ট্রিমে রয়েছে তার স্বাভাবিক ক্রমে পড়া হয়, এবং প্রতিটি বাইটের বিটগুলো সর্বনিম্ন-গুরুত্বপূর্ণ-বিট-প্রথম ক্রমে পড়া হয়। যখন একই সাথে একাধিক বিট পড়া হয়, তখন পূর্ণসংখ্যাটি মূল ডেটা থেকে তার আসল ক্রমে তৈরি করা হয়। ফেরত আসা পূর্ণসংখ্যার সর্বাধিক গুরুত্বপূর্ণ বিটগুলোই মূল ডেটার সর্বাধিক গুরুত্বপূর্ণ বিট হয়। সুতরাং, স্টেটমেন্টটি

b = ReadBits(2);

নিচের দুটি বিবৃতির সাথে সমতুল্য:

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

আমরা ধরে নিই যে প্রতিটি রঙের উপাদান, অর্থাৎ আলফা, লাল, নীল এবং সবুজ, একটি ৮-বিট বাইট ব্যবহার করে প্রকাশ করা হয়। আমরা এর সংশ্লিষ্ট টাইপটিকে uint8 হিসেবে সংজ্ঞায়িত করি। একটি সম্পূর্ণ ARGB পিক্সেলকে uint32 নামক একটি টাইপ দ্বারা প্রকাশ করা হয়, যা ৩২-বিটের একটি আনসাইনড ইন্টিজার। ট্রান্সফর্মগুলোর আচরণ দেখানো কোডে, এই মানগুলো নিম্নলিখিত বিটগুলোতে কোডিফাই করা হয়েছে: আলফা ৩১..২৪ বিটে, লাল ২৩..১৬ বিটে, সবুজ ১৫..৮ বিটে এবং নীল ৭..০ বিটে; তবে, এই ফরম্যাটের ইমপ্লিমেন্টেশনগুলো অভ্যন্তরীণভাবে অন্য কোনো উপস্থাপনা ব্যবহার করতে পারে।

সাধারণভাবে, একটি WebP লসলেস ইমেজে হেডার ডেটা, ট্রান্সফর্ম ইনফরমেশন এবং প্রকৃত ইমেজ ডেটা থাকে। হেডারে ইমেজের প্রস্থ এবং উচ্চতা থাকে। একটি WebP লসলেস ইমেজ এনট্রপি এনকোড হওয়ার আগে চার ধরনের ট্রান্সফর্মের মধ্য দিয়ে যেতে পারে। বিটস্ট্রিমে থাকা ট্রান্সফর্ম ইনফরমেশনে সংশ্লিষ্ট ইনভার্স ট্রান্সফর্মগুলো প্রয়োগ করার জন্য প্রয়োজনীয় ডেটা থাকে।

২ নামকরণ

এআরজিবি
একটি পিক্সেল মান যা আলফা, লাল, সবুজ এবং নীল মান নিয়ে গঠিত।
ARGB ছবি
ARGB পিক্সেল ধারণকারী একটি দ্বি-মাত্রিক অ্যারে।
রঙের ক্যাশে
সম্প্রতি ব্যবহৃত রঙগুলো সংরক্ষণ করার জন্য একটি ছোট হ্যাশ-অ্যাড্রেসড অ্যারে, যাতে সেগুলোকে আরও ছোট কোডের মাধ্যমে মনে রাখা যায়।
রঙিন সূচীকরণ চিত্র
রঙের একটি একমাত্রিক চিত্র যা একটি ক্ষুদ্র পূর্ণসংখ্যা (WebP লসলেস-এর মধ্যে ২৫৬ পর্যন্ত) ব্যবহার করে সূচিত করা যায়।
রঙ রূপান্তর চিত্র
রঙের উপাদানগুলির পারস্পরিক সম্পর্ক সম্পর্কিত তথ্য ধারণকারী একটি দ্বি-মাত্রিক সাবরেজোলিউশন চিত্র।
দূরত্ব ম্যাপিং
দ্বিমাত্রিক সান্নিধ্যে থাকা পিক্সেলগুলোর জন্য LZ77 দূরত্বকে সর্বনিম্ন মানে পরিবর্তন করে।
এনট্রপি চিত্র
একটি দ্বি-মাত্রিক সাবরেজোলিউশন চিত্র যা নির্দেশ করে যে চিত্রের প্রতিটি বর্গক্ষেত্রে কোন এনট্রপি কোডিং ব্যবহার করা উচিত, অর্থাৎ, প্রতিটি পিক্সেল হলো একটি মেটা প্রিফিক্স কোড।
এলজেড৭৭
একটি ডিকশনারি-ভিত্তিক স্লাইডিং উইন্ডো কম্প্রেশন অ্যালগরিদম যা হয় সিম্বল নির্গত করে অথবা সেগুলোকে পূর্ববর্তী সিম্বলগুলোর অনুক্রম হিসেবে বর্ণনা করে।
মেটা প্রিফিক্স কোড
একটি ক্ষুদ্র পূর্ণসংখ্যা (সর্বোচ্চ ১৬ বিট পর্যন্ত), যা মেটা প্রিফিক্স টেবিলের কোনো একটি উপাদানকে সূচিত করে।
ভবিষ্যদ্বাণীকারী চিত্র
একটি দ্বি-মাত্রিক সাবরেজোলিউশন চিত্র যা নির্দেশ করে যে চিত্রের একটি নির্দিষ্ট বর্গক্ষেত্রের জন্য কোন স্থানিক ভবিষ্যদ্বাণীকারী ব্যবহৃত হয়।
প্রিফিক্স কোড
এনট্রপি কোডিং করার একটি চিরায়ত পদ্ধতি যেখানে অধিক ঘন ঘন কোডের জন্য কম সংখ্যক বিট ব্যবহার করা হয়।
উপসর্গ কোডিং
বৃহত্তর পূর্ণসংখ্যাকে এনট্রপি কোড করার একটি পদ্ধতি, যেখানে পূর্ণসংখ্যাটির কয়েকটি বিটকে একটি এনট্রপি কোড ব্যবহার করে কোড করা হয় এবং বাকি বিটগুলোকে অপরিবর্তিতভাবে কোড করা হয়। এর ফলে, প্রতীকের পরিসর বড় হলেও এনট্রপি কোডগুলোর বর্ণনা তুলনামূলকভাবে ছোট থাকে।
স্ক্যান-লাইন অর্ডার
পিক্সেল প্রক্রিয়াকরণের একটি ক্রম (বাম থেকে ডানে এবং উপর থেকে নিচে), যা বাম দিকের উপরের পিক্সেল থেকে শুরু হয়। একটি সারি সম্পূর্ণ হলে, পরবর্তী সারির বাম দিকের কলাম থেকে চালিয়ে যান।

৩ রিফ হেডার

হেডারের শুরুতে RIFF কন্টেইনারটি থাকে। এটি নিম্নলিখিত ২১ বাইট নিয়ে গঠিত:

  1. স্ট্রিং 'RIFF'।
  2. চাঙ্ক দৈর্ঘ্যের একটি লিটল-এন্ডিয়ান, ৩২-বিট মান, যা হলো RIFF হেডার দ্বারা নিয়ন্ত্রিত চাঙ্কটির সম্পূর্ণ আকার। সাধারণত, এটি পেলোড আকারের সমান হয় (ফাইলের আকার থেকে ৮ বাইট কম: 'RIFF' আইডেন্টিফায়ারের জন্য ৪ বাইট এবং মানটি সংরক্ষণের জন্য ৪ বাইট)।
  3. স্ট্রিং 'WEBP' (RIFF কন্টেইনারের নাম)।
  4. স্ট্রিং 'VP8L' (লসলেস-এনকোডেড ইমেজ ডেটার জন্য FourCC)।
  5. লসলেস স্ট্রিমে থাকা বাইট সংখ্যার একটি লিটল-এন্ডিয়ান, ৩২-বিট মান।
  6. ১-বাইট স্বাক্ষর 0x2f।

বিটস্ট্রিমের প্রথম ২৮ বিট ইমেজটির প্রস্থ এবং উচ্চতা নির্দিষ্ট করে। প্রস্থ এবং উচ্চতা নিম্নোক্তভাবে ১৪-বিট পূর্ণসংখ্যা হিসেবে ডিকোড করা হয়:

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

ছবির প্রস্থ এবং উচ্চতার ১৪-বিট নির্ভুলতা একটি WebP লসলেস ছবির সর্বোচ্চ আকারকে ১৬৩৮৪×১৬৩৮৪ পিক্সেলে সীমাবদ্ধ করে।

`alpha_is_used` বিটটি শুধুমাত্র একটি ইঙ্গিত, এবং এটি ডিকোডিংকে প্রভাবিত করবে না। ছবিতে সমস্ত আলফা মান ২৫৫ হলে এটিকে ০-তে সেট করতে হবে, অন্যথায় ১-এ।

int alpha_is_used = ReadBits(1);

ভার্সন_নাম্বারটি একটি ৩-বিট কোড যা অবশ্যই ০ হতে হবে। অন্য কোনো মানকে ত্রুটি হিসেবে গণ্য করা উচিত।

int version_number = ReadBits(3);

৪ রূপান্তর

ট্রান্সফর্মগুলো হলো ইমেজ ডেটার এমন পরিবর্তন যা বিপরীতমুখী করা যায় এবং যা স্থানিক ও রঙের পারস্পরিক সম্পর্ককে মডেল করার মাধ্যমে অবশিষ্ট প্রতীকী এনট্রপি কমাতে পারে। এগুলো চূড়ান্ত কম্প্রেশনকে আরও ঘন করে তুলতে পারে।

একটি ইমেজ চার ধরনের ট্রান্সফর্মের মধ্য দিয়ে যেতে পারে। একটি ১ বিট ট্রান্সফর্মের উপস্থিতি নির্দেশ করে। প্রতিটি ট্রান্সফর্ম শুধুমাত্র একবার ব্যবহার করা যায়। ট্রান্সফর্মগুলো শুধুমাত্র প্রধান-স্তরের ARGB ইমেজের জন্য ব্যবহৃত হয়; সাবরেজোলিউশন ইমেজগুলোতে (কালার ট্রান্সফর্ম ইমেজ, এনট্রপি ইমেজ, এবং প্রেডিক্টর ইমেজ) কোনো ট্রান্সফর্ম থাকে না, এমনকি ট্রান্সফর্মের সমাপ্তি নির্দেশকারী ০ বিটটিও থাকে না।

সাধারণত, একটি এনকোডার রেসিডুয়াল ইমেজের শ্যানন এনট্রপি কমানোর জন্য এই ট্রান্সফর্মগুলো ব্যবহার করে। এছাড়াও, এনট্রপি মিনিমাইজেশনের উপর ভিত্তি করে ট্রান্সফর্ম ডেটা নির্ধারণ করা যেতে পারে।

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,
};

ট্রান্সফর্ম টাইপের পরে ট্রান্সফর্ম ডেটা থাকে। ট্রান্সফর্ম ডেটাতে ইনভার্স ট্রান্সফর্ম প্রয়োগ করার জন্য প্রয়োজনীয় তথ্য থাকে এবং এটি ট্রান্সফর্ম টাইপের উপর নির্ভর করে। ইনভার্স ট্রান্সফর্মগুলো বিটস্ট্রিম থেকে পড়ার বিপরীত ক্রমে প্রয়োগ করা হয়, অর্থাৎ শেষেরটি প্রথমে।

এরপরে, আমরা বিভিন্ন ধরণের জন্য ডেটা রূপান্তরের বর্ণনা দেব।

৪.১ ভবিষ্যদ্বাণীকারী রূপান্তর

প্রতিবেশী পিক্সেলগুলো প্রায়শই পরস্পর সম্পর্কযুক্ত থাকে—এই সত্যটি কাজে লাগিয়ে এনট্রপি কমানোর জন্য প্রেডিক্টর ট্রান্সফর্ম ব্যবহার করা যেতে পারে। প্রেডিক্টর ট্রান্সফর্মে, ইতিমধ্যে ডিকোড করা পিক্সেলগুলো থেকে (স্ক্যান-লাইন ক্রমানুসারে) বর্তমান পিক্সেলের মান অনুমান করা হয় এবং শুধুমাত্র অবশিষ্ট মানটি (প্রকৃত - অনুমিত) এনকোড করা হয়। একটি পিক্সেলের সবুজ উপাদান নির্ধারণ করে যে ARGB ছবির একটি নির্দিষ্ট ব্লকের মধ্যে ১৪টি প্রেডিক্টরের মধ্যে কোনটি ব্যবহৃত হবে। প্রেডিকশন মোড নির্ধারণ করে কোন ধরনের প্রেডিকশন ব্যবহার করা হবে। আমরা ছবিটিকে বর্গক্ষেত্রে ভাগ করি এবং একটি বর্গক্ষেত্রের সমস্ত পিক্সেল একই প্রেডিকশন মোড ব্যবহার করে।

প্রেডিকশন ডেটার প্রথম ৩ বিট, বিটের সংখ্যায় ব্লকের প্রস্থ ও উচ্চতা নির্ধারণ করে।

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);

ট্রান্সফর্ম ডেটাতে ছবির প্রতিটি ব্লকের জন্য প্রেডিকশন মোড থাকে। এটি একটি সাবরেজোলিউশন ছবি, যেখানে একটি পিক্সেলের সবুজ উপাদান নির্ধারণ করে যে ARGB ছবির একটি নির্দিষ্ট ব্লকের মধ্যে থাকা block_width * block_height সমস্ত পিক্সেলের জন্য ১৪টি প্রেডিক্টরের মধ্যে কোনটি ব্যবহৃত হবে। এই সাবরেজোলিউশন ছবিটি অধ্যায় ৫- এ বর্ণিত একই কৌশল ব্যবহার করে এনকোড করা হয়।

ব্লক কলামের সংখ্যা, transform_width , দ্বি-মাত্রিক ইন্ডেক্সিং-এ ব্যবহৃত হয়। একটি পিক্সেল (x, y)-এর জন্য, সংশ্লিষ্ট ফিল্টার ব্লক অ্যাড্রেসটি নিম্নোক্তভাবে গণনা করা যায়:

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

এতে ১৪টি ভিন্ন প্রেডিকশন মোড রয়েছে। প্রতিটি প্রেডিকশন মোডে, এক বা একাধিক প্রতিবেশী পিক্সেলের মান আগে থেকে জানা থাকলে, সেই পিক্সেলগুলোর মান থেকে বর্তমান পিক্সেলের মান অনুমান করা হয়।

আমরা বর্তমান পিক্সেল (P)-এর প্রতিবেশী পিক্সেলগুলো (TL, T, TR, এবং L) নিম্নরূপভাবে নির্বাচন করেছি:

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 পিক্সেল অজানা থাকে।

পূর্ববর্তী প্রতিবেশী পিক্সেলগুলোর উপর ভিত্তি করে, বিভিন্ন পূর্বাভাস মোডগুলো নিম্নরূপভাবে সংজ্ঞায়িত করা হয়।

মোড বর্তমান পিক্সেলের প্রতিটি চ্যানেলের পূর্বাভাসিত মান
0xff000000 (ARGB-তে নিরেট কালো রঙকে বোঝায়)
এল
টি
টিআর
টিএল
গড়২(গড়২(এল, টিআর), টি)
গড়২(এল, টিএল)
গড়২(এল, টি)
গড়২(টিএল, টি)
গড়২(টি, টিআর)
১০ গড়২(গড়২(L, TL), গড়২(T, TR))
১১ L, T, TL নির্বাচন করুন
১২ ClampAddSubtractFull(L, T, TL)
১৩ ClampAddSubtractHalf(Average2(L, T), TL)

প্রতিটি ARGB উপাদানের জন্য Average2 নিম্নরূপভাবে সংজ্ঞায়িত করা হয়:

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;
  }
}

প্রতিটি ARGB উপাদানের জন্য ClampAddSubtractFull এবং ClampAddSubtractHalf ফাংশনগুলো নিম্নরূপভাবে সম্পাদন করা হয়:

// 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-পিক্সেল, এবং বামতম কলামের সমস্ত পিক্সেল হবে T-পিক্সেল।

সর্বডান কলামের পিক্সেলগুলোর জন্য TR-পিক্সেল নির্ধারণ করা একটি ব্যতিক্রমী বিষয়। সীমানায় অবস্থিত নয় এমন পিক্সেলগুলোর মতোই, সর্বডান কলামের পিক্সেলগুলোকে [0..13] মোড ব্যবহার করে অনুমান করা হয়, কিন্তু এক্ষেত্রে বর্তমান পিক্সেলের একই সারিতে থাকা সর্ববামের পিক্সেলটিকে TR-পিক্সেল হিসেবে ব্যবহার করা হয়।

পূর্বাভাসিত মানের প্রতিটি চ্যানেলের সাথে এনকোড করা অবশিষ্ট মান যোগ করে চূড়ান্ত পিক্সেল মান পাওয়া যায়।

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);
}

৪.২ রঙ রূপান্তর

কালার ট্রান্সফর্মের লক্ষ্য হলো প্রতিটি পিক্সেলের R, G, এবং B মানগুলোকে সম্পর্কহীন করা। এই ট্রান্সফর্মে সবুজ (G) মান অপরিবর্তিত রাখা হয়, সবুজ মানের উপর ভিত্তি করে লাল (R) মানকে রূপান্তরিত করা হয়, এবং প্রথমে সবুজ মান ও পরে লাল মানের উপর ভিত্তি করে নীল (B) মানকে রূপান্তরিত করা হয়।

প্রেডিক্টর ট্রান্সফর্মের মতোই, প্রথমে ছবিটিকে ব্লকগুলিতে ভাগ করা হয় এবং একটি ব্লকের সমস্ত পিক্সেলের জন্য একই ট্রান্সফর্ম মোড ব্যবহার করা হয়। প্রতিটি ব্লকের জন্য তিন ধরনের কালার ট্রান্সফর্ম এলিমেন্ট থাকে।

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

প্রকৃত রঙ পরিবর্তনটি একটি রঙ পরিবর্তন ডেল্টা (color transform delta) নির্ধারণের মাধ্যমে করা হয়। এই রঙ পরিবর্তন ডেল্টা 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 গণনা করা হয় একটি 3.5-ফিক্সড-পয়েন্ট সংখ্যাকে প্রতিনিধিত্বকারী একটি সাইনড 8-বিট পূর্ণসংখ্যা এবং একটি সাইনড 8-বিট RGB কালার চ্যানেল (c) [-128..127] ব্যবহার করে এবং এটি নিম্নরূপভাবে সংজ্ঞায়িত করা হয়:

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

ColorTransformDelta() কল করার আগে 8-বিট আনসাইনড রিপ্রেজেন্টেশন (uint8) থেকে 8-বিট সাইনড রিপ্রেজেন্টেশন (int8)-এ রূপান্তর করা প্রয়োজন। সাইনড মানটিকে একটি 8-বিট টু'স কমপ্লিমেন্ট সংখ্যা হিসাবে বিবেচনা করা উচিত (অর্থাৎ: uint8-এর [128..255] রেঞ্জটি এর রূপান্তরিত int8 মানের [-128..-1] রেঞ্জে ম্যাপ করা হয়)।

গুণটি আরও বেশি প্রিসিশন (কমপক্ষে ১৬-বিট প্রিসিশন) ব্যবহার করে করতে হবে। শিফট অপারেশনের সাইন এক্সটেনশন বৈশিষ্ট্যটি এখানে বিবেচ্য নয়; ফলাফল থেকে শুধুমাত্র সর্বনিম্ন ৮টি বিট ব্যবহৃত হয়, এবং এই বিটগুলোতে সাইন এক্সটেনশন শিফটিং ও আনসাইনড শিফটিং একে অপরের সাথে সামঞ্জস্যপূর্ণ থাকে।

এখন, আমরা কালার ট্রান্সফর্ম ডেটার বিষয়বস্তু বর্ণনা করব, যাতে ডিকোডিং ইনভার্স কালার ট্রান্সফর্ম প্রয়োগ করে মূল লাল এবং নীল মানগুলো পুনরুদ্ধার করতে পারে। প্রেডিক্টর ট্রান্সফর্মের মতোই, কালার ট্রান্সফর্ম ডেটার প্রথম ৩ বিটে ইমেজ ব্লকের প্রস্থ এবং উচ্চতা বিটের সংখ্যায় থাকে।

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

কালার ট্রান্সফর্ম ডেটার অবশিষ্ট অংশে ColorTransformElement ইনস্ট্যান্স থাকে, যা ইমেজের প্রতিটি ব্লকের সাথে সঙ্গতিপূর্ণ। প্রতিটি ColorTransformElement 'cte' একটি সাবরেজোলিউশন ইমেজের পিক্সেল হিসেবে গণ্য করা হয়, যার আলফা কম্পোনেন্ট হলো 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;
}

৪.৩ গ্রিন ট্রান্সফর্ম বিয়োগ করুন

সাবট্র্যাক্ট গ্রিন ট্রান্সফর্ম প্রতিটি পিক্সেলের লাল এবং নীল মান থেকে সবুজ মান বিয়োগ করে। যখন এই ট্রান্সফর্মটি উপস্থিত থাকে, তখন ডিকোডারকে লাল এবং নীল উভয় মানের সাথে সবুজ মান যোগ করতে হয়। এই ট্রান্সফর্মের সাথে কোনো ডেটা সংযুক্ত থাকে না। ডিকোডার নিম্নোক্তভাবে ইনভার্স ট্রান্সফর্মটি প্রয়োগ করে:

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

এই ট্রান্সফর্মটি অপ্রয়োজনীয়, কারণ এটিকে কালার ট্রান্সফর্ম ব্যবহার করে মডেল করা যায়, কিন্তু এখানে কোনো অতিরিক্ত ডেটা না থাকায়, একটি পূর্ণাঙ্গ কালার ট্রান্সফর্মের চেয়ে সাবট্র্যাক্ট গ্রিন ট্রান্সফর্মটি কম বিট ব্যবহার করে কোড করা যায়।

৪.৪ রঙ সূচীকরণ রূপান্তর

যদি স্বতন্ত্র পিক্সেল মানের সংখ্যা বেশি না থাকে, তবে একটি কালার ইনডেক্স অ্যারে তৈরি করে পিক্সেল মানগুলোকে সেই অ্যারের ইনডেক্স দিয়ে প্রতিস্থাপন করা আরও বেশি কার্যকর হতে পারে। কালার ইনডেক্সিং ট্রান্সফর্ম এই কাজটি সম্পন্ন করে। (WebP লসলেস-এর প্রেক্ষাপটে, আমরা এটিকে বিশেষভাবে প্যালেট ট্রান্সফর্ম বলি না, কারণ WebP লসলেস এনকোডিং-এ একটি অনুরূপ কিন্তু আরও গতিশীল ধারণা বিদ্যমান: কালার ক্যাশে।)

কালার ইনডেক্সিং ট্রান্সফর্ম ইমেজে থাকা স্বতন্ত্র ARGB ভ্যালুগুলোর সংখ্যা পরীক্ষা করে। যদি সেই সংখ্যা একটি নির্দিষ্ট থ্রেশহোল্ডের (২৫৬) নিচে থাকে, তবে এটি সেই ARGB ভ্যালুগুলোকে নিয়ে একটি অ্যারে তৈরি করে, যা পরে পিক্সেল ভ্যালুগুলোকে সংশ্লিষ্ট ইনডেক্স দিয়ে প্রতিস্থাপন করতে ব্যবহৃত হয়: পিক্সেলের সবুজ চ্যানেলকে ইনডেক্স দিয়ে প্রতিস্থাপন করা হয়, সমস্ত আলফা ভ্যালুকে ২৫৫-এ সেট করা হয় এবং সমস্ত লাল ও নীল ভ্যালুকে ০-তে সেট করা হয়।

ট্রান্সফর্ম ডেটাতে কালার টেবিলের আকার এবং কালার টেবিলের এন্ট্রিগুলো থাকে। ডিকোডার নিম্নলিখিতভাবে কালার ইনডেক্সিং ট্রান্সফর্ম ডেটা পড়ে:

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

কালার টেবিলটি ইমেজ স্টোরেজ ফরম্যাট ব্যবহার করেই সংরক্ষণ করা হয়। RIFF হেডার, ইমেজ সাইজ এবং ট্রান্সফর্ম ছাড়া একটি ইমেজ রিড করে কালার টেবিলটি পাওয়া যায়, যেখানে ইমেজের উচ্চতা ১ পিক্সেল এবং প্রস্থ color_table_size ধরা হয়। ইমেজের এনট্রপি কমানোর জন্য কালার টেবিলটি সর্বদা সাবট্রাকশন-কোডেড করা থাকে। প্যালেটের রঙগুলোর ডেল্টাতে সাধারণত রঙগুলোর নিজেদের চেয়ে অনেক কম এনট্রপি থাকে, যার ফলে ছোট আকারের ইমেজের ক্ষেত্রে উল্লেখযোগ্য পরিমাণ সাশ্রয় হয়। ডিকোডিংয়ের ক্ষেত্রে, কালার টেবিলের প্রতিটি চূড়ান্ত রঙ পেতে হলে পূর্ববর্তী রঙের মানগুলোকে প্রতিটি ARGB কম্পোনেন্টের সাথে আলাদাভাবে যোগ করতে হয় এবং ফলাফলের সর্বনিম্ন গুরুত্বপূর্ণ ৮ বিট সংরক্ষণ করতে হয়।

ছবির ইনভার্স ট্রান্সফর্ম হলো পিক্সেল ভ্যালুগুলোকে (যা কালার টেবিলের ইনডেক্স) কালার টেবিলের আসল ভ্যালু দিয়ে প্রতিস্থাপন করা। এই ইনডেক্সিং করা হয় ARGB কালারের সবুজ উপাদানের উপর ভিত্তি করে।

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

যদি ইন্ডেক্সটি color_table_size সমান বা তার চেয়ে বড় হয়, তাহলে argb কালার ভ্যালুটি 0x00000000 (স্বচ্ছ কালো) সেট করা উচিত।

যখন কালার টেবিলটি ছোট হয় (১৬টি রঙের সমান বা তার কম), তখন বেশ কয়েকটি পিক্সেলকে একটি একক পিক্সেলে একত্রিত করা হয়। এই পিক্সেল বান্ডলিং একাধিক (২, ৪ বা ৮) পিক্সেলকে একটি একক পিক্সেলে আবদ্ধ করে, যা যথাক্রমে ছবির প্রস্থ কমিয়ে দেয়। পিক্সেল বান্ডলিং পার্শ্ববর্তী পিক্সেলগুলোর আরও কার্যকর জয়েন্ট ডিস্ট্রিবিউশন এনট্রপি কোডিং-এর সুযোগ করে দেয় এবং এনট্রপি কোডকে কিছু অ্যারিথমেটিক কোডিং-এর মতো সুবিধা প্রদান করে, কিন্তু এটি শুধুমাত্র তখনই ব্যবহার করা যায় যখন ১৬টি বা তার কম স্বতন্ত্র মান থাকে।

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 এর মান ০, ১, ২, বা ৩ হতে পারে। ০ মানটি নির্দেশ করে যে ছবিটির জন্য কোনো পিক্সেল বান্ডলিং করা হবে না। ১ মানটি নির্দেশ করে যে দুটি পিক্সেল একত্রিত করা হবে এবং প্রতিটি পিক্সেলের পরিসর হবে [০..১৫]। ২ মানটি নির্দেশ করে যে চারটি পিক্সেল একত্রিত করা হবে এবং প্রতিটি পিক্সেলের পরিসর হবে [০..৩]। ৩ মানটি নির্দেশ করে যে আটটি পিক্সেল একত্রিত করা হবে এবং প্রতিটি পিক্সেলের পরিসর হবে [০..১], অর্থাৎ, এটি একটি বাইনারি মান।

মানগুলো সবুজ কম্পোনেন্টে নিম্নোক্তভাবে প্যাক করা হয়:

  • width_bits = 1: প্রতিটি x মানের জন্য, যেখানে x ≡ 0 (mod 2), x-এ থাকা একটি সবুজ মান x / 2-এ থাকা সবুজ মানের ৪টি সর্বনিম্ন গুরুত্বপূর্ণ বিটে স্থাপন করা হয়, এবং x + 1-এ থাকা একটি সবুজ মান x / 2-এ থাকা সবুজ মানের ৪টি সর্বোচ্চ গুরুত্বপূর্ণ বিটে স্থাপন করা হয়।
  • width_bits = 2: প্রতিটি x মানের জন্য, যেখানে x ≡ 0 (mod 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);

৫ ছবির ডেটা

ইমেজ ডেটা হলো স্ক্যান-লাইন ক্রমানুসারে সাজানো পিক্সেল মানগুলোর একটি অ্যারে।

৫.১ চিত্র তথ্যের ভূমিকা

আমরা পাঁচটি ভিন্ন ভূমিকায় চিত্র তথ্য ব্যবহার করি:

  1. ARGB ইমেজ: ইমেজটির প্রকৃত পিক্সেল সংখ্যা সংরক্ষণ করে।
  2. এনট্রপি চিত্র: মেটা প্রিফিক্স কোডগুলো সংরক্ষণ করে (দেখুন "মেটা প্রিফিক্স কোডের ডিকোডিং" )।
  3. প্রেডিক্টর ইমেজ: প্রেডিক্টর ট্রান্সফর্মের মেটাডেটা সংরক্ষণ করে (দেখুন "প্রেডিক্টর ট্রান্সফর্ম" )।
  4. কালার ট্রান্সফর্ম ইমেজ: ইমেজের বিভিন্ন ব্লকের জন্য "কালার ট্রান্সফর্ম" -এ সংজ্ঞায়িত ColorTransformElement মান ব্যবহার করে এটি তৈরি করা হয়।
  5. কালার ইনডেক্সিং ইমেজ: color_table_size আকারের একটি অ্যারে (সর্বোচ্চ ২৫৬টি ARGB ভ্যালু) যা কালার ইনডেক্সিং ট্রান্সফর্মের মেটাডেটা সংরক্ষণ করে (দেখুন "কালার ইনডেক্সিং ট্রান্সফর্ম" )।

৫.২ চিত্র ডেটার এনকোডিং

চিত্র তথ্যের এনকোডিং তার ভূমিকার ওপর নির্ভরশীল নয়।

ছবিটিকে প্রথমে কয়েকটি নির্দিষ্ট আকারের ব্লকে (সাধারণত ১৬x১৬ ব্লক) ভাগ করা হয়। এই ব্লকগুলোর প্রত্যেকটিকে নিজস্ব এনট্রপি কোড ব্যবহার করে মডেল করা হয়। এছাড়াও, কয়েকটি ব্লক একই এনট্রপি কোড ব্যবহার করতে পারে।

যুক্তি: একটি এনট্রপি কোড সংরক্ষণ করতে খরচ হয়। এই খরচ কমানো যেতে পারে যদি পরিসংখ্যানগতভাবে সদৃশ ব্লকগুলো একটি এনট্রপি কোড শেয়ার করে, যার ফলে সেই কোডটি কেবল একবারই সংরক্ষণ করতে হয়। উদাহরণস্বরূপ, একটি এনকোডার তাদের পরিসংখ্যানগত বৈশিষ্ট্য ব্যবহার করে ব্লকগুলোকে ক্লাস্টার করার মাধ্যমে অথবা এলোমেলোভাবে নির্বাচিত একজোড়া ক্লাস্টারকে বারবার যুক্ত করার মাধ্যমে সদৃশ ব্লক খুঁজে বের করতে পারে, যখন এটি ছবিটি এনকোড করার জন্য প্রয়োজনীয় মোট বিটের পরিমাণ কমিয়ে আনে।

প্রতিটি পিক্সেল তিনটি সম্ভাব্য পদ্ধতির যেকোনো একটি ব্যবহার করে এনকোড করা হয়:

  1. প্রিফিক্স-কোডেড লিটারেল: প্রতিটি চ্যানেল (সবুজ, লাল, নীল এবং আলফা) স্বাধীনভাবে এনট্রপি-কোডেড করা হয়।
  2. LZ77 পশ্চাৎমুখী রেফারেন্স: ছবির অন্য কোনো স্থান থেকে পিক্সেলের একটি ক্রম অনুলিপি করা হয়।
  3. কালার ক্যাশ কোড: সম্প্রতি দেখা কোনো রঙের একটি সংক্ষিপ্ত গুণাত্মক হ্যাশ কোড (কালার ক্যাশ ইনডেক্স) ব্যবহার করা।

নিম্নলিখিত উপবিভাগগুলিতে এগুলোর প্রত্যেকটি বিস্তারিতভাবে বর্ণনা করা হয়েছে।

৫.২.১ প্রিফিক্স-কোডেড লিটারেল

পিক্সেলটি সবুজ, লাল, নীল এবং আলফা (এই ক্রমে) প্রিফিক্স-কোডেড মান হিসেবে সংরক্ষিত থাকে। বিস্তারিত জানতে অনুচ্ছেদ ৬.২.৩ দেখুন।

৫.২.২ এলজেড৭৭ পশ্চাৎমুখী রেফারেন্স

পশ্চাৎমুখী রেফারেন্সগুলি হল দৈর্ঘ্য এবং দূরত্বের টাপল কোড :

  • দৈর্ঘ্য নির্দেশ করে যে স্ক্যান-লাইন ক্রমানুসারে কতগুলো পিক্সেল অনুলিপি করতে হবে।
  • ডিস্ট্যান্স কোড হলো একটি সংখ্যা যা পূর্বে দেখা কোনো পিক্সেলের অবস্থান নির্দেশ করে, যেখান থেকে পিক্সেলগুলো কপি করতে হবে। এর সঠিক ম্যাপিং নিচে বর্ণনা করা হলো।

দৈর্ঘ্য এবং দূরত্বের মানগুলো LZ77 প্রিফিক্স কোডিং ব্যবহার করে সংরক্ষণ করা হয়।

LZ77 প্রিফিক্স কোডিং বড় পূর্ণসংখ্যার মানকে দুটি অংশে বিভক্ত করে: প্রিফিক্স কোড এবং অতিরিক্ত বিটসমূহ । প্রিফিক্স কোডটি একটি এনট্রপি কোড ব্যবহার করে সংরক্ষণ করা হয়, অপরদিকে অতিরিক্ত বিটসমূহ কোনো এনট্রপি কোড ছাড়াই অপরিবর্তিত অবস্থায় সংরক্ষণ করা হয়।

কারণ : এই পদ্ধতিটি এনট্রপি কোডের জন্য প্রয়োজনীয় স্টোরেজ কমিয়ে দেয়। এছাড়াও, বড় মান সাধারণত বিরল হয়, তাই ছবির খুব অল্প সংখ্যক মানের জন্য অতিরিক্ত বিট ব্যবহৃত হয়। ফলে, এই পদ্ধতিটি সামগ্রিকভাবে আরও ভালো কম্প্রেশন প্রদান করে।

নিম্নলিখিত সারণিতে বিভিন্ন পরিসরের মান সংরক্ষণের জন্য ব্যবহৃত প্রিফিক্স কোড এবং অতিরিক্ত বিটগুলো উল্লেখ করা হয়েছে।

মান পরিসীমা প্রিফিক্স কোড অতিরিক্ত অংশ
৫..৬
৭..৮
৯..১২
১৩..১৬
... ... ...
৩০৭২..৪০৯৬ ২৩ ১০
... ... ...
৫২৪২৮৯..৭৮৬৪৩২ ৩৮ ১৮
৭৮৬৪৩৩..১০৪৮৫৭৬ ৩৯ ১৮

প্রিফিক্স কোড থেকে একটি (দৈর্ঘ্য বা দূরত্ব) মান পাওয়ার সিউডোকোডটি নিম্নরূপ:

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;
দূরত্ব ম্যাপিং

পূর্বে যেমন উল্লেখ করা হয়েছে, একটি ডিস্ট্যান্স কোড হলো এমন একটি সংখ্যা যা পূর্বে দেখা কোনো পিক্সেলের অবস্থান নির্দেশ করে, যেখান থেকে পিক্সেলগুলো অনুলিপি করা হবে। এই উপবিভাগে একটি ডিস্ট্যান্স কোড এবং পূর্ববর্তী পিক্সেলের অবস্থানের মধ্যেকার ম্যাপিং সংজ্ঞায়িত করা হয়েছে।

১২০ অপেক্ষা বৃহত্তর দূরত্ব কোডগুলো স্ক্যান-লাইন ক্রমানুসারে পিক্সেল দূরত্বকে নির্দেশ করে, যা ১২০ দ্বারা অফসেট করা থাকে।

সর্বনিম্ন দূরত্ব কোড [১..১২০] বিশেষ এবং বর্তমান পিক্সেলের নিকটবর্তী প্রতিবেশের জন্য সংরক্ষিত। এই প্রতিবেশটি ১২০টি পিক্সেল নিয়ে গঠিত:

  • যেসব পিক্সেল বর্তমান পিক্সেলের ১ থেকে ৭ সারি উপরে এবং বর্তমান পিক্সেলের বামে সর্বোচ্চ ৮টি কলাম অথবা ডানে সর্বোচ্চ ৭টি কলামে অবস্থিত। [এই ধরনের মোট পিক্সেল = 7 * (8 + 1 + 7) = 112 ]।
  • যেসব পিক্সেল বর্তমান পিক্সেলের সাথে একই সারিতে এবং বর্তমান পিক্সেলের বাম দিকে সর্বোচ্চ ৮টি কলামের মধ্যে অবস্থিত। [এই ধরনের 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) এর একটি অফসেট নির্দেশ করে, অর্থাৎ বর্তমান পিক্সেলের উপরের পিক্সেলটি (X দিকে 0 পিক্সেল পার্থক্য এবং Y দিকে 1 পিক্সেল পার্থক্য)। একইভাবে, দূরত্ব কোড 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 হলো পিক্সেল এককে ছবির প্রস্থ।

৫.২.৩ কালার ক্যাশ কোডিং

কালার ক্যাশে ছবিতে সম্প্রতি ব্যবহৃত রঙগুলোর একটি সেট সংরক্ষণ করে।

যুক্তি: এই উপায়ে, সম্প্রতি ব্যবহৃত রঙগুলোকে কখনও কখনও অন্য দুটি পদ্ধতি ( ৫.২.১ এবং ৫.২.২- এ বর্ণিত) ব্যবহার করে নির্গত করার চেয়ে আরও দক্ষতার সাথে উল্লেখ করা যেতে পারে।

কালার ক্যাশ কোডগুলো নিম্নোক্তভাবে সংরক্ষিত হয়। প্রথমে, একটি ১-বিটের মান থাকে যা নির্দেশ করে কালার ক্যাশ ব্যবহৃত হচ্ছে কি না। যদি এই বিটটির মান ০ হয়, তবে কোনো কালার ক্যাশ কোড থাকে না এবং সবুজ প্রতীক ও লেংথ প্রিফিক্স কোড ডিকোডকারী প্রিফিক্স কোডে সেগুলো প্রেরণ করা হয় না। তবে, যদি এই বিটটির মান ১ হয়, তাহলে এরপরে কালার ক্যাশের আকার পড়া হয়:

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) সূত্র ব্যবহার করে ইন্ডেক্সিংয়ের মাধ্যমে কালারগুলো খোঁজা হয়। একটি কালার ক্যাশে শুধুমাত্র একবারই খোঁজা হয়; এখানে কোনো কনফ্লিক্ট রেজোলিউশন নেই।

একটি ছবির ডিকোডিং বা এনকোডিং-এর শুরুতে, সমস্ত কালার ক্যাশ ভ্যালুর প্রতিটি এন্ট্রি শূন্যতে সেট করা হয়। ডিকোডিং-এর সময় কালার ক্যাশ কোডটিকে এই রঙে রূপান্তর করা হয়। কালার ক্যাশের অবস্থা বজায় রাখা হয় স্ট্রিমে পিক্সেলগুলো যে ক্রমে প্রদর্শিত হয়, সেই ক্রমে প্রতিটি পিক্সেলকে—সেটি ব্যাকওয়ার্ড রেফারেন্সিং দ্বারা উৎপাদিত হোক বা লিটারেল হিসেবেই হোক—ক্যাশে প্রবেশ করানোর মাধ্যমে।

৬ এনট্রপি কোড

৬.১ সংক্ষিপ্ত বিবরণ

অধিকাংশ ডেটা একটি ক্যানোনিকাল প্রিফিক্স কোড ব্যবহার করে কোড করা হয়। তাই, প্রকৃত প্রিফিক্স কোড পাঠানোর পরিবর্তে, প্রিফিক্স কোডের দৈর্ঘ্য পাঠিয়ে কোডগুলো প্রেরণ করা হয়।

বিশেষত, এই ফরম্যাটটি স্থানগতভাবে পরিবর্তনশীল প্রিফিক্স কোডিং ব্যবহার করে। অন্য কথায়, ছবির বিভিন্ন ব্লক সম্ভাব্যভাবে ভিন্ন ভিন্ন এনট্রপি কোড ব্যবহার করতে পারে।

কারণ : ছবির বিভিন্ন অংশের বৈশিষ্ট্য ভিন্ন হতে পারে। তাই, সেগুলোকে ভিন্ন ভিন্ন এনট্রপি কোড ব্যবহারের সুযোগ দিলে আরও বেশি নমনীয়তা এবং সম্ভাব্য উন্নততর কম্প্রেশন পাওয়া যায়।

৬.২ বিবরণ

এনকোড করা চিত্র ডেটা কয়েকটি অংশ নিয়ে গঠিত:

  1. প্রিফিক্স কোডগুলোর পাঠোদ্ধার এবং গঠন।
  2. মেটা প্রিফিক্স কোড।
  3. এনট্রপি-কোডেড চিত্র ডেটা।

যেকোনো একটি পিক্সেল (x, y)-এর সাথে পাঁচটি প্রিফিক্স কোড যুক্ত থাকে। এই কোডগুলো হলো (বিটস্ট্রিম ক্রমানুসারে):

  • প্রিফিক্স কোড #১ : গ্রিন চ্যানেল, ব্যাকওয়ার্ড-রেফারেন্স লেংথ এবং কালার ক্যাশের জন্য ব্যবহৃত হয়।
  • প্রিফিক্স কোড #২, #৩, এবং #৪ : যথাক্রমে লাল, নীল, এবং আলফা চ্যানেলের জন্য ব্যবহৃত হয়।
  • প্রিফিক্স কোড #৫ : পশ্চাৎ-রেফারেন্স দূরত্বের জন্য ব্যবহৃত হয়।

এখন থেকে আমরা এই সেটটিকে প্রিফিক্স কোড গ্রুপ হিসেবে উল্লেখ করব।

৬.২.১ প্রিফিক্স কোড ডিকোড করা এবং গঠন করা

এই অংশে বিটস্ট্রিম থেকে প্রিফিক্স কোডের দৈর্ঘ্যগুলো কীভাবে পড়তে হয় তা বর্ণনা করা হয়েছে।

প্রিফিক্স কোডের দৈর্ঘ্য দুইভাবে কোড করা যায়। ব্যবহৃত পদ্ধতিটি একটি ১-বিট মান দ্বারা নির্দিষ্ট করা হয়।

  • যদি এই বিটটির মান ১ হয়, তবে এটি একটি সরল কোড দৈর্ঘ্যের কোড
  • যদি এই বিটটির মান ০ হয়, তবে এটি একটি স্বাভাবিক কোড দৈর্ঘ্যের কোড

উভয় ক্ষেত্রেই, অব্যবহৃত কোড লেংথ থাকতে পারে যা তখনও স্ট্রিমের অংশ থাকে। এটি অদক্ষ হতে পারে, কিন্তু ফরম্যাট দ্বারা এটি অনুমোদিত। বর্ণিত ট্রি-টি অবশ্যই একটি পূর্ণ বাইনারি ট্রি হতে হবে। একটি একক লিফ নোডকে একটি পূর্ণ বাইনারি ট্রি হিসাবে বিবেচনা করা হয় এবং এটিকে সিম্পল কোড লেংথ কোড অথবা নরমাল কোড লেংথ কোড ব্যবহার করে এনকোড করা যেতে পারে। নরমাল কোড লেংথ কোড ব্যবহার করে একটি একক লিফ নোড কোড করার সময়, একটি বাদে বাকি সব কোড লেংথ শূন্য হয়, এবং একক লিফ নোডের মানটিকে ১ লেংথ দিয়ে চিহ্নিত করা হয় — এমনকি যখন সেই একক লিফ নোড ট্রি ব্যবহার করার সময় কোনো বিট ব্যবহৃত হয় না।

সাধারণ কোডের দৈর্ঘ্য কোড

এই ভ্যারিয়েন্টটি সেই বিশেষ ক্ষেত্রে ব্যবহৃত হয় যখন [0..255] পরিসরের মধ্যে শুধুমাত্র ১ বা ২ টি প্রিফিক্স সিম্বল থাকে এবং তাদের কোড দৈর্ঘ্য 1 হয়। অন্য সকল প্রিফিক্স কোড দৈর্ঘ্য অন্তর্নিহিতভাবে শূন্য হয়।

প্রথম বিটটি প্রতীকের সংখ্যা নির্দেশ করে:

int num_symbols = ReadBits(1) + 1;

নিম্নলিখিতগুলি হল প্রতীকী মান।

এই প্রথম প্রতীকটি is_first_8bits এর মানের উপর নির্ভর করে ১ বা ৮ বিট ব্যবহার করে কোড করা হয়। এর পরিসর যথাক্রমে [০..১] বা [০..২৫৫]। দ্বিতীয় প্রতীকটি, যদি থাকে, তবে তা সর্বদা [০..২৫৫] পরিসরের মধ্যে আছে বলে ধরে নেওয়া হয় এবং ৮ বিট ব্যবহার করে কোড করা হয়।

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 ধারণকারী কোড হিসাবে কোড করা যেতে পারে।

সাধারণ কোডের দৈর্ঘ্য কোড

প্রিফিক্স কোডের কোড দৈর্ঘ্যগুলো ৮ বিটের মধ্যে থাকে এবং এগুলো নিম্নোক্তভাবে পড়া হয়। প্রথমে, num_code_lengths কোড দৈর্ঘ্যের সংখ্যা নির্দিষ্ট করে।

int num_code_lengths = 4 + ReadBits(4);

কোডের দৈর্ঘ্যগুলো প্রিফিক্স কোড ব্যবহার করে এনকোড করা হয়; প্রথমে নিম্ন-স্তরের কোডের দৈর্ঘ্য, code_length_code_lengths , পড়তে হয়। kCodeLengthCodeOrder এর ক্রম অনুসারে সেই code_length_code_lengths গুলোর বাকিগুলো শূন্য হয়।

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 , তাহলে প্রতিটি প্রতীক প্রকারের (A, R, G, B, এবং দূরত্ব) জন্য বিভিন্ন পঠিত প্রতীকের সর্বাধিক সংখ্যা ( max_symbol ) তার বর্ণমালার আকারের সমান সেট করা হয়:

  • জি চ্যানেল: ২৫৬ + ২৪ + color_cache_size
  • অন্যান্য আক্ষরিক মান (A, R, এবং B): ২৫৬
  • দূরত্বের কোড: 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] আক্ষরিক কোডের দৈর্ঘ্য নির্দেশ করে।
    • মান ০ মানে কোনো প্রতীক কোড করা হয়নি।
    • [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 এবং অন্যান্য মানগুলি 0 হয়।

৬.২.২ মেটা প্রিফিক্স কোডের ডিকোডিং

পূর্বে যেমন উল্লেখ করা হয়েছে, এই ফরম্যাটটি ছবির বিভিন্ন ব্লকের জন্য ভিন্ন ভিন্ন প্রিফিক্স কোড ব্যবহারের সুযোগ দেয়। মেটা প্রিফিক্স কোডগুলো হলো এমন সূচক যা শনাক্ত করে যে ছবির বিভিন্ন অংশে কোন প্রিফিক্স কোডগুলো ব্যবহার করতে হবে।

মেটা প্রিফিক্স কোড শুধুমাত্র তখনই ব্যবহার করা যেতে পারে, যখন ছবিটি একটি ARGB ছবি হিসেবে ব্যবহৃত হচ্ছে।

মেটা প্রিফিক্স কোডগুলোর জন্য দুটি সম্ভাবনা রয়েছে, যা একটি ১-বিট মান দ্বারা নির্দেশিত হয়:

  • এই বিটটি শূন্য হলে, ইমেজের সর্বত্র কেবল একটিই মেটা প্রিফিক্স কোড ব্যবহৃত হয়। এর বেশি কোনো ডেটা সংরক্ষণ করা হয় না।
  • এই বিটটির মান এক হলে, ছবিটি একাধিক মেটা প্রিফিক্স কোড ব্যবহার করে। এই মেটা প্রিফিক্স কোডগুলো একটি এনট্রপি ইমেজ হিসেবে সংরক্ষিত থাকে (যা নিচে বর্ণনা করা হয়েছে)।

একটি পিক্সেলের লাল এবং সবুজ উপাদানগুলো একটি ১৬-বিট মেটা প্রিফিক্স কোড নির্ধারণ করে, যা ARGB ছবির একটি নির্দিষ্ট ব্লকে ব্যবহৃত হয়।

এনট্রপি চিত্র

এনট্রপি চিত্রটি নির্ধারণ করে যে চিত্রের বিভিন্ন অংশে কোন প্রিফিক্স কোডগুলো ব্যবহৃত হয়।

প্রথম ৩টি বিটে 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;

ARGB ইমেজের একটি পিক্সেল (x, y) দেওয়া থাকলে, ব্যবহারযোগ্য সংশ্লিষ্ট প্রিফিক্স কোডগুলো আমরা নিম্নরূপে পেতে পারি:

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 )।

এরপর ডিকোডারটি "ডিকোডিং এন্ট্রপি-কোডেড ইমেজ ডেটা" অংশে ব্যাখ্যা করা পদ্ধতি অনুযায়ী পিক্সেল (x, y) ডিকোড করার জন্য প্রিফিক্স কোড গ্রুপ prefix_group ব্যবহার করে।

৬.২.৩ এনট্রপি-কোডেড চিত্র ডেটার ডিকোডিং

ইমেজের বর্তমান অবস্থান (x, y)-এর জন্য, ডিকোডার প্রথমে সংশ্লিষ্ট প্রিফিক্স কোড গ্রুপটি শনাক্ত করে (যেমনটি গত বিভাগে ব্যাখ্যা করা হয়েছে)। প্রিফিক্স কোড গ্রুপটি পাওয়া গেলে, পিক্সেলটি নিম্নোক্তভাবে পড়া এবং ডিকোড করা হয়।

এরপর, প্রিফিক্স কোড #১ ব্যবহার করে বিটস্ট্রিম থেকে S প্রতীকটি পড়ুন। মনে রাখবেন যে S হলো 0 থেকে (256 + 24 + color_cache_size - 1) পরিসরের মধ্যে যেকোনো একটি পূর্ণসংখ্যা।

S-এর ব্যাখ্যা এর মানের উপর নির্ভর করে:

  1. যদি S < 256
    1. S-কে সবুজ উপাদান হিসেবে ব্যবহার করুন।
    2. প্রিফিক্স কোড #২ ব্যবহার করে বিটস্ট্রিম থেকে লাল রঙ পড়ুন।
    3. প্রিফিক্স কোড #৩ ব্যবহার করে বিটস্ট্রিম থেকে নীল রঙটি পড়ুন।
    4. প্রিফিক্স কোড #4 ব্যবহার করে বিটস্ট্রিম থেকে আলফা পড়ুন।
  2. যদি S >= 256 এবং S < 256 + 24 হয়
    1. দৈর্ঘ্য প্রিফিক্স কোড হিসেবে S - 256 ব্যবহার করুন।
    2. দৈর্ঘ্যের জন্য বিটস্ট্রিম থেকে অতিরিক্ত বিটগুলো পড়ুন।
    3. লেংথ প্রিফিক্স কোড এবং পঠিত অতিরিক্ত বিটগুলো থেকে ব্যাকওয়ার্ড-রেফারেন্স লেংথ L নির্ধারণ করুন।
    4. প্রিফিক্স কোড #৫ ব্যবহার করে বিটস্ট্রিম থেকে ডিসটেন্স প্রিফিক্স কোডটি পড়ুন।
    5. বিটস্ট্রিম থেকে দূরত্বের জন্য অতিরিক্ত বিটগুলো পড়ুন।
    6. ডিস্ট্যান্স প্রিফিক্স কোড এবং পঠিত অতিরিক্ত বিটগুলো থেকে ব্যাকওয়ার্ড-রেফারেন্স ডিস্ট্যান্স D নির্ণয় করুন।
    7. বর্তমান অবস্থান থেকে D পিক্সেল বিয়োগ করে শুরু হওয়া পিক্সেলের ক্রম থেকে L পিক্সেল (স্ক্যান-লাইন ক্রমানুসারে) অনুলিপি করুন।
  3. যদি S >= 256 + 24 হয়
    1. কালার ক্যাশে সূচক হিসেবে S - (256 + 24) ব্যবহার করুন।
    2. ওই ইনডেক্সে থাকা কালার ক্যাশ থেকে ARGB কালারটি নিন।

৭. ফরম্যাটের সামগ্রিক কাঠামো

নিচে অগমেন্টেড ব্যাকাস-নাউর ফর্ম (ABNF) RFC 5234 RFC 7405- এর ফরম্যাটটির একটি চিত্র দেওয়া হলো। এতে সমস্ত বিবরণ অন্তর্ভুক্ত করা হয়নি। ছবির শেষ প্রান্ত (EOI) শুধুমাত্র পিক্সেল সংখ্যার (image_width * image_height) মধ্যে পরোক্ষভাবে কোড করা থাকে।

Note that *element means element can be repeated 0 or more times. 5element means element is repeated exactly 5 times. %b represents a binary value.

7.1 Basic Structure

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 Structure of Transforms

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 Structure of the Image Data

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)

The following is a possible example sequence:

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