ডিভাইসপিক্সেল কনটেন্টবক্সের সাথে পিক্সেল-নিখুঁত রেন্ডারিং

একটি ক্যানভাসে কত পিক্সেল আছে?

Chrome 84 থেকে, ResizeObserver devicePixelContentBox নামে একটি নতুন বক্স পরিমাপ সমর্থন করে, যা ফিজিক্যাল পিক্সেলে উপাদানটির মাত্রা পরিমাপ করে। এটি পিক্সেল-নিখুঁত গ্রাফিক্স রেন্ডারিং সক্ষম করে, বিশেষ করে উচ্চ-ঘনত্বের পর্দার প্রসঙ্গে।

ব্রাউজার সমর্থন

  • 84
  • 84
  • 93
  • এক্স

পটভূমি: CSS পিক্সেল, ক্যানভাস পিক্সেল এবং ফিজিক্যাল পিক্সেল

যদিও আমরা প্রায়শই em , % বা vh এর মতো দৈর্ঘ্যের বিমূর্ত একক নিয়ে কাজ করি, এটি সবই পিক্সেলে ফুটে ওঠে। যখনই আমরা CSS-এ একটি উপাদানের আকার বা অবস্থান নির্দিষ্ট করি, ব্রাউজারের লেআউট ইঞ্জিন অবশেষে সেই মানটিকে পিক্সেলে রূপান্তর করবে ( px )। এগুলি হল "সিএসএস পিক্সেল", যেগুলির অনেক ইতিহাস রয়েছে এবং শুধুমাত্র আপনার স্ক্রিনে থাকা পিক্সেলগুলির সাথে একটি শিথিল সম্পর্ক রয়েছে৷

দীর্ঘ সময়ের জন্য, 96DPI ("ডটস পার ইঞ্চি") এর সাথে যেকোনও ব্যক্তির স্ক্রীনের পিক্সেল ঘনত্ব অনুমান করা মোটামুটি যুক্তিসঙ্গত ছিল, যার অর্থ যে কোনো প্রদত্ত মনিটর প্রতি সেমিতে প্রায় 38 পিক্সেল হবে। সময়ের সাথে সাথে, মনিটরগুলি বৃদ্ধি পেয়েছে এবং/অথবা সঙ্কুচিত হয়েছে বা একই পৃষ্ঠের এলাকায় আরও পিক্সেল থাকতে শুরু করেছে। এটিকে একত্রিত করুন যে ওয়েবে প্রচুর সামগ্রী তাদের মাত্রাগুলিকে সংজ্ঞায়িত করে, ফন্টের আকার সহ, px তে, এবং আমরা এই উচ্চ-ঘনত্ব ("HiDPI") স্ক্রিনে অযোগ্য পাঠ্য দিয়ে শেষ করি৷ একটি পাল্টা-পরিমাপ হিসাবে, ব্রাউজারগুলি মনিটরের প্রকৃত পিক্সেল ঘনত্ব লুকিয়ে রাখে এবং পরিবর্তে ভান করে যে ব্যবহারকারীর একটি 96 ডিপিআই ডিসপ্লে রয়েছে। CSS-এ px ইউনিট এই ভার্চুয়াল 96 DPI ডিসপ্লেতে এক পিক্সেলের আকার উপস্থাপন করে, তাই নাম "CSS Pixel"। এই ইউনিট শুধুমাত্র পরিমাপ এবং অবস্থানের জন্য ব্যবহার করা হয়. কোনো প্রকৃত রেন্ডারিং হওয়ার আগে, ফিজিক্যাল পিক্সেলে একটি রূপান্তর ঘটে।

