একটি ক্যানভাসে আসলে কতগুলো পিক্সেল থাকে?
ক্রোম ৮৪ থেকে, ResizeObserver devicePixelContentBox নামে একটি নতুন বক্স পরিমাপ সমর্থন করে, যা এলিমেন্টের মাত্রা বাস্তব পিক্সেলে পরিমাপ করে। এটি পিক্সেল-পারফেক্ট গ্রাফিক্স রেন্ডার করতে সক্ষম করে, বিশেষ করে উচ্চ-ঘনত্বের স্ক্রিনের ক্ষেত্রে।
পটভূমি: সিএসএস পিক্সেল, ক্যানভাস পিক্সেল, এবং ভৌত পিক্সেল
যদিও আমরা প্রায়শই em , % বা vh মতো দৈর্ঘ্যের বিমূর্ত একক নিয়ে কাজ করি, শেষ পর্যন্ত এর মূল কথাই হলো পিক্সেল। যখনই আমরা CSS-এ কোনো এলিমেন্টের আকার বা অবস্থান নির্দিষ্ট করি, ব্রাউজারের লেআউট ইঞ্জিন অবশেষে সেই মানটিকে পিক্সেলে ( px ) রূপান্তর করে। এগুলোই হলো "CSS পিক্সেল", যার একটি দীর্ঘ ইতিহাস রয়েছে এবং আপনার স্ক্রিনের পিক্সেলের সাথে এর সম্পর্ক খুবই শিথিল।
দীর্ঘদিন ধরে, ৯৬ ডিপিআই ("ডটস পার ইঞ্চি") দিয়ে যেকোনো স্ক্রিনের পিক্সেল ডেনসিটি অনুমান করা বেশ যুক্তিসঙ্গত ছিল, যার অর্থ হলো যেকোনো মনিটরে প্রতি সেন্টিমিটারে প্রায় ৩৮টি পিক্সেল থাকতো। সময়ের সাথে সাথে, মনিটরগুলো আকারে বড় বা ছোট হয়েছে অথবা একই পৃষ্ঠতলে আরও বেশি পিক্সেল ধারণ করতে শুরু করেছে। এর সাথে যোগ করুন যে, ওয়েবের অনেক কন্টেন্ট তাদের মাত্রা, ফন্ট সাইজ সহ, পিক্সেল ( px এককে নির্ধারণ করে, যার ফলে এই উচ্চ-ঘনত্বের ("হাইডিপিআই") স্ক্রিনগুলোতে লেখা অপাঠ্য হয়ে পড়ে। এর প্রতিকার হিসেবে, ব্রাউজারগুলো মনিটরের আসল পিক্সেল ডেনসিটি লুকিয়ে রাখে এবং এমন ভান করে যে ব্যবহারকারীর কাছে একটি ৯৬ ডিপিআই ডিসপ্লে রয়েছে। সিএসএস-এ px এককটি এই ভার্চুয়াল ৯৬ ডিপিআই ডিসপ্লেতে একটি পিক্সেলের আকারকে বোঝায়, তাই এর নাম "সিএসএস পিক্সেল"। এই এককটি শুধুমাত্র পরিমাপ এবং অবস্থানের জন্য ব্যবহৃত হয়। কোনো প্রকৃত রেন্ডারিং হওয়ার আগে, এটিকে বাস্তব পিক্সেলে রূপান্তর করা হয়।
আমরা কীভাবে এই ভার্চুয়াল ডিসপ্লে থেকে ব্যবহারকারীর আসল ডিসপ্লেতে যাব? এখানেই আসে devicePixelRatio )। এই গ্লোবাল ভ্যালুটি আপনাকে বলে দেয় যে একটি সিএসএস (CSS) পিক্সেল তৈরি করতে আপনার কতগুলো ফিজিক্যাল পিক্সেল প্রয়োজন। যদি devicePixelRatio (dPR) 1 হয়, তাহলে আপনি প্রায় ৯৬ ডিপিআই (DPI) যুক্ত একটি মনিটরে কাজ করছেন। যদি আপনার রেটিনা স্ক্রিন থাকে, তাহলে আপনার dPR সম্ভবত 2 হবে। ফোনে 2 , 3 বা এমনকি 2.65 মতো উচ্চতর (এবং অদ্ভুত) dPR ভ্যালু দেখা অস্বাভাবিক নয়। এটা মনে রাখা জরুরি যে এই ভ্যালুটি সঠিক , কিন্তু এটি আপনাকে মনিটরের আসল ডিপিআই (DPI) ভ্যালু বের করতে দেয় না। 2 এর একটি dPR মানে হলো ১টি সিএসএস (CSS) পিক্সেল ঠিক ২টি ফিজিক্যাল পিক্সেলের সাথে ম্যাপ হবে।
1 … এটির প্রস্থ ৩৪৪০ পিক্সেল এবং ডিসপ্লে এলাকাটি ৭৯ সেমি চওড়া। এর ফলে রেজোলিউশন হয় ১১০ ডিপিআই। ৯৬-এর কাছাকাছি, কিন্তু ঠিক তা নয়। এই কারণেই বেশিরভাগ ডিসপ্লেতে একটি <div style="width: 1cm; height: 1cm"> ট্যাগের আকার ঠিক ১ সেমি হয় না।
অবশেষে, আপনার ব্রাউজারের জুম ফিচারের দ্বারাও dPR প্রভাবিত হতে পারে। আপনি জুম ইন করলে, ব্রাউজার রিপোর্ট করা dPR বাড়িয়ে দেয়, যার ফলে সবকিছু বড় আকারে রেন্ডার হয়। জুম করার সময় আপনি যদি DevTools Console-এ devicePixelRatio চেক করেন, তাহলে ভগ্নাংশ মান দেখতে পাবেন।

