ציור רסטר עם Imager#
Imager הוא סוס העבודה של הרסטר: צרו תמונה, ציירו עליה, סַננו אותה, שמרו אותה. הוא מתוחזק באופן פעיל, הוא מקשר עותקים משלו של ספריות הפורמט כך ש־PNG, JPEG, TIFF, GIF ו־WEBP כולם עובדים מהקופסה, והפרימיטיבים לציור שלו הם אלה מפרק האלגוריתמים הרצים במהירות C. אם אתם מתחילים פרויקט רסטר ב־Perl היום ואין לכם סיבה להעדיף משהו אחר, התחילו כאן.
כל מתודת ציור מחזירה שקר בכישלון ומגדירה את errstr, ולכן הניב הוא לבדוק ולדווח:
use Imager;
my $img = Imager->new(xsize => 400, ysize => 300, channels => 4)
or die Imager->errstr;
channels => 4 מבקש RGBA (אדום, ירוק, כחול, אלפא); השתמשו ב־3 עבור RGB אטום. ערוץ האלפא הוא מה שמאפשר להרכבה מפרק היסודות לעבוד כשמציירים תמונה אחת על אחרת.
צבעים#
בכל מקום שבו מתודה מקבלת color =>, אתם יכולים להעביר שם, מחרוזת hex, אובייקט Imager::Color, או הפניית מערך פשוטה:
my $red = Imager::Color->new('red');
my $red2 = Imager::Color->new('#FF0000');
my $red3 = Imager::Color->new(255, 0, 0);
my $trans = Imager::Color->new(255, 0, 0, 128); # 50% alpha
$img->box(color => [0, 128, 255], filled => 1); # array-ref, no object
צורת הפניית־המערך היא הפחות טקסית עבור צבע חד־פעמי והיא מה שרוב הדוגמאות למטה משתמשות בו.
הפרימיטיבים#
שימו לב לשמות הפרמטרים בקפידה, מפני שהם אינם כולם באותה צורה. תיבות משתמשות בקואורדינטות פינה xmin/ymin/xmax/ymax; מעגלים וקשתות משתמשים במרכז x/y עם רדיוס r; קשתות מוסיפות מעלות התחלה וסיום d1/d2.
# fill the whole canvas white first
$img->box(color => 'white', filled => 1);
# a line; aa => 1 requests anti-aliasing
$img->line(x1 => 0, y1 => 0, x2 => 399, y2 => 299,
color => [255, 0, 0], aa => 1);
# an outlined box (no fill) and a filled one
$img->box(xmin => 20, ymin => 20, xmax => 120, ymax => 90,
color => 'black');
$img->box(xmin => 140, ymin => 20, xmax => 240, ymax => 90,
color => [200, 220, 255], filled => 1);
# a filled circle: center (200,150), radius 60
$img->circle(x => 200, y => 150, r => 60, color => [0, 128, 255]);
# an arc is a pie slice from d1 to d2 degrees
$img->arc(x => 200, y => 150, r => 80, d1 => 0, d2 => 120,
color => 'yellow');
# a polygon from a list of [x, y] points
$img->polygon(points => [[300, 40], [380, 40], [340, 110]],
color => [120, 200, 120]);
# a polyline is an open path; it takes no fill
$img->polyline(points => [[300, 130], [340, 200], [380, 130]],
color => 'black', aa => 1);
# flood fill from a seed point out to a border
$img->flood_fill(x => 5, y => 5, color => [245, 245, 245]);
שתי מלכודות שכדאי לומר פעם אחת. box ממולא רק כשמעבירים filled => 1 (או תבנית fill =>); בלעדיו מקבלים מתאר. polyline מציירת נתיב פתוח ואינה מקבלת fill, בניגוד ל־polygon הסוגר ויכול למלא. כאשר צורה יוצאת כמתאר שציפיתם שיהיה ממולא, או מילוי אינו עושה דבר בשקט, אחת משתי אלה היא הסיבה.
טעינה, שינוי־קנה־מידה ושמירה#
קריאה וכתיבה מסיקות את הפורמט מסיומת שם הקובץ, או שאתם יכולים לכפות אותו עם type =>:
my $photo = Imager->new;
$photo->read(file => 'input.jpg') or die $photo->errstr;
# scale by a factor, or to fit a box
my $thumb = $photo->scale(scalefactor => 0.25);
my $fit = $photo->scale(xpixels => 200, ypixels => 200, type => 'min');
$thumb->write(file => 'thumb.png') or die $thumb->errstr;
scale מחזיר תמונה חדשה; הוא אינו משנה את המקור. צורת type => 'min' משנה קנה־מידה כך שהתמונה נכנסת בתוך התיבה הנתונה תוך שמירה על יחס גובה־רוחב; 'max' ממלא את התיבה (חותך את הגלישה כשחותכים לאחר מכן); 'nonprop' מתעלם מיחס גובה־רוחב ומותח בדיוק לפיקסלים שנקבתם.
מסננים#
filter מיישם אפקט במקום, מכונה על ידי type =>. הקונבולוציה מפרק האלגוריתמים נמצאת כאן כטשטוש גאוסי מובנה ומיסוך unsharp, בתוספת conv כללי עבור גרעין משלכם:
# Gaussian blur; larger stddev is more blur
$img->filter(type => 'gaussian', stddev => 2.5);
# unsharp mask to sharpen; both params optional
$img->filter(type => 'unsharpmask', stddev => 2.0, scale => 1.0);
# your own 1-D convolution coefficients (a horizontal blur here)
$img->filter(type => 'conv', coef => [0.25, 0.5, 0.25]);
# contrast, mosaic (pixelate), and inversion are all filters too
$img->filter(type => 'contrast', intensity => 1.4);
$img->filter(type => 'mosaic', size => 8);
$img->filter(type => 'hardinvert');
הקבוצה המלאה כוללת את gaussian, gaussian2 (סטיות x ו־y נפרדות), conv, unsharpmask, contrast, mosaic, noise, hardinvert, autolevels, postlevels, ואת מחוללי הטקסטורה הפרוצדורליים fountain, gradgen, radnoise ו־turbnoise. פנו למסנן הנקוב בשם לפני שאתם כותבים לולאת פיקסלים משלכם; רדו לרשימת מקדמי conv או לגישה ידנית רק כשדבר מובנה אינו מתאים.
תוכנית שלמה#
מחברים הכול יחד: כרזה קטנה עם רקע, כמה צורות, מעבר טשטוש, ותמונה ממוזערת שמורה.
use strict;
use warnings;
use Imager;
my $img = Imager->new(xsize => 400, ysize => 300, channels => 4)
or die Imager->errstr;
$img->box(color => [30, 30, 60], filled => 1); # dark bg
$img->circle(x => 120, y => 150, r => 70, color => [255, 200, 60]);
$img->circle(x => 260, y => 150, r => 70, color => [60, 200, 255]);
$img->filter(type => 'gaussian', stddev => 1.2); # soften
$img->line(x1 => 0, y1 => 250, x2 => 399, y2 => 250,
color => 'white', aa => 1);
$img->write(file => 'poster.png') or die $img->errstr;
$img->scale(scalefactor => 0.5)->write(file => 'poster-thumb.png')
or die $img->errstr;
מתי Imager הוא הכלי הלא־נכון#
Imager הוא רסטר, ולכן יש לו את מגבלת הרסטר: יש לו רזולוציה קבועה, ושינוי־קנה־מידה מעברה מדרדר. עבור פלט בלתי־תלוי־רזולוציה (לוגו שחייב להדפיס בחדות בכל גודל, תרשים המיועד לרשת כתגיות) אתם רוצים כלי ווקטור, המכוסה בCairo ו־SVG. עבור בסיס קוד קיים הבנוי על GD או ImageMagick, או עבור החוזקות הספציפיות שלהם, ראו את הפרק הבא. וכאשר הציור צריך להופיע בחלון חי ולא בקובץ, זהו פרק ערכות הכלים ל־GUI. עבור כל דבר שהוא ”צור או שנה bitmap על הדיסק“, Imager הוא התשובה.