কিভাবে আমরা এই ভার্চুয়াল ডিসপ্লে থেকে ব্যবহারকারীর আসল ডিসপ্লেতে যাব? devicePixelRatio লিখুন। এই গ্লোবাল ভ্যালু আপনাকে বলে যে একটি সিএসএস পিক্সেল তৈরি করতে আপনার কতগুলি ফিজিক্যাল পিক্সেল প্রয়োজন। devicePixelRatio (dPR) 1 হলে, আপনি মোটামুটি 96DPI সহ একটি মনিটরে কাজ করছেন। আপনার যদি রেটিনা স্ক্রীন থাকে, তাহলে আপনার dPR সম্ভবত 2 । ফোনে 2 , 3 বা এমনকি 2.65 এর মতো উচ্চতর (এবং আরও অদ্ভুত) dPR মানগুলির সম্মুখীন হওয়া অস্বাভাবিক নয়। এটা মনে রাখা অপরিহার্য যে এই মানটি সঠিক , কিন্তু আপনাকে মনিটরের প্রকৃত DPI মান বের করতে দেয় না। 2 এর dPR মানে হল 1 CSS পিক্সেল ঠিক 2 ফিজিক্যাল পিক্সেলের মানচিত্র করবে।

উদাহরণ
আমার মনিটরের ক্রোম অনুযায়ী 1 এর dPR আছে...

এটির প্রস্থ 3440 পিক্সেল এবং ডিসপ্লের এলাকা 79 সেমি চওড়া। এটি 110 DPI এর রেজোলিউশনের দিকে নিয়ে যায়। 96 এর কাছাকাছি, কিন্তু পুরোপুরি নয়। এই কারণেই একটি <div style="width: 1cm; height: 1cm"> বেশিরভাগ ডিসপ্লেতে 1cm মাপ ঠিক হবে না।

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

DevTools জুম করার কারণে বিভিন্ন ভগ্নাংশ devicePixelRatio দেখাচ্ছে।

আসুন মিশ্রণে <canvas> উপাদান যোগ করি। আপনি width এবং height বৈশিষ্ট্যগুলি ব্যবহার করে ক্যানভাসে কতগুলি পিক্সেল চান তা নির্দিষ্ট করতে পারেন। সুতরাং <canvas width=40 height=30> একটি ক্যানভাস হবে 40 বাই 30 পিক্সেল। যাইহোক, এর মানে এই নয় যে এটি 40 বাই 30 পিক্সেলে প্রদর্শিত হবে। ডিফল্টরূপে, ক্যানভাস তার অন্তর্নিহিত আকার নির্ধারণ করতে width এবং height বৈশিষ্ট্য ব্যবহার করবে, তবে আপনি যে সমস্ত CSS বৈশিষ্ট্যগুলি জানেন এবং পছন্দ করেন তা ব্যবহার করে আপনি ইচ্ছামত ক্যানভাসের আকার পরিবর্তন করতে পারেন। আমরা এখন পর্যন্ত যা কিছু শিখেছি তার সাথে, এটি আপনার মনে হতে পারে যে এটি প্রতিটি পরিস্থিতিতে আদর্শ হবে না। ক্যানভাসে একটি পিক্সেল একাধিক ফিজিক্যাল পিক্সেল, অথবা একটি ফিজিক্যাল পিক্সেলের একটি ভগ্নাংশকে কভার করতে পারে। এটি অপ্রীতিকর ভিজ্যুয়াল আর্টিফ্যাক্ট হতে পারে।

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

পিক্সেল পরিপূর্ণতা

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

ওয়েবে যতটা সম্ভব একটি পিক্সেল-নিখুঁত ক্যানভাসের কাছাকাছি কিছু অর্জন করার জন্য, এটি কমবেশি যাওয়ার পদ্ধতি হয়েছে:

<style>
  /* … styles that affect the canvas' size … */
</style>
<canvas id="myCanvas"></canvas>
<script>
  const cvs = document.querySelector('#myCanvas');
  // Get the canvas' size in CSS pixels
  const rectangle = cvs.getBoundingClientRect();
  // Convert it to real pixels. Ish.
  cvs.width = rectangle.width * devicePixelRatio;
  cvs.height = rectangle.height * devicePixelRatio;
  // Start drawing…
</script>

