Grafikus C# – Windows Forms tanfolyam › 9.2 lecke Windows Forms · 9.2 lecke · képfeldolgozás A 9.1 leckében a Képek igazítása, méretezése és mentése
PictureBox csak megjelenítésben nagyította a képet. Ebben a leckében a kép tényleges méretét változtatod meg: kisebbre (bélyegkép, feltöltésre szánt kép), arányosan, jó minőségben. Megtanulod a forgatást és tükrözést (a telefonnal készített fényképek EXIF-tájolásával együtt), a vágást, és a mentést PNG, JPEG és BMP formátumban, JPEG minőség-beállítással. Ezekkel a lépésekkel már egy kis képszerkesztőt építesz.
A lecke végére
- arányos új méretet számolsz (illesztés egy dobozba), és jó minőségben átméretezed a képet;
- beállítod a rajzolás minőségét (
InterpolationMode,PixelOffsetMode,ImageAttributes); - forgatsz és tükrözöl (
RotateFlip), és kezeled a fényképek EXIF-tájolását; - képrészletet vágsz ki (
Cloneés határellenőrzés); - PNG-be, BMP-be és beállítható minőségű JPEG-be mentesz;
- kerülöd a tipikus hibákat (azonos fájlba mentés, fekete JPEG-háttér, torzult arány).
Mielőtt elkezded
Ehhez a leckéhez a 9.1 lecke (Bitmap, biztonságos betöltés) és a 7.8 lecke (SaveFileDialog) szükséges. A Graphics objektumot a 9.3 lecke tárgyalja részletesen; itt csak a méretezéshez szükséges részét használjuk.
Arányos és jó minőségű átméretezés
Két lépés: (1) kiszámolod az új méretet az arány megtartásával, (2) új, kisebb Bitmapra lerajzolod az eredeti képet jó minőségű beállításokkal. Az new Bitmap(kep, new Size(w, h)) rövid, de gyengébb minőséget ad, a GetThumbnailImage pedig kifejezetten alacsony minőségű: ezeket kerüld:
// 1. az új méret: a kép belefér a megadott dobozba, az arány megmarad
private Size Illeszt(Size forras, Size doboz)
{
double arany = Math.Min((double)doboz.Width / forras.Width, (double)doboz.Height / forras.Height);
return new Size(Math.Max(1, (int)Math.Round(forras.Width * arany)), Math.Max(1, (int)Math.Round(forras.Height * arany)));
}
// 2. az átméretezés jó minőségben
private Bitmap Atmeretez(Image forras, int szelesseg, int magassag)
{
Bitmap uj = new Bitmap(szelesseg, magassag, PixelFormat.Format32bppArgb);
uj.SetResolution(forras.HorizontalResolution, forras.VerticalResolution);
using (Graphics g = Graphics.FromImage(uj))
using (ImageAttributes beallitas = new ImageAttributes())
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic; // a legjobb minőségű mintavétel
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
beallitas.SetWrapMode(WrapMode.TileFlipXY); // megelőzi a kép szélén a halvány keretet
g.DrawImage(forras, new Rectangle(0, 0, szelesseg, magassag), 0, 0, forras.Width, forras.Height, GraphicsUnit.Pixel, beallitas);
}
return uj;
}
// használat: legfeljebb 800 × 600 (nagyítani nem érdemes)
Size ujMeret = Illeszt(kep.Size, new Size(800, 600));
if (ujMeret.Width < kep.Width)
{
Bitmap kicsi = Atmeretez(kep, ujMeret.Width, ujMeret.Height);
}Illeszt az oldalarányok közül a kisebbet választja: így a kép biztosan belefér, és nem torzul. A Math.Max(1, ...) megakadályozza a 0 pixeles méretet (kivétel lenne).HighQualityBicubic kicsinyítésnél a legszebb eredményt adja (lassabb, de a méretezés ritka művelet). A NearestNeighbor pixelgrafikánál kell (kemény élek).PixelOffsetMode.HighQuality és a TileFlipXY a kép szélén keletkező elmosódott vagy halvány vonal ellen véd: apró, de látható különbség.Jósolj!
A kép 4000 × 3000, a doboz 800 × 800. Mekkora az Illeszt eredménye?
Jóslat ellenőrzése
Az arányok: 800 / 4000 = 0,2 és 800 / 3000 ≈ 0,267. A kisebb (0,2) a mérvadó: 800 × 600. A szélesség éri el a határt, a magasság kisebb marad (a kép 4:3 arányú).
Forgatás, tükrözés és az EXIF-tájolás
// 90 fokos forgatások és tükrözések: a képet helyben módosítja
kep.RotateFlip(RotateFlipType.Rotate90FlipNone); // 90° jobbra
kep.RotateFlip(RotateFlipType.Rotate270FlipNone); // 90° balra
kep.RotateFlip(RotateFlipType.Rotate180FlipNone);
kep.RotateFlip(RotateFlipType.RotateNoneFlipX); // vízszintes tükrözés
kep.RotateFlip(RotateFlipType.RotateNoneFlipY); // függőleges tükrözés
pbKep.Refresh(); // a PictureBox újrarajzolja a módosított képet
// a telefonnal készített fényképek „fekvőnek” látszanak: a tájolás az EXIF adatban van
private void ExifTajolas(Bitmap kep)
{
const int TajolasAzonosito = 0x0112;
if (Array.IndexOf(kep.PropertyIdList, TajolasAzonosito) < 0)
{
return; // nincs tájolás adat
}
int ertek = kep.GetPropertyItem(TajolasAzonosito).Value[0];
switch (ertek)
{
case 3: kep.RotateFlip(RotateFlipType.Rotate180FlipNone); break;
case 6: kep.RotateFlip(RotateFlipType.Rotate90FlipNone); break;
case 8: kep.RotateFlip(RotateFlipType.Rotate270FlipNone); break;
}
kep.RemovePropertyItem(TajolasAzonosito); // különben a mentett kép újra elfordulna
}RotateFlip a képet módosítja (nem újat ad), ezért a hívás után Refresh() kell a megjelenítéshez. Az eredeti megőrzéséhez dolgozz másolaton (9.1 lecke).0x0112 az EXIF tájolás kódja (3: 180°, 6: 90° jobbra, 8: 270°).new Bitmap(kep)) megtartja-e a tulajdonságokat, a használt verziótól függhet: próbáld ki a saját fényképeiddel.Graphics.RotateTransform segítségével történik (9.5 lecke), az eredmény nagyobb vászonra kerül.Jósolj!
Függőlegesen tartott telefon fényképét nyitod meg (EXIF: 6). A program nem kezeli az EXIF adatot. Mit mutat a PictureBox?
Jóslat ellenőrzése
A képet oldalára fordítva (a tárolt tájolásban): a GDI+ az EXIF tájolást nem érvényesíti. Más képnézegetők (böngésző, fotónézegető) elforgatják, ezért a hiba a saját programodban tűnik fel.
Képrészlet kivágása
A Bitmap.Clone(Rectangle, PixelFormat) a kijelölt téglalapot új képként adja. A téglalap nem lóghat ki a képből: ellenkező esetben OutOfMemoryException kivételt dob (ugyanaz a félrevezető kivétel, mint érvénytelen képfájlnál). Ezért metszed a téglalapot a kép határával:
private Bitmap Kivag(Bitmap kep, Rectangle kert)
{
// a kép határain belülre vágjuk a kért téglalapot
Rectangle hatar = Rectangle.Intersect(kert, new Rectangle(0, 0, kep.Width, kep.Height));
if (hatar.Width < 1 || hatar.Height < 1)
{
return null; // üres terület: nincs mit vágni
}
return kep.Clone(hatar, kep.PixelFormat); // új, önálló Bitmap
}
// a másik módszer: a forrás téglalapot a DrawImage-nek adjuk (méretezéssel együtt)
Bitmap reszlet = new Bitmap(200, 200);
using (Graphics g = Graphics.FromImage(reszlet))
{
g.DrawImage(kep, new Rectangle(0, 0, 200, 200), new Rectangle(50, 50, 100, 100), GraphicsUnit.Pixel); // a 100 × 100-as részlet 200 × 200-ra nagyítva
}Clone a kijelölt részletet új, önálló Bitmapként adja; az eredeti ettől nem változik. Mindkettőt külön kell felszabadítani (9.8 lecke).DrawImage a forrás egy részét a cél egy részére rajzolja: egyszerre vág és méretez.Jósolj!
A kép 300 × 200, a kért téglalap new Rectangle(250, 150, 100, 100) (kilóg a jobb alsó sarkon), és a kód Rectangle.Intersect nélkül hívja a Clone metódust. Mi történik?
Jóslat ellenőrzése
OutOfMemoryException (a kért terület nagyobb, mint a kép). A Rectangle.Intersect a téglalapot 50 × 50-esre vágja (250,150 → 300,200), ezzel a vágás sikerül.
Mentés: PNG, BMP és JPEG minőséggel
// PNG és BMP: egyszerű mentés (a PNG veszteségmentes, átlátszóságot is tárol)
kep.Save(utvonal, ImageFormat.Png);
kep.Save(utvonal, ImageFormat.Bmp);
// JPEG: a minőség beállítható (1–100), a JPEG veszteséges és nem tárol átlátszóságot
private ImageCodecInfo Kodek(ImageFormat formatum)
{
foreach (ImageCodecInfo k in ImageCodecInfo.GetImageEncoders())
{
if (k.FormatID == formatum.Guid) { return k; }
}
return null;
}
private void JpegMent(Image kep, string utvonal, long minoseg)
{
// az átlátszó részek feketék lennének: előbb fehér háttérre rajzoljuk a képet
using (Bitmap hattérrel = new Bitmap(kep.Width, kep.Height, PixelFormat.Format24bppRgb))
{
using (Graphics g = Graphics.FromImage(hattérrel))
{
g.Clear(Color.White);
g.DrawImage(kep, 0, 0, kep.Width, kep.Height);
}
using (EncoderParameters beallitasok = new EncoderParameters(1))
{
beallitasok.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, minoseg);
hattérrel.Save(utvonal, Kodek(ImageFormat.Jpeg), beallitasok);
}
}
}| Formátum | Tömörítés | Átlátszóság | Mikor? |
|---|---|---|---|
| PNG | veszteségmentes | igen | Ábrák, képernyőképek, rajzok, átlátszó háttér. |
| JPEG | veszteséges (minőség állítható) | nem | Fényképek (kis fájl). A minőség 75–90 a gyakori kompromisszum. |
| BMP | nincs (nagy fájl) | nem | Egyszerű, tömörítetlen; ritkán ajánlott. |
| GIF | veszteségmentes, 256 szín | egy átlátszó szín | Egyszerű animáció, kevés színű ábra. |
Image.FromFile töltötte be, és ugyanarra a fájlra mentenéd, a GDI+ általános hibát dob (ExternalException: a fájl zárolt). A másolatos betöltés (9.1 lecke) ezt megoldja; a mentéshez egyébként is érdemes új fájlnevet kérni.JpegMent ezért fehér háttérre rajzol.Save(utvonal, ImageFormat.Png) akkor is PNG-t ír, ha a név .jpg. A SaveFileDialog.FilterIndex alapján válaszd a formátumot, és a kiterjesztést illeszd hozzá (7.8 lecke).Jósolj!
Átlátszó hátterű PNG-t simán kep.Save("kimenet.jpg", ImageFormat.Jpeg) hívással mentesz. Milyen háttere lesz a JPEG-nek?
Jóslat ellenőrzése
Fekete: a JPEG nem tárol átlátszóságot, az átlátszó képpontok feketévé válnak (az alfa nélküli átalakításnál). Ezért kell előbb fehér (vagy más) háttérre rajzolni.
Mit tegyél ide: a mini képszerkesztő (átméretezés, forgatás, vágás, mentés), alatta a méret- és fájlméret-adatok.Javasolt fájlnév: winforms-9-2-szerkeszto.webp · Alt szöveg: Mini képszerkesztő
Hibakeresés: te vagy a hibadetektív
Mi a baj, és hogyan javítod?
Gondold végig az okot, és csak utána nyisd le a megoldást.
1. A kicsinyített kép torzult
Bitmap kicsi = Atmeretez(kep, 800, 800); // fix négyzetHiba és javítás
A méret nem az arányból számolódik. Használd az Illeszt metódust: Size m = Illeszt(kep.Size, new Size(800, 800));.
2. A kicsinyített kép szögletes, pixeles
A kicsinyítés után a vonalak recések. Az alapértelmezett (gyenge) minőségű mintavételt használod, vagy az Ok és javítás
InterpolationMode értéke NearestNeighbor. Állítsd HighQualityBicubic értékre.
3. Általános GDI+ hiba mentéskor
kep = Image.FromFile(utvonal);
kep.Save(utvonal, ImageFormat.Png); // ugyanaz a fájl, amelyet a kép zárolHiba és javítás
A fájlt a betöltött kép zárolja. Töltsd be másolattal (9.1 lecke), vagy mentsd másik fájlnévre.
4. OutOfMemoryException vágáskor
A A téglalap kilóg a képből (vagy a szélessége/magassága nulla vagy negatív). Metszd a kép határával (Clone kivételt dob.Ok és javítás
Rectangle.Intersect), és ellenőrizd a méretét.
5. A fénykép elfordítva jelenik meg
A telefonos fénykép oldalára dőlt. Az EXIF tájolást (Ok és javítás
0x0112) a GDI+ nem érvényesíti. Olvasd ki, forgasd el a képet, és töröld a tájolás adatot.
6. A JPEG háttere fekete
Az átlátszó PNG-ből mentett JPEG fekete hátterű. A JPEG nem tárol átlátszóságot. Rajzold fehér háttérre (Ok és javítás
g.Clear(Color.White)) mentés előtt.
Mini projekt: mini képszerkesztő
Készíts egyszerű képszerkesztőt:
- Felület:
pbKep(Zoom), bal oldalon panel: Megnyitás..., Eredeti visszaállítása; Méretezés:nudSzeles,nudMagas(px),chkArany(Arány megtartása), Átméretezés gomb; Forgatás: ↶ 90°, ↷ 90°, Vízszintes tükrözés, Függőleges tükrözés; Vágás:nudX,nudY,nudVSz,nudVM, Kivágás; Mentés: Mentés másként...,tbMinoseg(TrackBar, 10–100, alap 85) a JPEG-hez;lblInfo. - Két kép: az
eredeti(a betöltött másolat) és azaktualis(a szerkesztett). A szerkesztés azaktualismásolatán dolgozik; az Eredeti visszaállítása az eredetiből új másolatot készít. - Betöltés: a 9.1 lecke
KepBetoltmetódusa, azExifTajolasalkalmazásával. - Átméretezés: ha a
chkAranybe van pipálva, a szélesség módosítása a magasságot is számolja (és fordítva); a méret 1–10 000 pixel; azAtmeretezjó minőségű beállításokkal. - Forgatás és tükrözés:
RotateFlipazaktualisképen, frissítéssel; a vágó mezők határai a kép méretéhez igazodnak. - Vágás: a
Kivagmetódus (határellenőrzéssel); üres terület esetén érthető üzenet. - Mentés: a
SaveFileDialogszűrői: PNG, JPEG, BMP; aFilterIndexszerinti formátum és kiterjesztés; JPEG esetén fehér háttér és a csúszka minősége; a mentés után az állapotsor a fájlméretet is mutatja; új fájlnév (ne írja felül a megnyitottat). - Teszteld: nagy fénykép, telefonos álló fénykép (EXIF), átlátszó PNG mentése JPEG-be, arányos és nem arányos méretezés, vágás kilógó téglalappal, minőségcsúszka (fájlméret változása), az eredeti visszaállítása, a régi képek felszabadítása.
Megoldás megnyitása (próbáld ki előbb önállóan!)
Az Illeszt, Atmeretez, Kivag, ExifTajolas, JpegMent, Kodek és a 9.1 leckei KepBetolt metódusokat a lecke mutatja; itt a szerkesztő vezérlése áll.
private Bitmap eredeti = null;
private Bitmap aktualis = null;
private bool beallit = false; // a mezők kódból való módosításakor nem reagálunk
private void Frissit()
{
pbKep.Image = aktualis;
pbKep.Refresh();
beallit = true;
nudSzeles.Value = Math.Max(1, Math.Min(10000, aktualis.Width));
nudMagas.Value = Math.Max(1, Math.Min(10000, aktualis.Height));
nudX.Maximum = aktualis.Width - 1;
nudY.Maximum = aktualis.Height - 1;
nudVSz.Maximum = aktualis.Width;
nudVM.Maximum = aktualis.Height;
beallit = false;
lblInfo.Text = aktualis.Width + " × " + aktualis.Height + " képpont";
}
private void Csere(Bitmap uj)
{
Bitmap regi = aktualis;
aktualis = uj;
Frissit();
if (regi != null && regi != eredeti) { regi.Dispose(); } // az eredetit nem szabadítjuk
}
private void btnMegnyit_Click(object sender, EventArgs e)
{
if (ofd.ShowDialog(this) != DialogResult.OK) { return; }
string hiba;
Bitmap uj = KepBetolt(ofd.FileName, out hiba);
if (uj == null) { lblInfo.Text = hiba; return; }
ExifTajolas(uj);
Bitmap regiEredeti = eredeti;
Bitmap regiAktualis = aktualis;
eredeti = uj;
aktualis = new Bitmap(eredeti);
Frissit();
if (regiAktualis != null && regiAktualis != regiEredeti) { regiAktualis.Dispose(); }
if (regiEredeti != null) { regiEredeti.Dispose(); }
}
private void btnVissza_Click(object sender, EventArgs e)
{
if (eredeti != null) { Csere(new Bitmap(eredeti)); }
}
private void nudSzeles_ValueChanged(object sender, EventArgs e)
{
if (beallit || aktualis == null || !chkArany.Checked) { return; }
beallit = true;
nudMagas.Value = Math.Max(1, Math.Min(10000, (int)Math.Round(nudSzeles.Value * aktualis.Height / aktualis.Width)));
beallit = false;
}
private void nudMagas_ValueChanged(object sender, EventArgs e)
{
if (beallit || aktualis == null || !chkArany.Checked) { return; }
beallit = true;
nudSzeles.Value = Math.Max(1, Math.Min(10000, (int)Math.Round(nudMagas.Value * aktualis.Width / aktualis.Height)));
beallit = false;
}
private void btnAtmeretez_Click(object sender, EventArgs e)
{
if (aktualis != null) { Csere(Atmeretez(aktualis, (int)nudSzeles.Value, (int)nudMagas.Value)); }
}
private void Forgat(RotateFlipType mod)
{
if (aktualis == null) { return; }
if (aktualis == eredeti) { aktualis = new Bitmap(eredeti); } // az eredetit nem módosítjuk
aktualis.RotateFlip(mod);
Frissit();
}
private void btnBalra_Click(object sender, EventArgs e) { Forgat(RotateFlipType.Rotate270FlipNone); }
private void btnJobbra_Click(object sender, EventArgs e) { Forgat(RotateFlipType.Rotate90FlipNone); }
private void btnTukorX_Click(object sender, EventArgs e) { Forgat(RotateFlipType.RotateNoneFlipX); }
private void btnTukorY_Click(object sender, EventArgs e) { Forgat(RotateFlipType.RotateNoneFlipY); }
private void btnKivag_Click(object sender, EventArgs e)
{
if (aktualis == null) { return; }
Bitmap reszlet = Kivag(aktualis, new Rectangle((int)nudX.Value, (int)nudY.Value, (int)nudVSz.Value, (int)nudVM.Value));
if (reszlet == null) { lblInfo.Text = "A kijelölt terület üres vagy a képen kívül van."; return; }
Csere(reszlet);
}
private void btnMent_Click(object sender, EventArgs e)
{
if (aktualis == null) { return; }
sfd.Filter = "PNG (*.png)|*.png|JPEG (*.jpg)|*.jpg|BMP (*.bmp)|*.bmp";
sfd.FileName = "kep-szerkesztett";
if (sfd.ShowDialog(this) != DialogResult.OK) { return; }
try
{
switch (sfd.FilterIndex)
{
case 2: JpegMent(aktualis, sfd.FileName, tbMinoseg.Value); break;
case 3: aktualis.Save(sfd.FileName, ImageFormat.Bmp); break;
default: aktualis.Save(sfd.FileName, ImageFormat.Png); break;
}
lblInfo.Text = "Mentve: " + Path.GetFileName(sfd.FileName) + " (" + (new FileInfo(sfd.FileName).Length / 1024) + " KB)";
}
catch (Exception ex)
{
MessageBox.Show(this, "A mentés nem sikerült: " + ex.Message, "Mentés", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (aktualis != null && aktualis != eredeti) { aktualis.Dispose(); }
if (eredeti != null) { eredeti.Dispose(); }
}Mit tegyél ide: a kész mini képszerkesztő egy képpel, a bal oldali vezérlőkkel és az adatsorral.Javasolt fájlnév: winforms-9-2-kesz.webp · Alt szöveg: A kész mini képszerkesztő
Három önálló gyakorlófeladat
Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.
1. Négyzetes bélyegkép
Készíts 128 × 128-as négyzetes bélyegképet: a kép középső négyzetét vágd ki, és kicsinyítsd. Hogyan számolod a vágás téglalapját? A rövidebb oldal a négyzet oldala; a téglalap a hosszabb oldal közepén kezdődik.Egy lehetséges megoldás
int oldal = Math.Min(kep.Width, kep.Height);
Rectangle kozep = new Rectangle((kep.Width - oldal) / 2, (kep.Height - oldal) / 2, oldal, oldal);
using (Bitmap negyzet = Kivag(kep, kozep))
{
Bitmap bely = Atmeretez(negyzet, 128, 128);
}
2. Kötegelt kicsinyítés
Egy mappa összes képét kicsinyítsd legfeljebb 1024 × 1024-re egy kicsi almappába. Mire kell figyelned? Minden képet külön töltsz be (zárolás nélkül), átméretezel, elmentesz, és azonnal felszabadítasz (Egy lehetséges megoldás
using), különben sok kép együtt elfogyasztja a memóriát. A hibás képeket átugrod és megszámolod.int hibas = 0;foreach (string ut in Directory.GetFiles(mappa, "*.jpg"))
{
string hiba;
using (Bitmap kep = KepBetolt(ut, out hiba))
{
if (kep == null) { hibas++; continue; }
Size m = Illeszt(kep.Size, new Size(1024, 1024));
using (Bitmap kicsi = Atmeretez(kep, Math.Min(m.Width, kep.Width), Math.Min(m.Height, kep.Height)))
{
kicsi.Save(Path.Combine(celMappa, Path.GetFileName(ut)), ImageFormat.Jpeg);
}
}
}
3. Vízjel a kép sarkába
Írj © Név feliratot a kép jobb alsó sarkába (a A felirat méretét a Graphics.DrawString segítségével). Hol számolod a helyét?Egy lehetséges megoldás
g.MeasureString adja, a pozíció: kép mérete mínusz felirat mérete mínusz margó (9.3 és 9.5 lecke).using (Graphics g = Graphics.FromImage(kep))
using (Font betu = new Font("Segoe UI", 14))
using (Brush ecset = new SolidBrush(Color.FromArgb(160, Color.White)))
{
SizeF m = g.MeasureString("© Név", betu);
g.DrawString("© Név", betu, ecset, kep.Width - m.Width - 10, kep.Height - m.Height - 10);
}
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Arányos méret
A kép 1200 × 900. Mekkora lesz, ha a szélessége 400 px, az arány megmarad? Az arány 400 / 1200 = 1/3, így a magasság 900 / 3 = 300: a méret 400 × 300.Megoldás megnyitása
2. Formátumválasztás
Melyik formátumba mented: (a) fénykép e-mailben; (b) átlátszó háttérű logó; (c) képernyőkép szöveggel? (a) JPEG (80–90 minőség); (b) PNG; (c) PNG (veszteségmentes, a szöveg éles marad).Megoldás megnyitása
Közepes szint
3. Kitöltő méretezés
A kép töltse ki a 400 × 400-as dobozt (a kilógó részt levágva, cover). Mi a számolás lényege? A nagyobb arányt választod (a Megoldás megnyitása
Math.Max), így a kép mindkét irányban kitölti a dobozt; a közepéből vágsz. (Az Illeszt a kisebbet használja, az az ellenkezője.)
4. Forgatás tetszőleges szöggel
A kép elforgatása 30 fokkal. Mekkora vászon kell, hogy semmi ne vágódjon le? A vászon szélessége: Megoldás megnyitása
w·|cos α| + h·|sin α|, magassága: w·|sin α| + h·|cos α| (a 9.5 leckében részletezzük a koordináta-transzformációt).
Kihívás szint
5. Célméretű fájl
A JPEG legyen legfeljebb 200 KB: keresd meg a legnagyobb minőséget, amely belefér. Milyen algoritmust használsz? Bináris keresés a minőségen (1–100): mentés memóriába (Megoldás megnyitása
MemoryStream), méret mérése, szűkítés. Így nem kell lemezre írni a próbálkozásokat.private long JpegMeret(Bitmap kep, long minoseg)
{
using (MemoryStream ms = new MemoryStream())
using (EncoderParameters p = new EncoderParameters(1))
{
p.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, minoseg);
kep.Save(ms, Kodek(ImageFormat.Jpeg), p);
return ms.Length;
}
}
private int LegjobbMinoseg(Bitmap kep, long maxBajt)
{
int also = 10, felso = 100, jo = 10;
while (also <= felso)
{
int kozep = (also + felso) / 2;
if (JpegMeret(kep, kozep) <= maxBajt) { jo = kozep; also = kozep + 1; }
else { felso = kozep - 1; }
}
return jo;
}
6. Képméret a memóriában
Mennyi memóriát foglal egy 4000 × 3000-es 4000 × 3000 × 4 bájt = 48 000 000 bájt ≈ 46 MB, a fájlméretétől függetlenül (egy 2 MB-os JPEG is kitömörítve ennyi). Ezért kell a nagy képeket gyorsan kicsinyíteni és felszabadítani (9.8 lecke).Bitmap 32 bites képpontformátumban, és miért fontos ez a méretezésnél?Megoldás megnyitása
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Az csak megjelenítés: a kép tényleges méretét nem változtatja (a mentett fájl nagy marad). A dobozba illeszkedő, arányos új méretet (a két arány közül a kisebbet). 90 fokos forgatás és tükrözés helyben; után A telefonos fényképek tájolása az EXIF adatban van, amit a GDI+ nem érvényesít. A képből kilógó téglalap Fehér (vagy más) háttérre rajzolni, különben az átlátszó rész fekete lesz.1. Miért nem elég a PictureBox nagyítása?
2. Mit számol az Illeszt?
3. Melyik InterpolationMode a legjobb minőségű?
HighQualityBicubic.4. Mit tesz a RotateFlip?
Refresh() kell.5. Miért kell az EXIF-tájolás kezelése?
6. Miért kell a Clone előtt a Rectangle.Intersect?
OutOfMemoryException kivételt dob.7. Mit kell tenni JPEG mentéskor átlátszó kép esetén?
Mit tudsz már?
- Arányosan és jó minőségben átméretezel egy képet.
- Forgatsz, tükrözöl, és kezeled a fényképek EXIF-tájolását.
- Biztonságosan vágsz (határellenőrzéssel).
- PNG, BMP és minőség-beállított JPEG formátumba mentesz.
- Elkerülöd a tipikus hibákat (azonos fájl, fekete háttér, torzulás).
Következő lecke: Paint esemény és Graphics objektum
Saját rajzolás a vezérlők felületére: a Paint esemény, a Graphics objektum és az első alakzatok.



