מהנושא במגזין ליישום בפרויקט
דפי שירות וטכניים רלוונטיים למאמר
מי שמפעיל יישומי VCL על לקוחות מודרניים של Windows נתקע מוקדם או מאוחר באותו סימפטום: האייקונים נראים ב‑125%/150%/200% מטושטשים, משופשפים או מקבלים שפת גבול אפור. בדיוק כאן נוגע הנושא של אייקונים VCL ב‑High‑DPI במעשה: לא מפני ש‑High‑DPI הוא חדש, אלא מפני שהבעיות בדרך כלל נחשפות רק בשימוש יומיומי — על Terminalserverים, בהחלפת מצב עגינה של מחשב נייד או ברגע של תצורות DPI שונות לכל מסך.
הבעיה המרכזית כמעט אף פעם אינה „ה‑PNG שבור“, אלא ה‑pipeline: מאיפה מגיע האייקון (Resource, קובץ, SVG, פונט), באיזה גודל הוא מסופק, איך הוא נוחת בתוך ה‑TImageList ומי ומתי מבצע את הסקיילינג. ב‑VCL מצטברים כמה מושגים שצריך להפריד ביניהם: DPI-Awareness (האם Windows עושה את הסקייל לאפליקציה או האם האפליקציה עושה זאת בעצמה), Per‑Monitor‑DPI (לכל מסך מותר להיות שונה) ו‑אסטרטגיית ImageList (להחזיק רזולוציות מרובות או לרסטר בזמן ריצה).
במאמר זה לא ניכנס במכוון לוויכוחים על UI‑Design, אלא לגישה נקייה ועמידה בתפעול: לסקל אייקונים בזמן ריצה, אך כך שהם לא יהפכו לטשטוש פיקסלים, שהערוצי Alpha יישארו שלמים וששינויי DPI יעבדו ללא מהבהוב או גדלי תמונה שגויים. בנוסף נסקור מלכודות, רמזי דיבוג והערכת מציאות כנה מתי המאמץ הנוסף מוצדק.
מדוע נוצר טשטוש פיקסלים: הבנת שרשרת הסקייל ב‑VCL
הסיבה הנפוצה ביותר לאייקונים מטושטשים היא מיון/גידול חד‑פעמי בזמן לא נכון. זרימת עבודה קלאסית ב‑Legacy‑VCL:
- האפליקציה מספקת אייקונים רק ב‑16×16 או 24×24.
- Windows או ה‑VCL מבצעים סקייל ל‑20×20 / 32×32 / 48×48.
- הממדים משתמש במטודת אינטרפולציה שמתאימה לתמונות צילומיות, אך מטשטשת קצוות בגראפיקה מבוססת פיקסלים.
- בנוסף, השקיפות (Alpha) נדחסת ללוגיקת מסכה או מומרת מספר פעמים.
זה נהיה בעייתי במיוחד כאשר מתרחשים כמה שלבי סקיילינג ברציפות: למשל כאשר ה‑ImageList כבר מספקת bitmap מסולק, ואז Windows מרנדרת שוב בגלל חוסר DPI‑Awareness או בגלל שה‑System‑DPI שונה. התוצאה: טשטוש כפול.
נקודה שנוטים להמעיט בערכה בפרויקטים היא זמן הסקיילינג עצמו — ה‑Skalierungs‑Zeitpunkt. ב‑Per‑Monitor‑DPI (PMv2, כלומר Per‑Monitor‑DPI‑Awareness v2) הערך היעיל של ה‑DPI יכול להשתנות כשחלון עובר בין מסכים או שכוח Remote‑Desktop משנה DPI דינמית. אם אז ה‑TImageList או קאש אינם נבנים מחדש כראוי, פתאום רואים אייקונים בגדלים שגויים או ברסטר הלא נכון.
TImageList תחת High‑DPI: מלכודות טיפוסיות ביישומים אמיתיים
ה־TImageList הוגדרה היסטורית עבור ביטמפים קטנים, עם מידות קבועות, אינדקסים ולוגיקת אחסון יחסית קשיחה. בסביבות High-DPI זה יוצר מלכודות מעשיות:
1) Width/Height מקובעים
הרבה טפסי VCL מגדירים את ImageList.Width/Height בזמן העיצוב ומשאירים זאת כך. ב־150% Windows לעתים רוצה, למשל, להפוך 16×16 ל־24×24. אם הרשימה נשארת על 16×16, התמונה תיחתך או תעבור סקיילינג במקום אחר — שניהם לא טובים.
2) PNG-Alpha und Maskenlogik
תלוי בגרסת Delphi וב־VCL‑Controls, תוכל למצוא עצמך במהירות במצב מעורב: PNGs עם ערוץ אלפא מאוחסנים פנימית לעתים כ־32‑bit Bitmap ולעתים כ־Mask+Color. ברגע שאתה מבצע המרות מרובות (PNG → Bitmap → ImageList → Draw), נוצרות הילות אפורות או קצוות חדים. האפקט תלוי ברקע: על ToolBar כהה זה נראה רע יותר מאשר על פאנל בהיר.
3) DPI-Wechsel zur Laufzeit: Caches, Handles, OwnerDraw
כמה קומפוננטות מטמנות את הצגת התמונה או מאמצות ImageList‑Handles בשלב שבו ה‑DPI עדיין לא סופי. במיוחד ב־Toolbars, TreeViews/ListViews ובתסריטי OwnerDraw רואים לאחר שינויי DPI מדי פעם גדלי תמונה שגויים או אייקונים ריקים, עד שמתרחש Repaint או RecreateWnd.
4) Terminalserver und Remote-Desktop als Realitätstest
כשמיישום מפעילים דרך RDP, שינויי DPI ו‑session‑reconnects אינם נדירים. שם בדיוק אסטרטגיית ImageList שאינה עמידה מתבררת כבעייתית: המשתמש יראה אחרי ה‑Reconnect אייקונים מטושטשים או סרגלי כלים בסקייל שגוי, אף על פי שבמכשיר המקומי הכל נראה תקין.
Sauberer Ansatz: Mehrere Auflösungen bereitstellen statt brutales Upscaling
ההחלטה החשובה היא רעיונית: האם ברצונך להגדיל אייקונים בזמן ריצה מתוך גרפיקת בסיס יחידה (למשל 16×16 → 32×32), או להספק מספר רזולוציות נאטיביות ולבחור את המתאימה בהתאם ל‑DPI?
בפרקטיקה ברוב המקרים עדיף לספק מספר רזולוציות. ניתן לבצע upscaling כאשר המקור וקטורי (SVG, Icon‑Font) או כאשר נדרשים רק גורמי הגדלה מתונים. ברגע שאתה מגדיל באופן ניכר מתמונה רסטר קטנה אתה מאבד את איכות הקצוות, וזה בולט מיידית על מסכים מודרניים.
בעולם VCL לשם גישה זו יש כיום שני רכיבים רלוונטיים:
- TImageCollection: מיכל לתמונות במגוון גדלים/ווריאציות.
- TVirtualImageList: יוצר מכך בזמן ריצה ImageList בגודל הנדרש ומגיב לשינויים ב‑DPI.
זה לא פותר את כל הבעיות, אבל מזיז את נקודת ההכרעה למקום הנכון: אתה מגדיר מקורות תמונה באופן מסודר, והסקיילינג/בחירה נעשית בעקביות.
VCL High-DPI Icons zur Laufzeit skalieren: Wann es sinnvoll ist (und wann nicht)
יש סיבות לגיטימיות לשנות גודל אייקונים בזמן ריצה:
- אתה טוען אייקונים באופן דינמי (למשל מתיקיית פלאגין, חבילות מיתוג מותאמות לקוח, חבילות קונפיגורציה).
- אתה רוצה צינור אחיד למקורות שונים (ICO, PNG, SVG) ואינך מעוניין לקשור את כל הווריאציות בזמן הבנייה.
- אתה מייצר אייקונים באופן תוכנתי (סטטוס‑בדג׳ים, overlays, סמלים מורכבים).
לא משתלם לבצע שינוי-גודל בזמן ריצה אם למעשה יש לך רק אייקונים קלאסיים לסרגל כלים מתוך סט קבוע. אז הדרך בעלת המינימום תחזוקה היא: לספק מספר רזולוציות בצורה מסודרת ולתת ל-VCL לבחור.
אם מבצעים שינוי-גודל בזמן ריצה — אז לפי כללים ברורים:
- אף פעם לא לסקל תמונת Bitmap שכבר סולקה; תמיד להתחיל ממקור מאסטר (בעדיפות וקטורית או ברזולוציה גבוהה).
- Cache לכל גודל יעד ו-DPI, אחרת תבצע סקלינג מחדש בכל Paint — זה צורך CPU ועלול לגרום לגמגום.
- לשמור על Alpha: למזער המרות, להשתמש ב-32-bit RGBA, לא לרסטר את הרקע.
ארכיטקטורה פרגמטית: Icon-Pipeline כרכיב עצמאי
ביישומים גדולים יותר משתלם לא לפזר את הנושא בכל מקום, אלא לבנות צינור קטן. זה לא חייב להיות Framework — יותר נכון להגדיר תחום אחריות ברור:
- מקור האייקון: מאיפה מגיעים ה-Master-Assets (משאבים, קבצים, מסד נתונים, API)?
- Rasterizer/Scaler: איך מייצרים מה-Master את גודל היעד (אינטרפולציה, במידת הצורך רינדור SVG)?
- Cache: מפתח (Icon-ID, פיקסלים יעד, DPI, Theme) ומחזור חיים (להפוך ללא תקף בעת שינוי DPI, שינוי Theme, החלפת חבילה).
- Consumer-Adapter: איך מגיע התוצר למבני VCL (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?
יתרון: אפשר לשחזר ולדבג באגים של DPI ברצף במקום אחד, במקום לחפש ב-40 Forms איפה שוב מבצעים סקלינג.
לטפל בשינויי DPI כראוי: אירועים, Rebuild, Repaint
Unter Windows sind DPI-Wechsel ein eigener Lebenszyklus. In der VCL gibt es je nach Version und DPI-Awareness mehrere Ereignisse/Mechanismen, aber das Grundprinzip bleibt:
- Wenn sich die DPI des Fensters ändert, müssen bildbasierte Ressourcen, die pixelgenau sein sollen, neu bereitgestellt werden.
- Wenn du ImageLists dynamisch füllst, reicht ein bloßes Invalidate oft nicht — du brauchst ein Rebuild der Images in der neuen Zielgröße.
Ein praxistaugliches Muster ist: Auf DPI-Änderung (z. B. Form-Scale/Monitorwechsel) invalidierst du den Icon-Cache für diese DPI und baust die betroffenen ImageLists neu auf. Wichtig ist, dabei nicht in Paint-Events zu skalieren, sondern in einem kontrollierten Update-Block (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw aus, dann wieder an). So vermeidest du Flackern und halbfertige UI-Zustände.
Warum TImageList so oft unscharf wird: Interpolation, DPI-Rundung, Kanten
Wenn du einmal verstanden hast, dass nicht DPI das Problem ist, sondern Interpolation plus Rundung, lassen sich viele Effekte erklären:
אם יש לך איקונים רסטריים, ist es in vielen Teams ge4ngige Praxis, pro Zielgrf6dfe eigene PNGs zu liefern (16/20/24/32/40/48). Klingt nach viel, ist aber oft weniger Aufwand als jahrelang bbwarum sieht das auf diesem Monitor komisch ausab zu debuggen.
ניפוי שגיאות: להפוך מריחת פיקסלים לשחזורית במקום לתקן לפי Bauchgeffchl
High-DPI-Bugs wirken oft zufe4llig. Mit ein paar Checks werden sie deterministisch:
1) לרשום בזמן ריצה את DPI und ImageList-Grf6dfe
Logge bei Start und bei DPI-Wechsel: CurrentPPI des Forms, Screen.PixelsPerInch (Achtung: das kann System-DPI sein), sowie ImageList.Width/Height der betroffenen Listen. Wenn du nach einem Monitorwechsel noch 16d716 siehst, obwohl du 32 erwartest, ist die Ursache klar: Rebuild fehlt oder kommt zu spe4t.
2) להגדיל את האייקונים באופן נראה לעין
Ein schneller Test ist, die Toolbar-Icons tempore4r auf 48 px zu setzen. Schlechte Skalierung springt dann sofort ins Auge. Gute Pipelines bleiben auch bei 48 px scharf, weil sie aus einer passenden Quelle rastern.
3) לבדוק החלפת נושא ורקע
Halos am Rand sind oft Alpha-/Premultiply-Probleme. Teste auf hell/dunkel und auf Fle4chen mit Farbverlauf. Wenn die Kante je nach Hintergrund anders aussieht, stimmt die Transparenzbehandlung nicht.
4) Remote Desktop / Monitorwechsel כסקריפט בדיקה
Erstelle ein kurzes Testskript ffcr die QS: App starten auf Monitor A (100a0%), Fenster auf Monitor B (150a0%), zurfcck, dann RDP reconnect. Wenn das stabil ist, sind viele Kundenprobleme bereits erschlagen.
מיגרציה ביישומים קיימים: שלבweise statt Big Bang
ביישומי Delphi-VCL gewachsenen steckt die Icon-Logik oft an vielen Stellen: Menfcs, Toolbars, ActionLists, TreeViews, Statusanzeigen. Ein Big-Bang-Umbau bringt Risiko. Bewe4hrt hat sich ein schrittweises Vorgehen:
- מיפוי: Welche ImageLists gibt es? Welche Controls nutzen sie? Welche Grf6dfen werden erwartet?
- תעדוף: Zuerst die prominentesten Fle4chen (Haupttoolbar, Navigation, Kontextmenfcs).
- מקור אחיד: Icons zentralisieren (ImageCollection oder eigener Loader), statt pro Form Einzeldateien zu laden.
- בדיקות שינוי DPI: Ab dem ersten umgebauten Modul konsequent DPI-Wechsel-Tests fahren.
חשוב: אם אתה מפעיל במקביל Pipeline ישנה וחדשה, תעד כללים ברורים. אחרת תיווצר תערובת שבה חלק מהאייקונים חדים ואחרים נראים באופן ברור bbaltab.
ביצועים וזיכרון: קנה מידה בזמן ריצה ללא תופעות לוואי
סקיילינג צורכת משאבי CPU וזיכרון. בתוכנה עסקית הדבר בא לידי ביטוי לעיתים רחוקות במצבים רדומים, אבל כשמעבירים חלון למסך חדש או באתחול עם Forms רבים זה עלול לגרום לקריעות. שלוש קווי הנחיה מעשיים:
- הגבל את גודל ה-Cache: אל תשמור כל שלב ביניים לנצח. אם אתה צריך רק 100% ו-150% — cache רק את הגדלים הללו.
- Lazy Build: רסטר את האייקונים רק כשהמסך באמת זקוק להם. בממשקי משתמש גדולים זה חוסך זמן אתחול.
- Batch-Rebuild: בשינוי DPI אל תגרום לכל Control להתרענן בנפרד. Rebuild מרכזי מונע סקלינג מיותר ושרידות גבוהה יותר.
אם אתה עובד עם TVirtualImageList, חלק גדול מהקונספט כבר מוטמע שם — אבל עדיין עליך להיזהר שלא תוסיף סקלינג משלך מעליו.
Fallback-Strategien: מה עושים אם לא כל גדלי האייקונים זמינים?
במציאות אין תמיד את כל הנכסים בכל הגדלים. לכן צריך אסטרטגיית Fallback ברורה כדי למנוע תוצאות מקריות או לא עקביות:
- Prefer Downscale: עדיף להקטין מ-64px ל-32px מאשר להגדיל מ-16px ל-32px.
- הגדר רמות: קבע אילו גדלי יעד אתה באמת תומך (למשל 16/20/24/32/40/48) והתאם אליהם את ה-DPI בצורה מנוהלת.
- בדוק שקיפויות: בפלי‑בקים שים לב במיוחד לערוץ ה-alpha — שם נוטות להיווצר הילות.
טעות טיפוסית היא פשוט לקחת את הגודל הקרוב הבא לכל אייקון. זה גורם לשונות בחדות הנתפסת בהתאם לאייקון. עדיף תכנית מיפוי קשיחה ומתועדת.
מתי המאמץ באמת מצדיק את עצמו?
יש שלושה אינדיקטורים ברורים שמציינים שהקמת pipeline נקייה לניהול אייקוני High‑DPI כדאית:
- המשתמשים שלך עובדים עם מסכים מעורבים (לפטופ + חיצוני) או משתמשים רבות ב-RDP.
- היישום ארוך־טווח ומתוחזק על פני שנים, ותפיסת ה-UI מהווה חלק מקבלת המוצר.
- אתה עומד בפני צעדים של מודרניזציה (הגברת DPI‑awareness, החלפת Controls, עדכון פריסת ה-toolbar).
אם האפליקציה רצה רק על מערכת קיוסק קבועה עם רזולוציה זהה, אפשר לשמור על גישה מינימלית: לספק את גודל האייקון המתאים, להגדיר DPI‑awareness כהלכה — וזהו.
מסקנה: High‑DPI אינו פרט קוסמטי, אלא החלטת רינדור
אייקונים מטושטשים ב-VCL אינם בדרך כלל תקלה מבודדת, אלא סימן לשרשרת לא נקייה של מקורות, סקלינג וקאשינג. הגישה העמידה היא לספק אייקונים בכמה רזולוציות ולהנגישם בקונסיסטנטיות דרך Pipeline מרכזית (למשל ImageCollection/VirtualImageList או שכבת אייקונים משלך). סקלינג בזמן ריצה מתאים כאשר יש מקורות דינמיים או סמלים מורכבים — אך רק עם מקור מאסטרי, Cache מבוסס DPI וכללי Rebuild ברורים.
אם יש לך תסמינים קונקרטיים (שינוי DPI שמשחית אייקונים, הילות בקצוות, גדלים שגויים אחרי RDP), כדאי לבודד את הנושא באופן מכוון ולטפל בו כחלק ארכיטקטוני קטן במקום לערום Workarounds לכל Form. אם אתה צריך תמיכה בדיבוג או במודרניזציה בשלבים, תמצא כאן את נקודת ההתחלה המתאימה: יצירת קשר עם Net-Base Software GmbH.
לנושא זה חשובים גם Timagelist High Dpi ו-Delphi Vcl Dpi-Awareness. המאמר מציב את ההיבטים הללו בהקשר מובן ומדגים מהן הנקודות המעשיות שחשובות בשגרה.
השלב הבא
כאשר נושא זה הופך לפרויקט אמיתי, יש לבחון כבר בשלבים המוקדמים את הארכיטקטורה, המערכות הקיימות והתפעול יחד.
אנו תומכים לא רק בשאלות נקודתיות, אלא גם כשמקטעי קוד מקור, נושאי Legacy או רעיונות פורטל אמורים להפוך לפרויקט ארגוני מהימן ועמיד.
- המצב הקיים, תמונת היעד והסיכונים הטכניים מוערכים יחד.
- REST, גישה לנתונים, פורטלים ופריסה לא יידחו כהשלכות מאוחרות.
- מבעוד מועד אתם רואים איזה נתיב בר-קיימא מבחינה כלכלית ותפעולית.