বিচক্ষণ পাঠক হয়তো ভাবছেন যে ডিপিআর একটি পূর্ণসংখ্যার মান না হলে কী হয়। এটি একটি ভাল প্রশ্ন এবং এই পুরো সমস্যার মূল কারণটি ঠিক কোথায়। উপরন্তু, যদি আপনি শতাংশ, vh , বা অন্যান্য পরোক্ষ মান ব্যবহার করে একটি উপাদানের অবস্থান বা আকার নির্দিষ্ট করেন, তাহলে এটা সম্ভব যে তারা ভগ্নাংশীয় CSS পিক্সেল মানগুলিকে সমাধান করবে। margin-left: 33% এভাবে একটি আয়তক্ষেত্রের সাথে শেষ হতে পারে:

DevTools একটি getBoundingClientRect() কলের ফলে ভগ্নাংশ পিক্সেল মান দেখাচ্ছে।

সিএসএস পিক্সেলগুলি সম্পূর্ণরূপে ভার্চুয়াল, তাই একটি পিক্সেলের ভগ্নাংশ থাকা তাত্ত্বিকভাবে ঠিক আছে, কিন্তু ব্রাউজার কীভাবে ফিজিক্যাল পিক্সেলের ম্যাপিং বের করে? কারণ ভগ্নাংশ শারীরিক পিক্সেল একটি জিনিস নয়.

পিক্সেল স্ন্যাপিং

ইউনিট রূপান্তর প্রক্রিয়ার যে অংশটি উপাদানগুলিকে ফিজিক্যাল পিক্সেলের সাথে সারিবদ্ধ করার যত্ন নেয় তাকে "পিক্সেল স্ন্যাপিং" বলা হয় এবং এটি টিনের উপর যা বলে তা করে: এটি ভগ্নাংশ পিক্সেল মানগুলিকে পূর্ণসংখ্যা, ফিজিক্যাল পিক্সেল মানগুলিতে স্ন্যাপ করে। এটি ঠিক কীভাবে ঘটে তা ব্রাউজার থেকে ব্রাউজারে আলাদা। আমাদের যদি ডিসপ্লেতে 791.984px প্রস্থের একটি উপাদান থাকে যেখানে dPR 1 হয়, একটি ব্রাউজার উপাদানটিকে 792px ফিজিক্যাল পিক্সেলে রেন্ডার করতে পারে, অন্য একটি ব্রাউজার এটি 791px এ রেন্ডার করতে পারে। এটি শুধুমাত্র একটি একক পিক্সেল বন্ধ, কিন্তু একটি একক পিক্সেল পিক্সেল-নিখুঁত হওয়া প্রয়োজন এমন রেন্ডারিংয়ের জন্য ক্ষতিকারক হতে পারে। এটি অস্পষ্টতা বা Moiré প্রভাবের মতো আরও দৃশ্যমান শিল্পকর্মের দিকে নিয়ে যেতে পারে।

উপরের চিত্রটি ভিন্ন রঙের পিক্সেলের একটি রাস্টার। নীচের চিত্রটি উপরের মতই, তবে বাইলিনিয়ার স্কেলিং ব্যবহার করে প্রস্থ এবং উচ্চতা এক পিক্সেল কমানো হয়েছে। উদীয়মান প্যাটার্নটিকে Moiré প্রভাব বলা হয়।
(কোনও স্কেলিং ছাড়াই এটি দেখতে আপনাকে একটি নতুন ট্যাবে এই ছবিটি খুলতে হতে পারে।)

devicePixelContentBox

devicePixelContentBox আপনাকে ডিভাইস পিক্সেল (যেমন ফিজিক্যাল পিক্সেল) ইউনিটে একটি উপাদানের সামগ্রী বক্স দেয়। এটি ResizeObserver এর অংশ। যদিও ResizeObserver এখন Safari 13.1 থেকে সমস্ত প্রধান ব্রাউজারে সমর্থিত , devicePixelContentBox বৈশিষ্ট্য আপাতত শুধুমাত্র Chrome 84+ এ রয়েছে।

