یک PNG را به JPEG تبدیل کنید
یک PNG را در مرورگر شما به یک JPEG بیسلاین تبدیل میکند، با کیفیتی که خودتان انتخاب میکنید و با شفافیتی که روی رنگ دلخواه شما نقاشی میشود. رمزگشای PNG و رمزگذار JPEG هر دو در همین برگه اجرا میشوند، بنابراین تنظیمات یکسان در هر مرورگری همان فایل را میدهد و هیچچیز به جایی ارسال نمیشود.
هر PNG، از جمله ۱۶ بیتی، پالتدار و اینترلیس. در همین برگه خوانده میشود و هرگز به جایی ارسال نمیشود.
چگونگی کار
این چه کاری انجام میدهد
یک PNG را بدون اینکه از کامپیوتر شما خارج شود به JPEG تبدیل میکند و همان یک سؤالی را از شما میپرسد که هر مبدل دیگری بهجای شما بد جوابش میدهد: قسمتهای شفاف چه رنگی بشوند؟
این سؤال از آنجا پیش میآید که JPEG سه کانال رنگی ذخیره میکند و هیچ شفافیتی ندارد. یک PNG با پسزمینهٔ شفاف هنوز زیر آن پیکسلها مقادیر رنگی دارد، و در فایلی که از یک ابزار طراحی خروجی گرفته شده تقریباً همیشه صفرند. کانال آلفا را بدون هیچ کار دیگری دور بیندازید و صفرها نمایان میشوند — به همین دلیل لوگویی که بیدقت تبدیل شده بهصورت یک نشان سفید روی یک مستطیل سیاه میرسد.
روش
اینجا دو کدک هست و هیچکدام مال مرورگر نیست.
راه بدیهی برای ساختن این ابزار createImageBitmap به داخل OffscreenCanvas و خروج از طریق convertToBlob است. چهار خط است، و رد شد. مشخصات HTML آرگومان کیفیت را یک اشاره میداند و نگاشت را به پیادهسازی میسپارد، بنابراین کیفیت ۰٫۸ در کروم، فایرفاکس و سافاری فایل متفاوتی است — اندازهٔ متفاوت، آرتیفکتهای متفاوت، گاهی زیرنمونهبرداری رنگ متفاوت. صفحهای که خوانش اصلیاش «فایل شما اکنون ۸۲۷ بایت است» باشد نمیتواند بگذارد این عدد به این بستگی داشته باشد که کدام مرورگر باز است. بدتر اینکه، اینکه convertToBlob شفافیت را روی سیاه تخت کند یا روی سفید هم به موتور بستگی دارد، و از بیرون قابل تغییر نیست. بنابراین رمزگشای PNG و رمزگذار JPEG هر دو بر اساس مشخصات فنی ذکرشده در بالا نوشته شدهاند، و تنها API پلتفرم که درگیر است DecompressionStream برای جریان zlib داخل PNG است — تنها بخش کار که یک پاسخ درست دارد.
رمزگشا همان رمزگشای معمولی است: امضا را بررسی میکند، چانکها را پیمایش میکند، جریان IDAT را از حالت فشرده خارج میکند، سپس فیلترهای ردیف را برمیگرداند. همین مرحلهٔ آخر دلیل آن است که نمیتوان یک PNG را خارج از ترتیب رمزگشایی کرد، زیرا هر ردیف از ردیف بالای خود و پیکسل سمت چپش پیشبینی میشود، و Paeth بر اساس جهت گرادیان بین سه همسایه یکی را انتخاب میکند. فایلهای اینترلیس بهصورت هفت پاس با وضوح فزاینده میرسند و پیش از هر اتفاق دیگری در یک تصویر سرهم میشوند.
سپس تختکردن، که همان بخش مهم است. هر پیکسل به source × alpha + background × (1 − alpha) تبدیل میشود، که روی مقادیر همانطور که ذخیره شدهاند محاسبه میشود. آن مقادیر sRGB گاما-کدشدهاند، بنابراین این آمیزش از نظر فیزیکی درست نیست؛ اگر درست انجام شود، در نور خطی، یک خاکستری میانی نیمهشفاف روی سفید چند درصد روشنتر از اینجا درمیآید. این کار عمداً اینطور انجام میشود، چون هر بوم مرورگر و هر ویرایشگر تصویری هم در sRGB ترکیب میکند، و درستبودن بهتنهایی یعنی JPEG دیگر با PNGای که همین حالا میدیدید مطابقت ندارد.
رمزگذار، JPEG بیسلاین ترتیبی است. رنگها با ضرایب تمامدامنهٔ BT.601 که JFIF مشخص میکند به Y، Cb و Cr تبدیل میشوند — گونهٔ دامنهٔ استودیویی فایلی میسازد که در هر نمایشگری بیرنگ و شسته به نظر میرسد و در بازبینی کد نامرئی است. زیر کیفیت ۹۰، دو صفحهٔ رنگی در هر جهت با فیلتر جعبهای دوبهیک میانگینگیری میشوند، نه نمونهبرداری نقطهای، زیرا نمونهبرداری نقطهای از یک الگوی قرمز و سفید نیمی از رنگ را دور میریزد و نیمهٔ اشتباه را نگه میدارد. هر بلوک ۸×۸ از تبدیل کسینوسی گسسته میگذرد، بر جدول کوانتیزاسیون پیوست K که برای کیفیت انتخابی مقیاس شده تقسیم میشود و گرد میشود؛ بلوکهایی که از لبهٔ راست یا پایین بیرون میزنند بهجای پرشدن با خاکستری، آخرین پیکسل واقعی را تکرار میکنند، تا رمزگذار بیتهایی را خرج توصیف لبهای نکند که در تصویر نیست.
پیش از ادامهٔ مطلب
همان PNG را با کیفیت ۰٫۸ از طریق بوم خود مرورگر به JPEG کدگذاری میکنید، در کروم، در فایرفاکس و در سافاری. چه چیزی به دست میآورید؟
سه فایل متفاوت — اندازههای متفاوت، آرتیفکتهای متفاوت، و گاهی زیرنمونهبرداری رنگ متفاوت، زیرا مشخصات آرگومان کیفیت را یک اشاره میداند، نه یک قرارداد. صفحهای که خوانش اصلیاش «فایل شما اکنون ۸۲۷ بایت است» باشد نمیتواند بگذارد این عدد به این بستگی داشته باشد که کدام مرورگر باز است. بدتر اینکه، اینکه بوم شفافیت را روی سیاه تخت کند یا روی سفید هم به موتور واگذار شده و از بیرون قابل تغییر نیست. بنابراین رمزگشای PNG و رمزگذار JPEG اینجا هر دو بر اساس مشخصات فنی خودشان نوشته شدهاند، و تنها API پلتفرم که درگیر است DecompressionStream برای جریان zlib داخل PNG است — تنها بخش کار که یک پاسخ درست دارد.
- پیکسلهای ناشفاف۳٬۰۷۲۷۵٫۰٪
- شفاف، پرشده با پسزمینه۱٬۰۲۴۲۵٫۰٪
یک نمونهٔ عملی
یک PNG ۶۴ × ۶۴: گرادیان افقی قرمز به آبی، که شانزده ستون سمت چپش کاملاً شفاف است و زیرش صفر ذخیره شده، همان چیزی که یک خروجیگیرنده مینویسد. تبدیلشده روی پسزمینهٔ #ffffff:
Quality 40 766 B
Quality 82 827 B
Quality 95 1.2 KB
Dimensions 64 × 64
Transparent pixels 25.0% of pixels, filled with #ffffff
اینها همان خوانشهاییاند که پنل چاپ میکند، گردشده به همان شیوهای که یک مدیر فایل گرد میکند. رمزگذار دقیقاً ۷۶۶، ۸۲۷ و ۱٬۲۷۱ بایت مینویسد، و همین شمارش بایتهاست که فایل آزمون تضمین میکند، پس اگر رمزگذار روزی بدون تغییر این صفحه تغییر کند، ساخت شکست میخورد.
محاسبات زیرین در برابر مشخصات فنی بررسی میشود، نه در برابر رمزگذاری دیگر: جدولهای کوانتیزاسیون در کیفیت ۵۰ باید دقیقاً با جدولهای منتشرشدهٔ پیوست K برابر باشند، چهار جدول هافمن باید کدهای کامل و بدون پیشوند باشند که مجموعهٔ نمادهایشان دقیقاً همانهایی است که یک ضریب ممکن است لازم داشته باشد، و هیچ 0xFF واقعی داخل دادهٔ آنتروپیکدشده نباید بدون بایت درجشده بماند. رمزگشا از سمت دیگر، در برابر پیکسلها بررسی میشود — هر نوع رنگ و عمق بیتی که قالب اجازه میدهد، هر پنج فیلتر ردیف، و یک فایل اینترلیس که باید بایتبهبایت همان تصویر نسخهٔ غیراینترلیس خودش را تولید کند.
کاری که انجام نمیدهد
مدیریت رنگ را منتقل نمیکند. یک پروفایل ICC، چانک گاما یا دادههای کروماتیسیته در PNG دور ریخته میشود و JPEG بهصورت sRGB ساده نوشته میشود، بنابراین عکسی با گاموت گسترده بیجانتر از اصل درمیآید.
فقط JPEG بیسلاین مینویسد، نه پروگرسیو، بنابراین یک فایل بزرگ از بالا به پایین بار میشود، نه اینکه درجا واضح شود. CRC هر چانک PNG را بررسی نمیکند، بنابراین یک فایل خراب معمولاً بهصورت خطای رفعفشردهسازی ظاهر میشود، نه خطای چکسام. راهی برای رسیدن به اندازهٔ فایل هدف ندارد — شما کیفیتی را انتخاب میکنید و میبینید چقدر هزینه دارد. تغییر اندازه، برش یا چرخش نمیدهد، و هر جهتگیری EXIF را که PNG ممکن است داشته باشد نادیده میگیرد. فایلها به ۳۲ مگابایت و ۴۰ مگاپیکسل محدودند، زیرا هم تصویر رمزگشاییشده و هم سه صفحهٔ رنگی باید همزمان در حافظه نگه داشته شوند.
و چون تغییر اندازه نمیدهد، یک PNG بزرگ به یک JPEG بزرگ تبدیل میشود. برای اینکه عکس را تغییر اندازه هم بدهید، اول آن کار را بکنید: پیکسلهای کمتر بهتر فشرده میشوند تا همان پیکسلها که شدیدتر فشرده شدهاند.
مراحل انجام کار
- فایل انتخابشده را در همین برگه بخوانید و امضای هشتبایتی PNG را بررسی کنید، سپس زنجیرهٔ چانکها را برای یافتن IHDR، PLTE، tRNS، IDAT و pHYs پیمایش کنید.
- چانکهای IDAT بههمپیوسته را بهعنوان یک جریان zlib، با DecompressionStream خود مرورگر بهجای یک کتابخانهٔ inflate همراهشده، از حالت فشرده خارج کنید.
- فیلتر هر ردیف — None، Sub، Up، Average یا Paeth — را از بالا به پایین تصویر برگردانید، زیرا هر ردیف از ردیف بالای خود و پیکسل سمت چپش پیشبینی میشود.
- نمونهها را به RGBA هشتبیتی بسط دهید، فارغ از نوع رنگ و عمق بیت فایل، و اگر PNG اینترلیس باشد ابتدا هفت پاس را دوباره سرهم کنید.
- هر پیکسل را با استفاده از آلفای آن روی رنگ پسزمینهٔ انتخابی ترکیب کنید، تا پیکسل شفاف به همان رنگ تبدیل شود، نه به هر چیزی که زیرش بود.
- با ضرایب تمامدامنهٔ BT.601 که JFIF مشخص میکند به Y، Cb و Cr تبدیل کنید و اگر ۴:۲:۰ در کار است، وضوح دو صفحهٔ رنگی را با فیلتر جعبهای نصف کنید.
- تبدیل کسینوسی گسستهٔ هر بلوک ۸×۸ را بگیرید، بر جدول کوانتیزاسیون پیوست K که برای کیفیت انتخابی مقیاس شده تقسیم کنید و گرد کنید.
- ضرایب را با جدولهای پیوست K هافمنکد کنید، پس از هر بایت 0xFF واقعی یک بایت صفر درج کنید، و قطعههای نشانگر JFIF را دور نتیجه بنویسید.
فرضها
- شفافیت پیش از هر اتفاق دیگری روی رنگ انتخابشده تخت میشود و آمیزش در sRGB انجام میشود، نه در نور خطی. این از نظر فیزیکی درست نیست — یک پیکسل نیمهشفاف چند درصد تیرهتر از آنچه باید درمیآید — اما همان کاری است که بوم مرورگر و هر ویرایشگر تصویری میکند، بنابراین JPEG با PNGای که داشتید میدیدید مطابقت دارد.
- کیفیت همان مقیاس پیوست K مشخصات JPEG است، نه مقیاس مرورگر. همین عدد در اینجا در هر مرورگری بایتبهبایت همان فایل را میدهد، اما با «کیفیت ۸۲» در فتوشاپ یا GIMP مطابقت نخواهد داشت، زیرا هر کدام از آنها از جدولهای متفاوتی شروع میکنند.
- زیر کیفیت ۹۰، دو کانال رنگی در هر جهت با نصف وضوح ذخیره میشوند. روی عکس این نامرئی است؛ روی متن اشباع یا خطوط رنگی نازک بهصورت حاشیهٔ رنگی دیده میشود، و به همین دلیل میتوان این تنظیم را به ۴:۴:۴ اجبار کرد.
- یک PNG ۱۶ بیتی با نگهداشتن بایت بالای هر نمونه به هشت بیت کاهش مییابد. JPEG قالبی هشتبیتی است، بنابراین بایت پایین جایی برای رفتن ندارد.
- پروفایلهای رنگی منتقل نمیشوند. چانک iCCP، gAMA یا cHRM در PNG نادیده گرفته میشود و نتیجه بهصورت sRGB ساده نوشته میشود، بنابراین تصویری با گاموت گسترده بیجانتر از اصل درمیآید.
پرسشهای رایج
آیا PNG من جایی آپلود میشود؟
خیر. PNG در همین برگهٔ مرورگر خوانده میشود و رمزگشایی و رمزگذاری هر دو در یک Worker روی دستگاه خودتان انجام میشود. سروری برای فرستادنش وجود ندارد، و اگر در حالی که فایلی بار شده درخواستی از این سایت خارج شود، آزمون خودکار این صفحه شکست میخورد.
چرا پسزمینهٔ شفاف من در مبدلهای دیگر سیاه میشود؟
چون JPEG کانال آلفا ندارد، بنابراین شفافیت باید به رنگی واقعی تبدیل شود، و بیشتر ویرایشگرها هر چه را که زیر پیکسلهای شفاف ذخیره شده بود همانطور رها میکنند. آن معمولاً صفر است، که یعنی سیاه. مقادیر رنگ هنوز در PNG هستند — کانال آلفا تنها چیزی بود که پنهانشان میکرد. تنظیم پسزمینه در اینجا پیش از آنکه چیزی کدگذاری شود روی آنها رنگ میزند.
چرا JPEG من از PNG بزرگتر شده؟
چون PNG دقیقاً در همان چیزی خیلی خوب است که JPEG در آن بد است. رنگ تخت، لبههای تیز، اسکرینشاتها و طرحهای خطی بهصورت بیاتلاف عالی فشرده میشوند و با تبدیل کسینوسی بد. اگر رقم «کوچکتر به میزان» منفی درآمد، پاسخ صادقانه این است که این تصویر باید PNG بماند. JPEG در عکسها برنده است، و معمولاً با اختلاف زیاد.
از چه کیفیتی استفاده کنم؟
حدود ۸۰ برای هر چیزی که روی صفحهٔ وب میرود، و ۶۰ اگر میخواهید زیر محدودیت پیوست بروید. بالای ۹۰ فایل برای تفاوتی که تقریباً هیچکس نمیبیند بهسرعت بزرگ میشود، و بالای ۹۵ واقعاً خیلی سریع. زیر حدود ۵۰ بلوکهای هشتدرهشت در نواحی نرم مثل آسمان کمکم نمایان میشوند.
آیا این با «ذخیره بهعنوان JPEG» با همان کیفیت در فتوشاپ مطابقت دارد؟
نه، و هیچ دو رمزگذاری هم با هم مطابقت ندارند. کیفیت عددی نیست که مشخصات JPEG تعریف کرده باشد؛ روشی برای مقیاسکردن جدولهای کوانتیزاسیون است، و هر رمزگذار جور دیگری مقیاس میکند و از جدولهای متفاوتی شروع میکند. این ابزار از مقیاسبندی منتشرشده در پیوست K مشخصات استفاده میکند، که نزدیکترین چیز به یک مرجع مشترک است، و هر بار در هر مرورگری همان نتیجه را میدهد.
آیا PNGهای ۱۶ بیتی، پالتدار یا اینترلیس را پشتیبانی میکند؟
بله، همهٔ آنها را. رمزگشا هر نوع رنگ و عمق بیتی را که قالب اجازه میدهد پوشش میدهد — یک، دو، چهار، هشت و شانزده بیت، خاکستری، رنگ واقعی، پالتدار، و هر یک از اینها با کانال آلفا — بهعلاوهٔ اینترلیس Adam7 و هر پنج فیلتر ردیف. فایل ۱۶ بیتی به هشت بیت کاهش مییابد، چون JPEG جایی برای بقیه ندارد.
منابع
- Portable Network Graphics (PNG) Specification (Third Edition)
- Recommendation T.81: Information technology – Digital compression and coding of continuous-tone still images – Requirements and guidelines
- Recommendation T.871: Information technology – Digital compression and coding of continuous-tone still images: JPEG File Interchange Format (JFIF)
- RFC 1950: ZLIB Compressed Data Format Specification version 3.3