devicePixelRatio দেখাচ্ছে। চলুন, এর সাথে <canvas> এলিমেন্টটি যোগ করা যাক। আপনি width এবং height অ্যাট্রিবিউট ব্যবহার করে ক্যানভাসটি কত পিক্সেলের হবে তা নির্দিষ্ট করে দিতে পারেন। যেমন, <canvas width=40 height=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>
বিচক্ষণ পাঠক হয়তো ভাবছেন, dPR যখন একটি পূর্ণসংখ্যা হয় না, তখন কী ঘটে। এটি একটি ভালো প্রশ্ন এবং ঠিক এখানেই এই পুরো সমস্যার মূল কারণটি নিহিত। এছাড়াও, যদি আপনি পার্সেন্টেজ, vh , বা অন্যান্য পরোক্ষ মান ব্যবহার করে কোনো এলিমেন্টের অবস্থান বা আকার নির্দিষ্ট করেন, তাহলে সেগুলো ভগ্নাংশ CSS পিক্সেল মানে পরিণত হওয়ার সম্ভাবনা থাকে। margin-left: 33% দেওয়া একটি এলিমেন্টের ফলে এটি দেখতে এইরকম একটি আয়তক্ষেত্রে পরিণত হতে পারে:

getBoundingClientRect() কল করার ফলে DevTools ভগ্নাংশ পিক্সেল মান দেখাচ্ছে।সিএসএস পিক্সেল সম্পূর্ণরূপে ভার্চুয়াল, তাই তাত্ত্বিকভাবে পিক্সেলের ভগ্নাংশ থাকা ঠিক আছে, কিন্তু ব্রাউজার কীভাবে বাস্তব পিক্সেলের সাথে এর ম্যাপিং নির্ধারণ করে? কারণ ভগ্নাংশযুক্ত বাস্তব পিক্সেল বলে কিছু নেই।
পিক্সেল স্ন্যাপিং
ইউনিট রূপান্তর প্রক্রিয়ার যে অংশটি এলিমেন্টগুলোকে ফিজিক্যাল পিক্সেলের সাথে সারিবদ্ধ করার কাজটি করে, তাকে "পিক্সেল স্ন্যাপিং" বলা হয়, এবং এর কাজ হলো ভগ্নাংশ পিক্সেল মানকে পূর্ণসংখ্যা, অর্থাৎ ফিজিক্যাল পিক্সেল মানে স্ন্যাপ করা। এই প্রক্রিয়াটি ঠিক কীভাবে ঘটে, তা ব্রাউজারভেদে ভিন্ন হয়। যদি কোনো ডিসপ্লেতে dPR ১ হয় এবং এলিমেন্টটির প্রস্থ 791.984px হয়, তবে একটি ব্রাউজার এলিমেন্টটিকে 792px ফিজিক্যাল পিক্সেলে রেন্ডার করতে পারে, যেখানে অন্য একটি ব্রাউজার এটিকে 791px রেন্ডার করতে পারে। এটি মাত্র একটি পিক্সেলের পার্থক্য, কিন্তু যে রেন্ডারিংগুলো পিক্সেল-পারফেক্ট হওয়া প্রয়োজন, সেগুলোর জন্য একটি পিক্সেলের এই পার্থক্যও ক্ষতিকর হতে পারে। এর ফলে ঝাপসা ভাব বা মোয়ারে এফেক্টের মতো আরও দৃশ্যমান আর্টিফ্যাক্ট দেখা দিতে পারে।

(কোনো স্কেলিং ছাড়া ছবিটি দেখার জন্য আপনাকে এটি একটি নতুন ট্যাবে খুলতে হতে পারে।)
devicePixelContentBox
devicePixelContentBox আপনাকে একটি এলিমেন্টের কন্টেন্ট বক্সকে ডিভাইস পিক্সেল (অর্থাৎ ফিজিক্যাল পিক্সেল) এককে প্রকাশ করে। এটি ResizeObserver এর একটি অংশ। যদিও Safari 13.1 থেকে ResizeObserver এখন সব প্রধান ব্রাউজারে সমর্থিত , 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() এর options অবজেক্টের box প্রপার্টি আপনাকে নির্ধারণ করতে দেয় যে আপনি কোন আকারগুলো পর্যবেক্ষণ করতে চান। তাই প্রতিটি ResizeObserverEntry সর্বদা borderBoxSize , contentBoxSize এবং devicePixelContentBoxSize (যদি ব্রাউজার এটি সমর্থন করে) প্রদান করলেও, কলব্যাকটি কেবল তখনই চালু হবে যখন পর্যবেক্ষণ করা বক্সের কোনো মেট্রিক পরিবর্তিত হবে।
এই নতুন প্রপার্টির সাহায্যে, আমরা আমাদের ক্যানভাসের আকার এবং অবস্থানও অ্যানিমেট করতে পারি (কার্যকরভাবে ভগ্নাংশ পিক্সেল মান নিশ্চিত করে), এবং রেন্ডারিংয়ে কোনো মোয়ারে এফেক্ট দেখতে পাব না। আপনি যদি getBoundingClientRect() ব্যবহার করার পদ্ধতিতে মোয়ারে এফেক্ট দেখতে চান, এবং নতুন 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 ক্রোম ৮৪+ সংস্করণে সমর্থিত।