Forum > Graphics

Sprites processing with standard Lazarus tools

(1/3) > >>

_N_:
Hello all.
I am writing piece of software that works with sprites. It should be able to use external resources that are stored in png images (I cannot embed them). Also, I don't need all of them, but only some subset (subset of sprites in every png). So for now my plan is the following:
1. Ask use for the path to original sprites
2. Pick needed sprites, process them, and save near the application
3. Load processed sprites at runtime (no modifications at this step)
But I have problem with step 2 (I tried to do the processing from original resources at runtime, but it was very slow (I also do repainting), so decided to spend a few megabytes of RAM to speed up everything and do processing beforehand and only load result at runtime).
What's needed for a sprite file:
1. Get several first sprites from png image (I know sizes for each file).
2. Duplicate them with flips to get "full row".
3. Copy the row several times, but repainting some colors (I have color mapping what should be replaced).
4. Save all this into a png file that will be used further on.
I don't want to use OpenGL or similar and also would like not to depend on an external library (but still consider it as an option if using standard mechanism is problematic).
For now I have:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---  PNG := TPortableNetworkGraphic.Create();  Sprites := TImageList.Create(nil);  try    PNG.LoadFromFile(SourceFile);     Sprites.Width := PNG.Width div 5;    Sprites.Height := getHeightByName(SpriteName);    // Sprites.BkColor := clBlack;    // Sprites.DrawingStyle := dsTransparent;     for p := 1 to 10 do begin      Sprites.AddSliced(PNG, 5, 1); // Quick way to extract needed sprites from png       // HERE I NEED TO ADD FLIPPED/ROTATED EXISTING SPRITES       // Repaint sprites      for i := 0 to ROW_SPRITES_COUNT - 1 do begin        SpriteIndex := p * ROW_SPRITES_COUNT + i;         SpriteBitmap := TBitmap.Create();        try          Sprites.GetBitmap(SpriteIndex, SpriteBitmap);          RepaintSprite(SpriteBitmap, ORIGINAL_COLOR, DESIRED_COLOR);          Sprites.Replace(SpriteIndex, SpriteBitmap, nil);        finally          SpriteBitmap.Free();        end;      end;    end;     // Save image list to png file      Bitmap := TBitmap.Create();    Bitmap.Height := Sprites.Height;    Bitmap.Width := Sprites.Width * Sprites.Count;    for i:=0 to (Sprites.Count - 1) do      Sprites.Draw(Bitmap.Canvas, i * Sprites.Width, 0 , i);    try      // THIS LOSE TRANSPARENCY WHICH IS NEEDED      // I have to use png imgae instead bitmap here but I can get only bitmap from image list, what to do?      Bitmap.SaveToFile(DestFile);    finally      Bitmap.Free();    end;   finally    Sprites.Free();    PNG.Free();  end;       // -------------------------------------------- procedure RepaintSprite  if (ColorFrom = ColorTo) then Exit;  Bitmap.BeginUpdate();  for y:=0 to (Bitmap.Height - 1) do begin    for x:=0 to (Bitmap.Width - 1) do begin      // I know that it is possible to read whole row,  but I read somewhere      // that it may bring trouble on different platforms, is it true?      PixelColor := Bitmap.Canvas.Pixels[x, y];      for i:=0 to SHADES_NUMBER do begin        if (PixelColor = SPRITES_COLORS[ColorFrom][i]) then begin          Bitmap.Canvas.Pixels[x, y] := SPRITES_COLORS[ColorTo][i];          break;        end;      end;    end;  end;  Bitmap.EndUpdate();    
To sum up:
1. How to copy & flip sprites and and them into the same imagelist (or maybe I do not need image list at all here, but then I have to read sprites from original png myself, but I don't know how to copy an area and keep transparency...)?
2. Repaint. First, how to correctly replace (or maybe add after repaint) in imagelist. Second, but not critical, optimization. Pixel access is very slow, but can I safely use faster method (and keep cross platform support)? At least I can repaint first, so after flip I don't need to repaint again.
3. How to save back to png file. How to get sprite from imagelist keeping transparency and draw on resulting png in memory?

I do not need compatibility with Delphi, so using Lazarus specific classes are completely ok, but I need to support cross platform compilation.

I don't have much experience with images processing in Lazarus or Pascal, so sorry for several, probably silly questions (for people who know)... I googled, but there are different problems with different solutions... And people use different approaches: TBitmap, TImage, TPicture, TCanvas, Masks, etc (actually it should be sort of the same, but again if you know details about implementations). I got somewhat lost. Code above is what I was able to achieve for now...

Thank you in advance.

Mr.Madguy:
Unfortunately it sounds like "I need advanced 2D processing tools, but I don't want to use them". Some things, you ask for, like copying parts of bitmaps and even flipping them (not 100% sure about TCanvas, but raw GDI allows it), are possible. But other things, like color mapping, aren't possible. At least if you don't use 8bit palette, but 16/32bit target without palette. FPC doesn't seem to support 8bit bitmaps though. You can always try memory bitmaps, like DIBs from GDI. But if you want cross-platform solution, then may be you should try BGRABitmap.

Overall advanced 2D processing requires something like DirectDraw. But once DirectDraw is obsoleted and 2D is now considered to be subset of 3D - 3D libraries should be used instead.

marcov:
Any  Bitmap.Canvas.Pixels[x, y] is slow.

Try to find or create a dedicated copy procedure that optimizes this.

Mr.Madguy:
Pure Canvas can actually be used.

Sprite flipping can be achieved via code like this:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---  Canvas.CopyRect(ClientRect, Picture.Bitmap.Canvas, TRect.Create(0, Picture.Height, Picture.Width, 0)); 
Repainting some colors is harder task, but can actually be achieved via using masked bitmap. I won't provide full working example, but here is template:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---//This two are done every time, you need to change repaint colorBackdropBitmap.Canvas.Brush.Color := MyColor;BackdropBitmap.Canvas.FillRect(TRect.Create(0, 0, BackdropBitmap.Width, BackdropBitmap.Height));//Not sure, if 1x1 pixel bitmap can be used here for optimization purposes//Load predefined MaskBitmap hereBackdropBitmap.MaskHandle := MaskBitmap.ReleaseHandle;//You need to do it only once//You may destroy MaskBitmap after thatCanvas.Draw(0, 0, MySprite);Canvas.Draw(0, 0, BackdropBitmap);//Just another way to get mask bitmap, if one color should be changed to anotherMySprite.Mask(ColorToChange);//Ehhh. Mask is reversed here - you need to manually reverse it.BackdropBitmap.MaskHandle := MySprite.ReleaseMaskHandle; 

wp:
There is a demo in folder examples/sprites of the Lazarus installation.

Navigation

[0] Message Index

[#] Next page

Go to full version