Staňte se programátorem: Napište si malý Photoshop

Bitmapový editor Paint.NET je napsaný v jazyku C#. Dnes vám ukážeme, jak snadno lze v tomto jazyku napsat základní fotografické efekty. Napište si s námi takový malý Photoshop.

Jedním z nejznámějších desktopových programů napsaných pro Microsoft .NET Framework je bitmapový editor Paint.NET. Ačkoliv se zatím svými možnostmi a schopnostmi nemůže rovnat s vyspělými grafickými editory, je jednou z vlajkových lodí této platformy, většina .NET vývojářů se totiž soustředí především na webové aplikace, ASP.NET aj.

Bitmapový editor s fotografickými efekty I.

Bitmapový editor bez množství nejrůznějších fotografických a kreativních filtrů by v těžké konkurenci nemohl obstát. Dnes vám tedy ukážeme, jak si v jazyku C# napsat filtr, který původní obrázek převede do odstínu šedi, přidá mu historický nádech pomocí sépiového efektu a převede jej do negativu. Kód si pak budete moci vyzkoušet v jednoduchém editoru, který obrázek s efektem bude umět i uložit.

Klepněte pro větší obrázek  Klepněte pro větší obrázek
Náš jednoduchý editor s třemi bitmapovými efekty

V dnešním projektu se tedy dozvíte, jakým způsobem lze manipulovat s jednotlivými pixely obrázku, jejichž změnou budete moci na bitmapě vytvářet grafické efekty. Nejprve vám vysvětlíme teorii a ukážeme několik jednoduchých grafických efektů. Příští týden pak přijde řada i na složitější algoritmy.

Úprava pixelů bitmapy standartním způsobem

K pixelům bitmapy, která je realizována třídou System.Drawing.Bitmap, můžete přistupovat pomocí metody GetPixel a SetPixel. Metoda GetPixel vrací instanci struktury System.Drawing.Color představující barvu pixelu ležící na určitých souřadnicích X,Y v bitmapě. K nastavení pixelu na určitou souřadnici lze pak použít metodu SetPixel.

Následující metoda nastaví pouze červený filtr bitmapy:

void NastavCervenou(Bitmap b)
{
  for (int x = 0; x< b.Width; x++)
  {
    for (int y = 0; y<b.Height; y++)
    {
      Color vybBarva = b.GetPixel(x,y);
      b.SetPixel(x,y, Color.FromArgb(vybBarva.R,0,0));
    }
  }
}

Kód tedy vlastně u každého pixelu obrázku v barevném modelu RGB ponechá pouze červený kanál a zelený a modrý nastaví na nulu. Jednoduše řečeno, výsledek bude vypadat jako zobrazení pouze červeného kanálu v kdejakém bitmapovém editoru.

Rychlejší úprava pixelů bitmapy pomocí unsafe kódu

Nevýhodou tohoto postupu je jeho pomalý výkon – metody SetPixel a GetPixel jsou časově poměrně náročné. Pro zrychlení manipulace s pixely budete muset pracovat přímo s pamětí pomocí unsafe (nezabezpečeného) kódu.

Například takto by vypadala předchozí metoda NastavCervenou za použití efektivnějšího unsafe kódu:

using System.Drawing.Imaging;
using System.Drawing;

. . .

unsafe Bitmap NastavCervenou(Bitmap b)
{
 
// Uzamkne v paměti data bitmapy tak, aby s nimi bylo možno pracovat.
 
BitmapData bd = b.LockBits(new Rectangle(0,0,b.Width,b.Height),
                             ImageLockMode.ReadWrite,
                             PixelFormat.Format32bppArgb);

  for (int y = 0; y< b.Height; y++)
  {
    // Vytvoří ukazatel na začátek y-té řádky bitmapy
    int* ukzt = (int*)((int)bd.Scan0 + y * bd.Stride);
    for (int x = 0; x <b.Width; x++)
    {
      // Vybere barvu pixelu
      Color c = Color.FromArgb(*ukzt);
      Color cervenyOdstin = Color.FromArgb(c.R,0,0);
      // Nastaví barvu pixelu
      *ukzt = cervenyOdstin.ToArgb();
      // Posune ukazatel na x-tý pixel v dané řádce
      ukzt++;
   
}
  }
 
b.UnlockBits(bd);
  return b;
}

