Živě.cz o počítačích a internetu

Tipy a triky v Delphi, díl 35. - Převod souboru BMP do formátu JPEG a naopak; Inverze barev bitmapy; Změna rozlišení obrázku

Jan Šindelář - 27.3.2002

V našem seriálu jsme se doposud zabývali různými tipy ze všech možných oblastí, velká část z nich se týkala například práce se soubory či složkami, takže již máte jistý drobný základ pro vytvoření kupříkladu vlastního jednoduchého souborového manažeru. Po dnešním dílu si budete možná moci vytvořit primitivní prohlížeč obrázků, protože si ukážeme několik základních funkcí s nimi.

Převod souboru BMP do formátu JPEG a naopak

Tento převod je vlastně velmi snadný, ačkoliv se možná na první pohled může zdát opak. Nebudeme totiž všechno od základu programovat sami, což by bylo asi v tomto konkrétním případě nad rámec našeho článku, ale využijeme již funkcí a knihoven, které nám Delphi nabízejí. Hlavní práci v tomto případě tedy udělá knihovna Jpeg, takže ji jako obvykle přidejte do projektu. Pro samotnou konverzi obrázku BMP do formátu JPEG si vytvoříme jednoduchou funkci, jejímiž parametry jsou zdrojový soubor, dále výsledná požadovaná kompresní kvalita souboru JPEG po konverzi a název výstupního souboru. Funkce tedy bude vypadat takto:

procedure BmpToJpeg(Bmp: TBitmap; Q: TJpegQualityRange; var Jpg: TJpegImage);
begin
Jpg.CompressionQuality := Q;
Jpg.Assign(Bmp);
end;

Požadovaná kvalita Q (či chcete-li kompresní faktor), která – jak jistě víte – ovlivňuje výsledný vzhled a velikost souboru, může nabývat hodnot od 1 do 100, přičemž čím nižší je hodnota, tím menší je výsledný soubor (pochopitelně na úkor kvality obrazu). Tuto jednoduchou funkci poté využijeme například takto:

procedure TForm1.Button1Click(Sender: TObject);
var
Bmp : TBitmap;
JPG : TJpegImage;
begin
if Form1.OpenPictureDialog1.Execute then
begin
  Bmp := TBitmap.Create;
  JPG := TJpegImage.Create;
  Bmp.LoadFromFile(Form1.OpenPictureDialog1.FileName);
  BmpToJpeg(Bmp, 30, JPG);
  JPG.SaveToFile(`vystup.jpg`);
  JPG.Free;
  Bmp.Free;
end;
end;

Příklad používá dialog na otevření souboru s obrázkem – OpenPictureDialog, takže jej přidejte na formulář. Jak vidíte, pro jednoduchost je též nastaven napevno název výstupního souboru i jeho kvalita, takže si oba parametry nezapomeňte příslušným způsobem přizpůsobit.

A jak bude vypadat opačná konverze? Velmi podobně a pravděpodobně byste na ni přišli sami, ale pro úplnost zde máte příklad:

procedure JpegToBmp(Jpg: TJpegImage; PF: TPixelFormat; var Bmp: TBitmap);
begin
Bmp.Assign(Jpg);
Bmp.PixelFormat := PF;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
Bmp : TBitmap;
JPG : TJpegImage;
begin
if Form1.OpenPictureDialog1.Execute then
begin
  Bmp := TBitmap.Create;
  JPG := TJpegImage.Create;
  Jpg.LoadFromFile(Form1.OpenPictureDialog1.FileName);
  JpegToBmp(jpg, pf24bit, bmp);
  Bmp.SaveToFile(`vystup.bmp`);
  JPG.Free;
  Bmp.Free;
end;
end;

Ukázku jsme si drobně vylepšili o možnost nastavení požadované barevné hloubky výsledného BMP souboru (viz druhý parametr funkce). V nápovědě naleznete výčet ostatních hodnot, kterých může tato proměnná nabývat, a u samotného typu TBitmap či TJpegImage najdete řadu dalších vlastností a parametrů k experimentování.

Inverze barev bitmapy