ResizeObserver এ উল্লিখিত হিসাবে: এটি উপাদানগুলির জন্য document.onresize এর মতো , একটি ResizeObserver এর কলব্যাক ফাংশন পেইন্টের আগে এবং লেআউটের পরে কল করা হবে। এর মানে হল যে কলব্যাকের entries প্যারামিটারে আঁকা হওয়ার ঠিক আগে সমস্ত পর্যবেক্ষণ করা উপাদানের মাপ থাকবে। উপরে বর্ণিত আমাদের ক্যানভাস সমস্যার পরিপ্রেক্ষিতে, আমরা আমাদের ক্যানভাসে পিক্সেলের সংখ্যা সামঞ্জস্য করার জন্য এই সুযোগটি ব্যবহার করতে পারি, এটি নিশ্চিত করে যে আমরা ক্যানভাস পিক্সেল এবং ফিজিক্যাল পিক্সেলের মধ্যে একটি সঠিক ওয়ান-টু-ওয়ান ম্যাপিং করতে পারি।

const observer = new ResizeObserver((entries) => {
  const entry = entries.find((entry) => entry.target === canvas);
  canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
  canvas.height = entry.devicePixelContentBoxSize[0].blockSize;

  /* … render to canvas … */
});
observer.observe(canvas, {box: ['device-pixel-content-box']});

observer.observe() এর জন্য অপশন অবজেক্টের box প্রোপার্টি আপনাকে নির্ধারণ করতে দেয় আপনি কোন মাপ পর্যবেক্ষণ করতে চান। তাই প্রতিটি ResizeObserverEntry সর্বদা borderBoxSize , contentBoxSize এবং devicePixelContentBoxSize প্রদান করবে (যেটি ব্রাউজার এটি সমর্থন করে), কলব্যাক শুধুমাত্র তখনই ডাকা হবে যদি পর্যবেক্ষণ করা বক্সের মেট্রিক্সের কোনো পরিবর্তন হয়।

এই নতুন সম্পত্তির সাহায্যে, আমরা এমনকি আমাদের ক্যানভাসের আকার এবং অবস্থানকে অ্যানিমেট করতে পারি (কার্যকরভাবে ভগ্নাংশের পিক্সেল মান নিশ্চিত করে), এবং রেন্ডারিং-এ কোনো Moiré প্রভাব দেখতে পাই না। আপনি যদি getBoundingClientRect() ব্যবহার করে পদ্ধতিতে Moiré প্রভাব দেখতে চান, এবং নতুন ResizeObserver প্রপার্টি আপনাকে কীভাবে এটি এড়াতে দেয়, তাহলে Chrome 84 বা পরবর্তীতে ডেমো দেখুন!

বৈশিষ্ট্য সনাক্তকরণ

devicePixelContentBox জন্য ব্যবহারকারীর ব্রাউজারে সমর্থন আছে কিনা তা পরীক্ষা করার জন্য, আমরা যে কোনও উপাদান পর্যবেক্ষণ করতে পারি এবং ResizeObserverEntry এ সম্পত্তিটি উপস্থিত আছে কিনা তা পরীক্ষা করতে পারি:

function hasDevicePixelContentBox() {
  return new Promise((resolve) => {
    const ro = new ResizeObserver((entries) => {
      resolve(entries.every((entry) => 'devicePixelContentBoxSize' in entry));
      ro.disconnect();
    });
    ro.observe(document.body, {box: ['device-pixel-content-box']});
  }).catch(() => false);
}

if (!(await hasDevicePixelContentBox())) {
  // The browser does NOT support devicePixelContentBox
}

উপসংহার

পিক্সেলগুলি ওয়েবে একটি আশ্চর্যজনক জটিল বিষয় এবং এখন পর্যন্ত ব্যবহারকারীর স্ক্রীনে একটি উপাদান কতগুলি প্রকৃত পিক্সেল দখল করে তা জানার কোনও উপায় আপনার পক্ষে ছিল না৷ ResizeObserverEntry এ নতুন devicePixelContentBox সম্পত্তি আপনাকে সেই তথ্যের টুকরো দেয় এবং আপনাকে <canvas> এর সাথে পিক্সেল-নিখুঁত রেন্ডারিং করতে দেয়। devicePixelContentBox Chrome 84+ এ সমর্থিত।