Schválně si všimněte klíčového slova unsafe v deklaraci metody, které říká, že kód v této metodě budou používány ukazatele. Více o ukazatelích se dozvíte třeba na tomto webu.

K tomu, aby bylo možné ve Visual Studiu zkompilovat program obsahující unsafe kód, je nutné tuto vlastnost povolit v menu Project -> Project Options -> Debug.

Pro přístup k pixelum bitmapy je musíte uzamknout v paměti pomocí metody LockBits, která jako argument očekává instanci třídy Rectangle představující část bitmapy, jejíž data mají být uzamknuta, druhým argumentem je položka výčtového typu System.Drawing.Imaging.ImageLockMode, která nám definuje způsob, kterým můžete přistupovat k pixelům bitmapy.

Posledním argumentem je položka výčtového typu System.Drawing.Imaging.PixelFormat určující barevnou hloubku bitmapy. Následně cyklem projdete jednotlivé řádky bitmapy. Pozici prvního pixelu v y-tém řádku v bitmapy vypočítáte za použití vlastnosti Scan0 a Stride třídy BitmapData. Vlastnost Scan0 představuje počáteční pixel bitmapy a vlastnost Stride délku jedné řádky. Pro přesun na další pozici v řádce je nutné zvýšit ukazatel o hodnotu 1. Po dokončení úprav bitmapy, lze její data z paměti uvolnit pomocí metody UnLockBits.

A teď už slíbené fotografické filtry

Převod barev do odstínů šedi

Klepněte pro větší obrázek Klepněte pro větší obrázek
Převod do odstínů šedi a výsledný obrázek

Algoritmus s unsafe kódem může v C# vypadat třeba takto:

unsafe Bitmap SedaSkala(Bitmap b)
{
  BitmapData bd = b.LockBits(new Rectangle(0,0,b.Width,b.Height),
                            
ImageLockMode.ReadWrite,
                             PixelFormat.Format32bppArgb);

  for (int y = 0; y< b.Height; y++)
  {
    int* ukzt = (int*)((int)bd.Scan0 + y * bd.Stride);
    for (int x = 0; x <b.Width; x++)
    {
      Color c = Color.FromArgb(*ukzt);
      int seda = (int)(0.299 * c.R + 0.587 * c.G + 0.114 * c.B);
      Color sedaBarva = Color.FromArgb(seda,seda,seda);
      *ukzt = sedaBarva.ToArgb();
      ukzt++;
    }
  }
  b.UnlockBits(bd);
  return b;
}

Invertování barev

Klepněte pro větší obrázek  Klepněte pro větší obrázek  Klepněte pro větší obrázek  Klepněte pro větší obrázek
Negativ v několika podobách, efekty totiž můžete kombinovat, a výsledný obrázek

Algoritmus s unsafe kódem může v C# vypadat třeba takto:

unsafe Bitmap Invert(Bitmap b)
{
  BitmapData bd = b.LockBits(new Rectangle(0,0,b.Width,b.Height),
                                           ImageLockMode.ReadWrite,
                                           PixelFormat.Format32bppArgb);

  for (int y = 0; y< b.Height; y++)
  {
    int* ukzt = (int*)((int)bd.Scan0 + y * bd.Stride);
    for (int x = 0; x <b.Width; x++)
    {
      Color vybrBarva = Color.FromArgb(*ukzt);
      Color invBarva = Color.FromArgb(255-vybrBarva.R,
                                      255-vybrBarva.G,
                                      
255-vybrBarva.B);

      *ukzt = invBarva.ToArgb();
      ukzt++;
    }
  }
  b.UnlockBits(bd);
  return b;
}

Vytvoření sépiového nádechu

Klepněte pro větší obrázek  Klepněte pro větší obrázek
Nakonec nesmí chybět ještě sépiový efekt

Algoritmus s unsafe kódem může v C# vypadat třeba takto:

unsafe Bitmap Sepia(Bitmap b)
{
  BitmapData bd = b.LockBits(new Rectangle(0,0,b.Width,b.Height),
                                           ImageLockMode.ReadWrite,
                                           PixelFormat.Format32bppArgb);

  for (int y = 0; y< b.Height; y++)
  {
    int* ukzt = (int*)((int)bd.Scan0 + y * bd.Stride);
    for (int x = 0; x <b.Width; x++)
    {
      Color c = Color.FromArgb(*ukzt);
      int sepia = (int)(0.299 * c.R + 0.587 * c.G + 0.114 * c.B);
      int r = (int)((sepia > 206) ? 255 : sepia + 49);
      int g = (int)((sepia< 14) ? 0 : sepia - 14);
      int bl = (int)((sepia < 56) ? 0 : sepia - 56);
      Color sepiaColor = Color.FromArgb(r,g,bl);
      *ukzt = sepiaColor.ToArgb();
      ukzt++;
    }
  }
  b.UnlockBits(bd);
  return b;
}

Jak na složitější efekty? Přečtěte si příští článek

V tomto článku jsme vám ukázali, jakým způsobem lze manipulovat s pixely bitmapy za použití unsafe kódu. Příště vás seznámíme s pokročilejšími grafickými efekty.

Nakonec nesmí chybět ani slíbený spustitelný program a projekt.  K prvnímu budete potřebovat nainstalovaný novější Microsoft .NET Framework, pakliže si budete chtít projekt upravit a sestavit, budete potřebovat bezplatné vývojové prostředí Microsoft Visual C# 2008 Express Edition.

Máte zajímavý tip na drobnou aplikaci, kterou byste zde chtěli vidět? Neváhejte a napište nám váš nápad do diskuze pod článkem. 

Témata článku: Programování, Adobe Photoshop, Family, Kody

25 komentářů

Nejnovější komentáře

  • kvok 20. 8. 2008 14:50:06
    "Máte zajímavý tip na drobnou aplikaci, kterou byste zde chtěli vidět?...
  • jehoVista 19. 8. 2008 23:11:18
    imagemagick
  • jehoVista 19. 8. 2008 23:10:27
    Dva radkove komentare na 150 radek kodu? Kdyz ma slouzit vyuce?
Určitě si přečtěte

Špičkoví hackeři útočili na prohlížeče. Chrome odolal, ale Edge je tragédie

Špičkoví hackeři útočili na prohlížeče. Chrome odolal, ale Edge je tragédie

** Do Vancouveru se sjeli hackeři ** Soutěžili v útocích na prohlížeče ** Chrome odolal, ale Edge to projel na celé čáře

22.  3.  2017 | Jakub Čížek | 79

Pojďme programovat elektroniku: Meteostanice, která bude díky Sigfoxu posílat stav počasí třeba z vrcholu Sněžky

Pojďme programovat elektroniku: Meteostanice, která bude díky Sigfoxu posílat stav počasí třeba z vrcholu Sněžky

** Příští roky budou ve znamení internetu věcí ** Podívali jsme se podrobně na síť Sigfox ** Takhle s ní komunikují krabičky z celé Evropy

19.  3.  2017 | Jakub Čížek | 18

Kde nejlevněji uložit 1 TB dat: Srovnali jsme aktuální ceny cloudových úložišť

Kde nejlevněji uložit 1 TB dat: Srovnali jsme aktuální ceny cloudových úložišť

** Srovnali jsme známá cloudová úložiště podle toho, kolik měsíčně zaplatíte za 1TB ** Ceny se pohybují od dvou stovek až po tisíc korun ** Google umožní uložit až 30 TB dat

18.  3.  2017 | Stanislav Janů | 115

Obří Mechroboti jsou realitou, měří čtyři metry a mají hmotnost přes 1,5 tuny

Obří Mechroboti jsou realitou, měří čtyři metry a mají hmotnost přes 1,5 tuny

** Jihokorejská společnost Hankook Mirae Technology vyrábí obří Mechroboty ** Jsou určené pro ovládání člověkem uvnitř ** V prodeji se objeví koncem tohoto roku za 200 milionů korun

20.  3.  2017 | Karel Javůrek | 18

Google představil nový Android O. Na co se můžeme těšit?

Google představil nový Android O. Na co se můžeme těšit?

** Google vypustil vývojářskou verzi nového Androidu ** Přinese lepší notifikace nebo prodlouženou výdrž ** K uživatelům se dostane na podzim

22.  3.  2017 | Stanislav Janů | 60


Aktuální číslo časopisu Computer

Supertéma o počítačové bezpečnosti

AMD Ryzen přichází

Velké testy kinoprojektorů a levných sluchátek

Příslušenství do USB-C