K dalším běžným funkcím programů pracujících s obrázky patří funkce invertování obrázku neboli vytvoření negativu. Ukážeme si velmi jednoduchý postup:

procedure TForm1.Button1Click(Sender: TObject);
var
R: TRect;
Bmp: TBitmap;
begin
if Form1.OpenPictureDialog1.Execute then
begin
  Bmp := TBitmap.Create;
  Bmp.LoadFromFile(Form1.OpenPictureDialog1.FileName);
  with Bmp do
  begin
  R := Rect(0, 0, Width, Height);
  InvertRect(Canvas.Handle, R);
  end;
  Bmp.SaveToFile(`inverze.bmp`);
  Bmp.Free;
end;
end;

Opět vidíte, že název výstupního souboru je nastaven pevně, takže si budete muset kód mírně upravit. Když si trochu zaexperimentujete s parametry u funkce Rect (při přiřazování do proměnné R), můžete docílit toho, že invertován bude pouze výřez obrázku, který určíte danými souřadnicemi. Sice mě honem nenapadá, k čemu by se toho dalo v praxi využít, ale ta možnost zde každopádně je.

Změna rozlišení obrázku

I toto je běžná funkce grafických programů či prohlížečů. V našem příkladu si ukážeme konkrétně změnu rozlišení obrázku JPEG, ale postup je samozřejmě obdobný i pro jiné formáty (stejně jako všechny ostatní dnešní příklady). Při změně rozlišení má uživatel obvykle dvě možnosti, jak dosáhnout požadované velikosti. První spočívá v tom, že uživatel přímo zadá rozměry výsledného obrázku v pixelech, kdežto druhou možností je zadat faktor zmenšení/zvětšení například v procentech. Buď pro každý rozměr zvlášť, nebo za současného zachování poměru stran. Všechny příklady mají stejný základ, liší se pouze ve výpočtu výsledných souřadnic. Zde je tedy základní kód:

procedure TForm1.Button1Click(Sender: TObject);
var
Bmp : TBitmap;
Jpg : TJpegImage;
ScaleX, ScaleY : Double;
begin
if Form1.OpenPictureDialog1.Execute then
begin
  Jpg:=TJpegImage.Create;
  try
  Jpg.LoadFromFile(Form1.OpenPictureDialog1.FileName);
  ScaleX := 320 / Jpg.Width;
  ScaleY := 200 / Jpg.Height;
  Bmp := TBitmap.Create;
  try
    Bmp.Width := Round(Jpg.Width * ScaleX);
    Bmp.Height := Round(Jpg.Height * ScaleY);
    Bmp.Canvas.StretchDraw(Bmp.Canvas.Cliprect, Jpg);
    Jpg.Assign(Bmp);
    Jpg.SaveToFile(`resize.jpg`);
  finally
    Bmp.Free;
  end;
  finally
  Jpg.Free;
  end;
end;
end;

V tomto případě dojde ke změně rozlišení na 320x200 pixelů (soubor je opět uložen pod pevně zvoleným názvem).

Jak docílit změny pomocí procent (faktoru zvětšení/zmenšení)? Velice snadno: pouze změníme příslušné řádky, které upravují výsledné koeficienty u jednotlivých rozměrů. Úprava může vypadat například takto:

ScaleX := 0.25;
ScaleY := 0.25;

Zde se jedná o zmenšení obrázku na 25 % původní velikosti, a jelikož jsou pro oba rozměry koeficienty stejné, dojde též k zachování poměru stran. Pokud byste chtěli obě metody zkombinovat, tedy například uživatel určí jeden z rozměrů a vaše aplikace dopočítá rozměr druhý při zachování poměru stran, je výpočet velmi snadný a pravděpodobně jste jej již nyní odvodili sami. Pro úplnost si však uvedeme alespoň variantu, kdy uživatel zadá rozměr X a chce znát příslušné Y:

"Výsledný Rozměr Y" := ("Uživatelem požadovaný rozměr X" / "Původní rozměr X") * "Původní rozměr Y"

Trošku krkolomně zapsáno, to uznávám, ale snad je to dost ilustrativní.

A to je pro dnešek všechno. Příště budeme v tématu pokračovat a ukážeme si další příklady související s prací s grafickými soubory.